与えられた指数から完全数を計算する小さなプログラムを最適化しようとしています。
プログラムは(ほぼ)完全に実行されますが、タスクマネージャーを開くと、単一のスレッドで実行されます。つまり、私は何か間違ったことをしているに違いありませんが、F#に関する私の知識はまだ「始まり」の段階にあります。
この質問はできるだけ明確にしようと思いますが、うまくいかない場合はお知らせください。
完全数とは、すべての除数の合計(数自体を除く)が数自体と等しい数です(たとえば、除数1、2、および3の合計は6であるため、6は完全です)。
私は素数を使用して計算を高速化します。つまり、すべての除数が格納されている(巨大な)リストには興味がありません。そのために、Euclidが正しいことが証明された式を使用します:(2 *(numの累乗-1))*(2 *(numの累乗-1))ここで、後者はメルセンヌ素数です。(@Julietによる)stackoverflowの非常に高速なアルゴリズムを使用して、指定された数値が素数であるかどうかを判断しました。
インターネットでいくつかの記事を読んでいると(私はまだ良い本を購入していないので、恥ずかしいです)、シーケンスはリストよりも優れていることがわかりました。そのため、私は最初に完全数のシーケンスを生成する関数を作成し始めました。
let perfectNumbersTwo (n : int) =
seq { for i in 1..n do
if (PowShift i) - 1I |> isPrime
then yield PowShift (i-1) * ((PowShift i)-1I)
}
ヘルパー関数PowShiftは次のように実装されます。
let inline PowShift (exp:int32) = 1I <<< exp ;;
すべての電力計算のベースは2からであるため、ビットシフト演算子を使用します。したがって、これは簡単な方法である可能性があります。もちろん、私がこれについて尋ねた質問への貢献にはまだ感謝しています:両方の引数をbigintsとして受け入れるF#電源の問題> F#両方の引数をbigintsとして受け入れる電源の問題
Julietが作成した(ここで借用した)関数は次のとおりです。
let isPrime ( n : bigint) =
let maxFactor = bigint(sqrt(float n))
let rec loop testPrime tog =
if testPrime > maxFactor then true
elif n % testPrime = 0I then false
else loop (testPrime + tog) (6I - tog)
if n = 2I || n = 3I || n = 5I then true
elif n <= 1I || n % 2I = 0I || n % 3I = 0I || n % 5I = 0I then false
else loop 7I 4I;;
このコードを並列なしで使用すると、ラップトップで9番目の完全数(37桁で構成され、指数の値31で見つけることができます)を見つけるのに約9分かかります。私のラップトップには2つのコアを備えたCPUがあり、1つだけが50%(1つのコアの全負荷)で実行されているので、結果を並列に計算することで計算を高速化できます。
そこで、完全数関数を次のように変更しました。
//Now the function again, but async for parallel computing
let perfectNumbersAsync ( n : int) =
async {
try
for x in 1.. n do
if PowShift x - 1I |> isPrime then
let result = PowShift (x-1) * ((PowShift x)-1I)
printfn "Found %A as a perfect number" result
with
| ex -> printfn "Error%s" (ex.Message);
}
この関数を呼び出すには、小さなヘルパー関数を使用して実行します。
let runPerfects n =
[n]
|> Seq.map perfectNumbersAsync
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
非同期計算の結果は、perfectNumbersAsync関数内に表示しているため、無視されます。
上記のコードはコンパイルされて実行されますが、それでも1つのコアしか使用しません(ただし、9番目の完全数を計算すると10秒速く実行されます)。ヘルパー関数のPowShiftとisPrimeで何かをしなければならないのではないかと心配していますが、確かではありません。これらのヘルパー関数のコードをperfectNumbersAsyncの非同期ブロック内に配置する必要がありますか?読みやすさは向上しません...
F#で遊ぶほど、この言語を理解するようになりますが、この場合と同様に、専門家が必要になることもあります:)。
これを読んでくれてありがとう、私は自分自身を少し明確にしたことを願っています...
ロバート。