弱いゴールドバッハ予想の計算
http://commutative.world.coocan.jp/blog5/cat122/
Julia プログラムを速くするために何をすべきかの例を示すための素材として取り上げる。
元のプログラムは,上のリンクをクリック。
冒頭 12 行は,素数リストをファイルから詠み込んでいる。
しかしこれは,Using Primes; list = primes(2, prime(1000)) とすれば簡単である(時間はかからない)。使えるものは何でも使おう。
さて,元のプログラムはトップレベルに書かれているが,Julia のプログラムを速くするための定石は,「どんなに短くて,一回しか使わないプログラムでも関数にする」ということである。
関数にするだけで,元のプログラムが 40 秒ほどかかっているのが 0.4 秒と,100 倍速くなる。なお,結果の出力はコメントアウトし,条件を満たす個数のみを勘定するようにしている。
次いで,結果のチェック時に a <= b && b <= c をしているが,素数リストの添字 a, b, c のループの開始値を変えれば,ループ回数も減らせるし,このチェック自体不要になる。
この最適化によって,実行時間は 0.075 秒ほどになる。
k の計算に while を使っているが,これと同じことを 1 行で行う。
この最適化は,計算時間にはあまり効果を持たない。
最後に,3 重の for ループで,無駄なループをしないようにする(それまでの和がすでに r を超えれば,それ以降のループは不要)。
この最適化により,実行時間は 0.025 秒ほどになる。
結果,元のプログラムより 1500 倍ほど速くなった。39.674786 / 0.025536 ≒ 1553
なお,入出力は通常の計算より 3 桁ほどの処理時間を要するので,結果を出力し,出力時間も含めた合計処理時間にはほとんど変化はないというような場合もある。
参考までに,以下に,書き換えたプログラムを示しておく。
function f(n=1000)
list = primes(2, prime(n));
# max = i-1
num = 0
for r = 7:2:n+1
k = sum(list .<= r) + 1
for a = 1:k
list[a] >= r && break
for b = a:k
list[a] + list[b] >= r && break
for c = b:k
t = list[a] + list[b] + list[c]
t > r && break
if t == r
num += 1
# print("$r = ")
# print(list[a])
# print("+")
# print(list[b])
# print("+")
# println(list[c])
end
end
end
end
end
println("num = $num")
end
@time f()
実行時間の変化
関数にする
num = 198418
0.407057 seconds (1.19 k allocations: 29.109 KiB)
39.674786 / 0.407057 # 97.4673964579899 倍速くなった
b, c の初期値を a, b にする
num = 198418
0.074374 seconds (1.19 k allocations: 29.109 KiB)
k の計算に while を使わない
num = 198418
0.065854 seconds (2.68 k allocations: 2.171 MiB)
a, b, c の for loop で無駄なループをしない(break する)
num = 198418
0.025536 seconds (2.68 k allocations: 2.172 MiB)
39.674786 / 0.025536 # 1553.6805294486214 倍速くなった