このブログは、エンバカデロのMVPであり、 現在はGDK SoftwareのUSディレクターであるJim McKeethによるゲスト投稿の抄訳です。彼は以前、エンバカデロのチーフエバンジェリストとして従事していましたが、現在もDelphiコミュニティを支えています。
USでは3月14日の「パイの日」ということで、2024年もDelphiテイストで祝うために、RudyのBig Numbersライブラリを使用して作成した以前のサンプルコードを改良し、生成中の π の桁をリアルタイムで表示する機能を追加しました。
以前のサンプルでは、好きな桁数を生成することができましたが、リアルタイムでの表示は行えず、すべての処理が完了した後にその結果を表示するというものでした。
Table of Contents
RudyのBig Numbersライブラリとは?
このライブラリは、もともと故 Rudy Velthuis によって開発されたもので、Delphi でBig Integer、BigDecimal、および BigRationals をサポートしており、扱えるデータの桁数やメモリ量に上限がありません。
またこのライブラリは非常に扱いやすく、驚くほど優れたパフォーマンスを発揮します。様々なサンプル、デモ、ユニットテストが用意されています。BigRationals型は実験的としてマークされており、BigIntegersはBigDecimalsよりもパフォーマンスが優れている傾向があります。
Delphiで円周率を生成するには?
以前、別のブログでDelphiによる円周率を計算するサンプルコードをご紹介しています。このサンプルでは、チュドノフスキーのアルゴリズムとベイリー=ボールウェイン=プラウフ(BBP)のアルゴリズムの両方をサポートしています。BBPの方が高速で、桁を順番に生成する処理なので、そのアルゴリズムへのコールバックを追加する方が合理的だと考えました。コールバックは一度に 64 桁を返しますが、そのチャンクサイズは簡単に変更できます。
円周率の桁をリアルタイムで見る
円周率の桁がリアルタイムで表示されるのを見るのは、まるで禅みたいでワクワクします。現在はコンソールアプリのみでコールバック関数をサポートしていますが、将来的にはFireMonkeyアプリもアップデートする予定です。
Delphiにおけるベイリー=ボールウェイン=プラウフ(BBP)のπコード
コールバック関数に関する修正コードは、以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
function BBPpi(Places: UInt64; CallBack: TChunkCallback = nil): TDigits; // Bailey-Borwein-Plouffe begin SetLength(Result, Places); var idx: Uint64 := 0; var q := BigInteger.One; var r := BigInteger.Zero; var t := BigInteger.One; var k := BigInteger.One; var n := BigInteger.Create(3); var l := BigInteger.Create(3); var buffer: TDigits; SetLength(buffer, CallbackChunkSize); var bufferIdx: Integer := 0; while true do begin if 4*q+r-t = places then break; var newR := 10 * (r - n * t); n := (10 * (3 * q + r)) div t - 10 * n; q := q * 10; r := newR; end else begin var newR := (2 * q + r) * l; var newN := (q * (7 * k)+2+(r * l)) div (t * l); q := q * k; t := t * l; l := l + 2; k := k + 1; n := newN; r := newR; end; end; // Handle remaining buffer if (bufferIdx > 0) and Assigned(CallBack) then begin SetLength(buffer, bufferIdx); // Resize buffer to actual used size before callback CallBack(buffer); end; end; |
そして、この関数を呼び出し、1桁ずつコンソールに出力するコード例は、以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
var firstChunk: Boolean = True; procedure WritelnCallBack(Chunk: TDigits); begin var digits: String; if firstChunk then begin digits := DigitsToString(Chunk).Insert(1,'.'); firstChunk := False; end else digits := DigitsToString(Chunk); // slow down the output for demonstration purposes for var i := low(digits) to High(digits) do begin Write(digits[i]); sleep(100); end; end; const Places = 10000; begin BBPpi(Places, WritelnCallBack); Writeln; end. |
現在、無名メソッドの参照を参照を使用しているため、若干のオーバーヘッドが発生することはわかっていますが、コールバックの種類を変更する価値があるかどうか、または変更する価値があるかどうかについてはベンチマークを行っていません。今回ご紹介しているサンプルコードは、数字を表示することが目的となりますので、少し速度を落とす必要があると考えました。
またコールバックからの出力が関数呼び出しの結果と一致することを検証するためのユニットテストのコード例は以下の通りです。
1 2 3 4 5 6 7 8 9 10 11 |
procedure BBPpiTest.CompareCallbackToResult; begin var CallBackString := ''; var CallBackStringBuilder: TChunkCallBack := procedure(Chunk: TDigits) begin CallBackString := CallBackString + DigitsToString(Chunk); end; var calcPi := DigitsToString(BBPpi(100, CallBackStringBuilder)); Assert.AreEqual(calcPi, CallBackString); end; |
このクールな「ヴェイパーウェイヴ アテナ πの日」の壁紙はどこで入手できますか?
よくぞ聞いてくれました!4Kにアップスケールしたので、いくつかのバリエーションがあります。
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition