素因数分解
素因数分解は、正の整数を素数の積で表すことです。
90 = 2 × 32 × 5
プログラムは簡単です。
1.偶数2で除算します。
2.割り切れたら、2の乗数の値を加算して、1.へ戻ります。
3.割り切れなかったら、奇数の素数の値の除算を3から始めます。
4.奇数3で除算します。
5,割り切れたら、3の乗数の値を加算して、4.へ戻ります。
6.割り切れなかったら、除数値を5=3+2の値にします。
7.奇数5で除算します。
8,割り切れたら、5の乗数の値を加算して、7.へ戻ります。
9.割り切れなかったら、除数値を7=5+2の値にします。
.
. 除数値2加算を繰り返します。
.
分解終了判別は、除算ごとにおこないます。
前の除算の商の値の平方根値より、除数値が大きくなったら分解終了です。
最後の商が1でない場合は、その値が、与えられた値の最後の素数値となります。
問題点
与えられた値が大きくなると、計算途中で、素数でない値の除算があります。
その為、計算に無駄な時間が発生します。
除数値が、2ずつしか増えないためです。
除数値がP= 2,P= 3,5,7,9,11,13,15,17,19,21,23 ・ ・ ・ P=P+2
と変化します 9,15,21,は素数ではありませんが、9=3X3,15=3X5,21=3X7 で既に素数で除算されていますので割り切れることはありません。
プログラムは、プラットフォームWin64
64ビットでコンパイルしないと、計算に時間が掛かります。
Win32モードだと、20倍以上の時間を要します。
プログラム
// プラットフォームが32の場合素数の値が32ビットを超える (p >= 2147483647) と演算が遅くなります。 // 64ビット推奨です。 // test1 9223372036854775806 = 2 × 3 × 715827883 × 2147483647 // test2 1537228672809129301 = 715827883 × 2147483647 // test3 6416582420042734637 = 6416582420042734637 // test4 9000000000000001121 = 9000000000000001121 // test5 15372286728550 = 2 × 5^2 × 31 × 337 × 29429093 unit Main; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, System.Diagnostics; type TForm1 = class(TForm) Memo1: TMemo; LabeledEdit1: TLabeledEdit; Button1: TButton; Panel1: TPanel; procedure Button1Click(Sender: TObject); procedure prime_factorization(num: int64); private { Private 宣言 } AStopWatch : TStopWatch; procedure DisplayTime(msTime: Int64); public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} // タイマー時間表示 procedure TForm1.DisplayTime(msTime: Int64); begin Panel1.Caption := Format('%5.3f ', [msTime / 1000.0]) + 'sec'; Panel1.Update; end; // 素因数分解 procedure TForm1.prime_factorization(num: int64); var cnt : int64; // 素数の乗数 i : int64; // 素数 snum : int64; // √num procedure Memoout; // 素数値表示 begin case cnt of 0: // cnt = 0 if num > 1 then Memo1.Text := Memo1.Text + inttostr(num); 1: // cnt = 1 i * 表示 if num > 1 then Memo1.Text := Memo1.Text + inttostr(i) + ' × ' else Memo1.Text := Memo1.Text + inttostr(i); else // cnt > 1 i^cnt * 表示 if num > 1 then Memo1.Text := Memo1.Text + inttostr(i) + '^' + inttostr(cnt) + ' × ' else Memo1.Text := Memo1.Text + inttostr(i) + '^' + inttostr(cnt); end; end; begin Memo1.Lines.Append('素因数分解'); Memo1.Text := Memo1.Text + ' ' + intTostr(num) + ' = '; // 偶数素数2の検索 偶数は2のみ i := 2; // 偶数素数2セット 除数 cnt := 0; // 乗数=0 while num mod i = 0 do begin // 余りが出るまで除数iで除算 inc(cnt); // 割り切れた数カウント num := num div i; // 除算 end; if cnt > 0 then begin // 乗数が0より大きかったら Memoout; // 検索素数値表示 cnt := 0; // 乗数=0 end; // 3以上の奇数の素数検索 最大値は√num以下 i := 3; // 奇数除数 初期値3セット snum := trunc(sqrt(num)) + 1; // √num while i <= snum do begin // iが√numより小さかったら実行 while num mod i = 0 do begin // 余りが出るまで除数iで除算 inc(cnt); // 割り切れた数カウント(乗数) num := num div i; // 除算 snum := trunc(sqrt(num)) + 1; // √num end; if cnt > 0 then begin // 乗数が0より大きかったら Memoout; // 検索素数値表示 cnt := 0; // 乗数=0 end; inc(i, 2); // 除数2加算 end; Memoout; // 最後の値表示 end; procedure TForm1.Button1Click(Sender: TObject); var num : int64; // 入力値 ch, cc : integer; s : string; begin val(labelededit1.Text, num, ch); // string->整数値 memo1.Clear; memo1.update; if ch <> 0 then begin // 変換エラーがあったら s := labelededit1.Text[ch]; // エラー文字取得 val(s, num, cc); // 文字ミスか値が大きすぎか確認 if cc <> 0 then // 文字のミスの場合 Memo1.Text := '入力値の' + intTostr(ch) + '文字目 ' + s + ' が間違っています。' else // 文字にミスなしの場合値大きすぎ Memo1.Text := '入力値が大きすぎます。'; exit; end; if num < 2 then begin Memo1.Text := '2以上の値にして下さい。'; exit; end; Button1.Enabled := False; AStopWatch := TStopwatch.StartNew; // タイマースタート DisplayTime(AStopWatch.ElapsedMilliseconds); // 時間表示 prime_factorization(num); // 素因数分解 AStopWatch.Stop; // タイマー停止 DisplayTime(AStopWatch.ElapsedMilliseconds); // 時間表示 Button1.Enabled := True; end; end.