我正在尝试以最快的方式构建和比较两个使用Julia的相同长度的01个随机向量,每个向量具有相同数量的零和一。
这全部是针对以下概率问题的蒙特卡洛模拟
我们有两个独立的,每个with带有n个白球和n个黑球。然后我们取一对球,每个each一个,每次清空empty。每对颜色相同的概率是多少?
我的工作如下:
using Random
# Auxiliar function that compare the parity, element by element, of two
# random vectors of length 2n
function comp(n::Int64)
sum((shuffle!(Vector(1:2*n)) .+ shuffle!(Vector(1:2*n))).%2)
end
上面的代码生成从1到2n的向量的两个随机排列,逐个元素相加,对每个元素应用模2,然后求和其余向量的所有值。然后,Im在每个数字的奇偶校验上方使用颜色进行建模:黑白奇数。
如果最终总和为零,则两个随机向量的颜色逐个元素相同。一个不同的结果表明,两个向量没有成对的颜色。
然后我设置以下函数,它只是所需概率的蒙特卡洛模拟:
# Here m is an optional argument that control the amount of random
# experiments in the simulation
function sim(n::Int64,m::Int64=24)
# A counter for the valid cases
x = 0
for i in 1:2^m
# A random pair of vectors is a valid case if they have the
# the same parity element by element so
if comp(n) == 0
x += 1
end
end
# The estimated value
x/2^m
end
现在我想知道是否有一种比较这种向量的更快方法。我尝试了以下随机向量的替代构造和比较
shuffle!( repeat([0,1],n)) == shuffle!( repeat([0,1],n))
然后我相应地将代码更改为
comp(n)
通过这些更改,代码运行的速度稍慢,这是我使用功能@time
测试的结果。我所做的其他更改是将for
语句更改为while
语句,但是计算时间保持不变。
因为我不是程序员(实际上是昨天我学习了Julia语言,并安装了Juno前端),所以可能是进行相同计算的更快方法。由于蒙特卡洛模拟的有效性取决于随机实验的数量,因此会有所启发,因此计算速度越快,我们可以测试的值越大。
答案 0 :(得分:3)
此问题的关键成本是shuffle!
,因此,为了最大程度地提高仿真速度,您可以使用(我将其添加为答案,因为评论太长了):
function test(n,m)
ref = [isodd(i) for i in 1:2n]
sum(all(view(shuffle!(ref), 1:n)) for i in 1:m) / m
end
与其他答案中提出的代码有什么区别?
shuffle!
两个向量; shuffle!
其中之一就足够了,因为比较的结果对于两个向量独立改组后的两个向量的任何相同置换都是不变的;因此,我们可以假设一个向量在随机置换后重新排序,以便在前trues
个条目中有n
,在最后falses
个条目中有n
shuffle!
(即ref
向量仅分配一次)all
函数;这样,当我先打false
时支票就会停止;如果我打了前true
个条目中的所有n
,我就不必检查最后n
个条目,因为我知道它们都是false
,所以我不必检查他们答案 1 :(得分:2)
要使内容更简洁,您可以直接生成0/1值的向量,然后让Julia检查向量是否相等,例如
function rndvec(n::Int64)
shuffle!(vcat(zeros(Bool,n),ones(Bool,n)))
end
function sim0(n::Int64, m::Int64=24)
sum(rndvec(n) == rndvec(n) for i in 1:2^m) / 2^m
end
如BogumiłKamiński所述,避免分配会加快代码的速度(让Julia进行比较的速度比他的代码还快)。
function sim1(n::Int64, m::Int64=24)
vref = vcat(zeros(Bool,n),ones(Bool,n))
vshuffled = vref[:]
sum(shuffle!(vshuffled) == vref for i in 1:2^m) / 2^m
end
要更快地走,请使用惰性评估并快速退出:如果第一个元素不同,则甚至不需要生成其余向量。 但这会使代码变得更加棘手。
我发现这有点不符合问题的实质,但是您也可以做更多的数学运算。
生成了binomial(2*n, n)
个可能的向量,因此您可以进行计算
function sim2(n::Int64, m::Int64=24)
nvec = binomial(2*n, n)
sum(rand(1:nvec) == 1 for i in 1:2^m) / 2^m
end
以下是我获得的一些时机:
@time show(("sim0", sim0(6, 21)))
@time show(("sim1", sim1(6, 21)))
@time show(("sim2", sim2(6, 21)))
@time test(("test", test(6, 2^21)))
("sim0", 0.0010724067687988281) 4.112159 seconds (12.68 M allocations: 1.131 GiB, 11.47% gc time)
("sim1", 0.0010781288146972656) 0.916075 seconds (19.87 k allocations: 1.092 MiB)
("sim2", 0.0010628700256347656) 0.249432 seconds (23.12 k allocations: 1.258 MiB)
("test", 0.0010166168212890625) 1.180781 seconds (2.14 M allocations: 98.634 MiB, 2.22% gc time)