我需要为可变数量的数组有效地实现笛卡尔积。
我已经尝试了product
中的Iterators.jl
功能,但性能不足。
我是一名蟒蛇黑客,并使用了sklearn中的this function并且使用它获得了良好的性能结果。
我曾尝试编写此函数的Julia版本,但无法生成与python函数相同的结果。
我的代码是:
function my_repeat(a, n)
# mimics numpy.repeat
m = size(a, 1)
out = Array(eltype(a), n * m)
out[1:n] = a[1]
for i=2:m
out[(i-1)*n+1:i*n] = a[i]
end
return out
end
function cartesian(arrs; out=None)
dtype = eltype(arrs[1])
n = prod([size(i, 1) for i in arrs])
if is(out, None)
out = Array(dtype, n, length(arrs))
end
m = int(n / size(arrs[1], 1))
out[:, 1] = my_repeat(arrs[1], m)
if length(arrs[2:]) > 0
cartesian(arrs[2:], out=out[1:m, 2:])
for j = 1:size(arrs[1], 1)-1
out[(j*m + 1):(j+1)*m, 2:] = out[1:m, 2:]
end
end
return out
end
我用以下方法测试它:
aa = ([1, 2, 3], [4, 5], [6, 7])
cartesian(aa)
返回值为:
12x3 Array{Float64,2}:
1.0 9.88131e-324 2.13149e-314
1.0 2.76235e-318 2.13149e-314
1.0 9.88131e-324 2.13676e-314
1.0 9.88131e-324 2.13676e-314
2.0 9.88131e-324 2.13149e-314
2.0 2.76235e-318 2.13149e-314
2.0 9.88131e-324 2.13676e-314
2.0 9.88131e-324 2.13676e-314
3.0 9.88131e-324 2.13149e-314
3.0 2.76235e-318 2.13149e-314
3.0 9.88131e-324 2.13676e-314
3.0 9.88131e-324 2.13676e-314
我认为这里的问题是当我使用这一行:cartesian(arrs[2:], out=out[1:m, 2:])
时,关键字参数out
不会在递归调用中就地更新。
可以看出,我对这个函数的Python版本进行了非常天真的翻译(参见上面的链接)。很可能存在内部语言差异使得无法进行天真的翻译。我不认为这是真的,因为julia文档的functions部分引用了这句话:
Julia函数参数遵循有时称为“pass-by-sharing”的约定,这意味着值在传递给函数时不会被复制。函数参数本身充当新的变量绑定(可以引用值的新位置),但它们引用的值与传递的值相同。调用者可以看到对函数内可变值(如数组)的修改。这与Scheme,大多数Lisps,Python,Ruby和Perl以及其他动态语言中的行为相同。
如何在Julia中使这个(或同等的)功能起作用?
答案 0 :(得分:3)
Base中有一个repeat
函数。
更短更快的变体可能会使用笛卡尔包中的@forcartesian
宏:
using Cartesian
function cartprod(arrs, out=Array(eltype(arrs[1]), prod([length(a) for a in arrs]), length(arrs)))
sz = Int[length(a) for a in arrs]
narrs = length(arrs)
@forcartesian I sz begin
k = sub2ind(sz, I)
for i = 1:narrs
out[k,i] = arrs[i][I[i]]
end
end
out
end
行的顺序与您的解决方案不同,但也许这无关紧要?
答案 1 :(得分:1)
我明白了。
这不是Julia没有更新函数参数的问题,而是使用切片操作符a[ind]
的问题,它使得数据的副本,而不是通过引用索引我的数组。 multi dimensional array文档的这一部分得到了答案:
SubArray是AbstractArray的一个特化,它通过引用而不是通过复制来执行索引。使用子函数创建SubArray,其方式与getindex相同(带有数组和一系列索引参数)。 sub的结果看起来与getindex的结果相同,只是数据保留在原位。 sub将输入索引向量存储在SubArray对象中,该对象稍后可用于间接索引原始数组。
通过更改此行来解决此问题:
cartesian(arrs[2:], out=out[1:m, 2:])
以下内容:
out_end = size(out, 2)
cartesian(arrs[2:], out=sub(out, 1:m, 2:out_end))
答案 2 :(得分:0)
这是一个老问题,但随着 Julia 的进步,答案已经改变。
基本问题是像 a[1:3,:]
这样的切片进行复制。如果您在函数中更新该副本,它对 a
本身没有影响。
现代的答案是使用 @view a[1:3,:]
来获取对底层数组的一部分的引用。此视图的更新将反映在底层数组中。
您可以通过 @views
宏强制整个代码块使用视图语义。
有关更多讨论,请参阅 Inplace updating of function arguments。