Julia: プログラムを速くするための定石
https://blog.goo.ne.jp/r-de-r/e/2e21bccd94b2f24cdbc2b54030b13a1a
Julia: プログラムを速くするための定石--その2
https://blog.goo.ne.jp/r-de-r/e/4a815e35e7364368ed963e75a966b595
Julia: プログラムを速くするための定石--その3
https://blog.goo.ne.jp/r-de-r/e/bb84137b4936fb4ab5e76a3cbd7ce9e5
Julia: プログラムを速くするための定石--その4 の続きである
https://blog.goo.ne.jp/r-de-r/e/6b538afd6188153bf5e7db614b67f5e5
で,
ボレル予想
http://commutative.world.coocan.jp/blog5/2021/12/post-497.html
√2 の2進表示中の 0 と 1 の個数の勘定
元のプログラムの改善点は,関数にする,global を使わない,BigFloat の使い方,ビット 0/1 は両方数えなくてもよい,Int(floor(X)) より floor(Int, X) のほうが速い,何より,必要な桁数(precision)を正しく見積もる。
元のプログラムの実行結果は,
number of 0s: 500119, number of 1s: 499882
407.799036 seconds (9.55 M allocations: 4.550 TiB, 23.94% gc time)
書き換えたプログラムは以下の通り。
function f(n=1000000; precision=10000000)
setprecision(precision)
X = sqrt(big"2") - 1 # 直接求める
NO = 1 # considering "1...."
for i=1:n
X *= 2
floor(Int, X) == 1 && (NO += 1; X -= 1) # 整数部が 1, Int(floor(X)) より速い
end
NZ = n + 1 - NO # NZ は数えなくてよい
println("number of 0s: $NZ, number of 1s: $NO")
end
@time f(1000000, precision=1000000)
書き換えの経過
このプログラムは,関数化してもたいして速くならない
number of 0s: 500119, number of 1s: 499882
402.086354 seconds (8.55 M allocations: 4.550 TiB, 23.34% gc time)
1 だけを数えれば,計算時間はほぼ半分になると期待される(実際は半分より長い)
number of 0s: 9500119, number of 1s: 499882
271.447004 seconds (7.05 M allocations: 3.413 TiB, 25.05% gc time)
X -= floor(X) するのは無駄
floor(Int, X) は Int(floor(X)) より速い
number of 0s: 500119, number of 1s: 499882
184.315439 seconds (5.05 M allocations: 2.276 TiB, 29.69% gc time)
X は直接 sqrt(big"2") - 1 でよい
for i=1:1000000 で回しているのは,2進数での桁数。よって,precision でも 1000000 で十分(n より多めにとっておく)
number of 0s: 500119, number of 1s: 499882
18.305939 seconds (5.01 M allocations: 233.246 GiB, 24.00% gc time)
これにより,
402.086354 / 18.305939 = 21.96480355364453 倍速
円周率 π の2進表示中の 0 と 1 の個数の勘定
function g(n=1000000; precision=10000000)
setprecision(precision)
X = big(π) - 3 # 直接求める
NO = 2 # considering "11...."
for i=1:n
X *= 2
floor(Int, X) == 1 && (NO += 1; X -= 1) # 整数部が 1, Int(floor(X)) より速い
end
NZ = n + 2 - NO # NZ は数えなくてよい
println("number of 0s: $NZ, number of 1s: $NO")
end
@time g(1000000, precision=1100000)
元のプログラムは π をファイルで読んでいるので遅いだろう(ファイルを用意できないので再試できない)。
precision は √2 のときより少し多くしておく。
実行結果
number of 0s: 500279, number of 1s: 499723
21.048134 seconds (5.00 M allocations: 256.281 GiB, 23.65% gc time)