Arrays 朱莉娅:我可以在for循环的迭代中更新和存储相同的数组吗?
我尝试使用for循环更新数组,并在循环的同一迭代中存储数组的“当前”版本,如下所示:Arrays 朱莉娅:我可以在for循环的迭代中更新和存储相同的数组吗?,arrays,for-loop,matrix,indexing,julia,Arrays,For Loop,Matrix,Indexing,Julia,我尝试使用for循环更新数组,并在循环的同一迭代中存储数组的“当前”版本,如下所示: struct store a::Float64 mat::AbstractArray end function foo(x::AbstractArray) m, n = size(x) col = Array{store}(undef, m, n) A = zeros(m, n) for i in eachindex(col) A[i] =
struct store
a::Float64
mat::AbstractArray
end
function foo(x::AbstractArray)
m, n = size(x)
col = Array{store}(undef, m, n)
A = zeros(m, n)
for i in eachindex(col)
A[i] = 1.0
print(A)
col[i] = store(x[i], A)
A[i] = 0
end
return col
end
我添加了一个print()来检查数组是否按我希望的方式更新(它是这样的)。我要存储的矩阵除了索引当前位置的“1”之外,其他都是零。我得到的结果是:
foo(rand(2,2))
2×2 Array{store,2}:
store(0.447322, [0.0 0.0; 0.0 0.0]) store(0.949405, [0.0 0.0; 0.0 0.0])
store(0.56251, [0.0 0.0; 0.0 0.0]) store(0.156834, [0.0 0.0; 0.0 0.0])
通过在循环中放置数组“A”可以实现我想要的,但这也是非常低效的
有更好的办法吗
谢谢 使用
SparseArrays
可以有效地表示您描述的数组,其中单个索引存储一个特殊值
因为它们遵循一致的模式,所以可以动态生成矩阵。您可以避免一次将它们全部存储在内存中
这是一个节省内存的解决方案:
julia> using SparseArrays
julia> struct Store{N}
A::Array{Float64,N}
end
julia> function Base.getindex(store::Store, I...)
B = spzeros(size(store.A)...)
B[I...] = 1.0
return store.A[I...], B
end
您可以这样使用:
julia> foo = rand(2,2)
2×2 Array{Float64,2}:
0.741406 0.0833667
0.688376 0.706395
julia> store = Store(foo)
Store{2}([0.7414058497508282 0.08336674477744199; 0.6883759175546191 0.706394665153228])
julia> store[1]
(0.7414058497508282,
[1, 1] = 1.0)
如果您以前没有使用过SparSearray,则SparSearray的打印可能看起来很奇怪,但您可以确认它们的行为符合预期:
julia> a, b = store[4]
(0.706394665153228,
[2, 2] = 1.0)
julia> b[1], b[2], b[3], b[4] # only the fourth index will have a nonzero value
(0.0, 0.0, 0.0, 1.0)
正如您可能已经猜到的,您的问题是由于您插入到
col
中的数组总是相同的:在一次迭代中修改数组会在任何地方修改它
执行所需操作的第一种方法是在将数组插入col
时复制该数组:
function foo1(x::AbstractArray)
m, n = size(x)
col = Array{store}(undef, m, n)
A = zeros(m, n)
for i in eachindex(col)
A[i] = 1.0
col[i] = store(x[i], copy(A))
A[i] = 0
end
return col
end
另一种方法是,正如您所建议的,在每次迭代时创建一个新的
a
数组:
function foo2(x::AbstractArray)
m, n = size(x)
col = Array{store}(undef, m, n)
for i in eachindex(col)
A = zeros(m, n)
A[i] = 1.0
col[i] = store(x[i], A)
end
return col
end
看起来第二种方法更有效:
julia> using BenchmarkTools
julia> x = rand(2,2)
2×2 Array{Float64,2}:
0.899445 0.459424
0.287892 0.669846
julia> @btime foo1($x)
241.078 ns (10 allocations: 800 bytes)
2×2 Array{store,2}:
store(0.899445, [1.0 0.0; 0.0 0.0]) store(0.459424, [0.0 1.0; 0.0 0.0])
store(0.287892, [0.0 0.0; 1.0 0.0]) store(0.669846, [0.0 0.0; 0.0 1.0])
julia> @btime foo2($x)
198.404 ns (9 allocations: 688 bytes)
2×2 Array{store,2}:
store(0.899445, [1.0 0.0; 0.0 0.0]) store(0.459424, [0.0 1.0; 0.0 0.0])
store(0.287892, [0.0 0.0; 1.0 0.0]) store(0.669846, [0.0 0.0; 0.0 1.0])
正如在另一个答案中所说,将
A
存储为SparseArray
会更有效,尤其是当它的大小较大时:
using SparseArrays
function foo3(x::AbstractArray)
m, n = size(x)
col = Array{store}(undef, m, n)
for i in eachindex(col)
A = spzeros(m, n)
A[i] = 1.0
col[i] = store(x[i], A)
end
return col
end
这种策略对如此小的尺寸没有好处,但如果您的实际问题更大,它应该是最有效的:
julia> @btime foo3($x)
829.851 ns (33 allocations: 1.92 KiB)
2×2 Array{store,2}:
store(0.899445, [1, 1] = 1.0) store(0.459424, [1, 2] = 1.0)
store(0.287892, [2, 1] = 1.0) store(0.669846, [2, 2] = 1.0)
如果您关心效率,那么应该避免在结构定义中使用抽象字段类型。如果要允许多种类型的数组,请使用参数化结构。(还有一条建议:对类型名称使用大写,而不是小写。)