###素数計算をより高速処理をする為に
仕事時間の合間や休みの日に勉強がてら簡単なプログラムを作ったりしているのですが、
最近、素数の数を数えるプログラムをJavacriptで組みました。
様々な方法でアルゴリズムを組み、それぞれの計算速度を比較して楽しんでいますが、
より高速に処理をこなせるアルゴリズムを探しています。
素数判定の様々な計算方法は調べてみたのですが、
数学知識が乏しく理解できるものが少ないというのが現状です。
ここをこうすると速くなる、そもそもこういうアルゴリズムの方がいい!
など、皆さまの知識を吸収させていただきたく、質問させていただいきました。
###処理の内容
整数n以下の全ての素数の数を数える
###現在の実装
javascript
1function PrimeCalculater(op){ //この関数はクリックイベントで呼び出されます 2 let max_value = parseInt($('input.form_value').val()); //Webページ側から整数nとなる値を取得します 3 let primeArr = new Array(); //素数格納用配列 4 let cal_counter = 0; //計算回数カウンタ 5 primeArr.push(2,3,5,7); //Webページ側で最小値10と設定しているので、10以下の素数はあらかじめ用意します 6 7 const startTime = performance.now(); //測定用 8 9 for(i=11; i<max_value; i+=2){ //偶数は省く 10 let status = true; 11 for(j=1; (primeArr[j]<parseInt(Math.sqrt(i))+1) && j<primeArr.length && status; j++){ //primeArr[0] = 2 なので省く 12 cal_counter++; 13 if((i % primeArr[j]) == 0) status = false; //割り切れたら処理を中断し次の検査値へ 14 } 15 if(status) { 16 primeArr.push(i); 17 } 18 } 19 20 //Webページに結果を出力 21 $('td.cal').text(((performance.now() - startTime)/1000).toFixed(12) + ' 秒'); 22 $('td.counter').text(cal_counter.toLocaleString() + ' 回'); 23 $('td.answer').text((primeArr.length).toLocaleString() + ' 個'); 24}
いくつか作成したアルゴリズムの中で最速のものが上記のコードでした。
もちろん、演算回数をカウントする部分を無くしたらもう少し早くなると思いますが…。
Webサイト側では 10~999,999,999 の整数が入力できるフォームがあり、
そこに数値を入力し、実行ボタンを押すことで計算を開始し、結果をサイト上に表示する仕組みです。
(実際はその他の計算方法が選択できる構造になっています)
処理としては、Web側で指定された整数に対して、11~指定値までの奇数を順番に素数判定していきます。
判定方法ですが、判定する素数の平方根(小数点切り捨て)+1 の値までに存在する素数のみで
割り切れるかどうかで素数かどうかを判断し、途中で割り切れれば処理を中断し次の値のチェックに移行します。
素数だった場合は素数リストにその値を追加していきます。
これを指定数まで繰り返していきます。
最終的にその配列の要素数を出力するという仕組みです。
###計算速度
この実装で1億までの素数の数を数えさせた結果は画像の通りです。
これ以外だと…
1000万の場合は 約3.2秒
100万の場合は 約0.14秒
10万の場合は 約0.01秒
というような計算速度です。
(PC依存の速度なので環境によってだいぶ違うとは思います)
###やりたいこと・知りたいこと
この実装、またはこれ以外のアルゴリズムでより早い計算方法を知りたいです。
自分の解法ではこの速度が限界でした。ご鞭撻のほどお願いいたします。
###追記
具体的には1億以上の数値が入力された場合の演算を高速化したいと考えています。
###2020/08/25 追記(エラトステネスの篩実装)
ご助言頂きましたエラトステネスの篩による実装です。
javascript
1//max_valueにはフォームからの入力値が入ります 2//ここでmax_valueの値が1億などとされるとこの実装ではmemory outし、ページが停止します。 3let prime = new Array(max_value).fill(true); 4 5//primeCounterは本来必要ありませんが、最終的に配列の要素数 = 素数の数としてWebページに出力する都合上、 6//素数の数をカウントしてその大きさの配列を作る為に使用しています 7let primeCounter = 0; 8 9prime[0] = false; 10prime[1] = false; 11 12for(let i = 2; i <= maxSqrt; i++) { //maxSqrt には parseInt(Math.sqrt(max_value))+1 が代入されています 13 if(prime[i]) { 14 for(let j = i * 2; j <= max_value; j += i) { 15 cal_counter++; //この変数は別の場所で宣言しており、演算回数を図る為のものです 16 prime[j] = false; 17 } 18 } 19} 20for(let i = 0; i < prime.length; i++) { 21 cal_counter++; 22 if(prime[i]) primeCounter++; 23} 24 25primeArr = new Array(primeCounter);
エラトステネスの篩に関係のない部分は省略しています。
エラトステネスの篩というものの解釈がこれであっているのか自信はありませんが、
設定された整数と同一の数の配列を宣言し、その中身を一度全て TRUE としておきます。
それから 0 と 1 は素数ではないので手動で FALSE を代入します。
繰り返しを 2 からスタートし、ネストしたループ処理でその倍数となる要素を全て FALSE にしていきます。
ループ処理の際、その要素が既に篩に掛けられ素数ではない判定(FALSE)になっている場合は次へ移行します。
この処理を parseInt(Math.sqrt(max_value))+1 以下まで繰り返します。
最後に配列内で TRUE となっている要素の数を数えればそれが素数の数になっているはず、
という実装です。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/08/04 12:16
2020/08/04 12:32
2020/08/04 22:55
2020/08/05 01:14
2020/08/05 02:33