arrays - 如何针对大量迭代优化这种随机熵计算?
问题描述
我的代码试图做什么:
我有一个包含分子排列的初始数组,每个分子都在一个单元格中,并且仅限于在二维数组中移动(上下左右)(数组大小:200x200)。在每一步,我都会随机抽取一个分子,并将其移动到一个随机的相邻单元格中。
从某个数字开始,每隔几次迭代,我计算这个网格的熵。网格被切割成 25x25 的小方块。然后我使用香农熵来计算系统的熵。
客观的:
使用 i5-6500,不使用 GPU,在适当的时间内进行 1e8+ 次迭代。
我的代码:
function advance_multi_lattice(grid) #Find the next state of the system
rnd=rand(1:count(!iszero,grid)) #Random number to be used for a random molecule.
slots=find(!iszero,grid) #Cells containing molecules.
chosen_slot=find(!iszero,grid)[rnd] #Random cell. May contain multiple molecules.
dim=size(grid)[1] #Need this for rnd=3,4 later.
grid[chosen_slot]-=1 #Remove the molecules from the cell
rnd_arr=[1,2,3,4] #Array to random from.
while true
rnd=rand(rnd_arr) #Random number to see which side should the molecules go.
if rnd==1 #Right for example.
try #In case moving right is impossible, ie: moving right gets the molecule out. Remove 1 from rnd_arr and repeat.
grid[chosen_slot+1]+=1
break
catch
filter!(e->e!=1,rnd_arr)
end
elseif rnd==2
try #Same
grid[chosen_slot-1]+=1
break
catch
filter!(e->e!=2,rnd_arr)
end
#Repeat for the other numbers : 3 and 4...
return Grid
end
function S(P) #Entropy, if no molecules then return 0.
s=[]
for k in P
if k==0
push!(s,0)
else
push!(s,-k*log(k))
end
end
return s
end
function find_molecules(grid) #How many molecules in the array
s=0
for slot in grid
s+=slot
end
return s
end
function entropy_scale(grid,total_molecules) #Calculate the entropy of the grid.
P_array=Array{Float64}([])
for i=1:8
for j=1:8
push!(P_array,find_molecules(grid[(i-1)*25+1:i*25,(j-1)*25+1:j*25]))
end
end
P_array=P_array./total_molecules
return sum(S(P_array))
end
function entropy_evolution(grid,n) #The loop function. Changes the grid and returns the entropy as a function of steps.
t_arr=Array{Int64}([])
S_arr=Array{Float64}([])
p=Progress(Int(n)) #Progress bar, using ProgressMeter.
total_molecules=find_molecules(grid)
for k=1:1e3
grid=advance_multi_lattice(grid)
next!(p)
end
for k=1e3+1:n
grid=advance_multi_lattice(grid)
if k%500==0 #Only record entropy every 500 steps
push!(S_arr,entropy_scale(grid,totel_molecules))
end
next!(p)
end
return S_arr,grid
end
我的代码的结果:
对于 1e5 次迭代,我得到 43 秒。这意味着如果我想要一个有趣的结果( 1e9+ ),我需要很多时间,最多 1 小时以上。更改熵计算阈值几乎不会影响性能,除非它非常小。
解决方案
我假设您在 Julia 1.0 下工作(对于 Julia 0.6,需要进行一些小改动 - 我在代码中注明了这一点)。
为了提高性能,您应该保留分子向量 - 而不是网格(您不需要它,因为您允许分子占据相同的位置)。
我们将分子的位置编码为元组(x,y)
。现在你需要一个随机移动一个分子的函数。以下是您如何实现它(我硬编码了边界,但当然您可以将它们更改为参数):
function move_molecule((x,y)) # in Julia 0.6 it should be move_molecule(t)
# and here in Julia 0.6 you should add: x, y = t
if x == 1
if y == 1
((1,2), (2,1))[rand(1:2)]
elseif y == 200
((1,199), (2,200))[rand(1:2)]
else
((2,y), (1,y-1), (1, y+1))[rand(1:3)]
end
elseif x == 200
if y == 1
((200,2), (199,1))[rand(1:2)]
elseif y == 200
((200,199), (199,200))[rand(1:2)]
else
((200,y), (200,y-1), (200, y+1))[rand(1:3)]
end
else
if y == 1
((x,2), (x-1,1), (x+1, 1))[rand(1:3)]
elseif y == 200
((x,199), (x-1,200), (x+1, 200))[rand(1:3)]
else
((x+1,y), (x-1,y), (x, y+1), (x,y-1))[rand(1:4)]
end
end
end
现在,一个将在一个步骤中移动给定数量的随机分子的函数steps
是:
function go_sim!(molecules, steps)
for k in 1:steps
i = rand(axes(molecules, 1)) # in Julia 0.6 it should be: i = rand(1:length(molecules))
@inbounds molecules[i] = move_molecule(molecules[i])
if k % 500 == 0
# here do entropy calculation
end
end
end
您没有提供完全可重现的示例,所以我在此停止 - 但使用此数据结构重写用于熵计算的其余代码应该很容易(实际上它可能更简单)。这是一个基准(性能不依赖于网格的大小,也不依赖于分子的数量,这是使用网格的代码的一个重要优势):
julia> molecules = [(rand(1:200), rand(1:200)) for i in 1:1000];
julia> @time go_sim!(molecules, 1e9)
66.212943 seconds (22.64 k allocations: 1.191 MiB)
你会1e9
在大约一分钟内获得步骤(没有熵计算)。
良好性能所需的关键要素是什么:
- 不要使用 try-catch 块,因为它们非常慢;
- 尽量避免分配内存(即创建可变对象);我的代码基本上没有分配 - 特别是这就是我到处使用元组的原因(
move_molecule
为简单起见,您可以在函数中使用矩阵,但性能会差 2 倍左右)
希望这可以帮助。
推荐阅读
- java - iText PDF:文档没有页面
- powershell - 无法在 powershell 中将自定义对象导出到 CSV
- python - 无法撤消 tkinter 文本字段中的图像
- sql - SQL逐步对列求和
- python - 通过切片扩展 numpy 数组
- python - 在 MacOS 上找不到 CSS 文件
- visual-studio-code - 如何在代码块之间的 VS Code 中留下更多空行?
- go - go-grpc 导入“google/protobuf/struct.proto”未找到或有错误
- vim - 退出 vim 后终端字体损坏
- lucene.net - Umbraco 8 检查/Lucene 查询在代码中未返回任何结果,但从后台搜索按预期工作