Have an amazing solution built in RAD Studio? Let us know. Looking for discounts? Visit our Special Offers page!
DelphiRAD Studio事例

(2024年のπの日なので) Delphi で円周率の桁をリアルタイムで表示する

athena-pi-day-4k-2033409-2

このブログは、エンバカデロのMVPであり、 現在はGDK SoftwareのUSディレクターであるJim McKeethによるゲスト投稿の抄訳です。彼は以前、エンバカデロのチーフエバンジェリストとして従事していましたが、現在もDelphiコミュニティを支えています。

USでは3月14日の「パイの日」ということで、2024年もDelphiテイストで祝うために、RudyのBig Numbersライブラリを使用して作成した以前のサンプルコードを改良し、生成中の π の桁をリアルタイムで表示する機能を追加しました。

以前のサンプルでは、好きな桁数を生成することができましたが、リアルタイムでの表示は行えず、すべての処理が完了した後にその結果を表示するというものでした。

RudyのBig Numbersライブラリとは?

このライブラリは、もともと故 Rudy Velthuis によって開発されたもので、Delphi でBig Integer、BigDecimal、および BigRationals をサポートしており、扱えるデータの桁数やメモリ量に上限がありません。

またこのライブラリは非常に扱いやすく、驚くほど優れたパフォーマンスを発揮します。様々なサンプル、デモ、ユニットテストが用意されています。BigRationals型は実験的としてマークされており、BigIntegersはBigDecimalsよりもパフォーマンスが優れている傾向があります。

Delphiで円周率を生成するには?

以前、別のブログでDelphiによる円周率を計算するサンプルコードをご紹介しています。このサンプルでは、チュドノフスキーのアルゴリズムとベイリー=ボールウェイン=プラウフ(BBP)のアルゴリズムの両方をサポートしています。BBPの方が高速で、桁を順番に生成する処理なので、そのアルゴリズムへのコールバックを追加する方が合理的だと考えました。コールバックは一度に 64 桁を返しますが、そのチャンクサイズは簡単に変更できます。

円周率の桁をリアルタイムで見る

円周率の桁がリアルタイムで表示されるのを見るのは、まるで禅みたいでワクワクします。現在はコンソールアプリのみでコールバック関数をサポートしていますが、将来的にはFireMonkeyアプリもアップデートする予定です。

Delphiにおけるベイリー=ボールウェイン=プラウフ(BBP)のπコード

コールバック関数に関する修正コードは、以下の通りです。

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桁ずつコンソールに出力するコード例は、以下の通りです。

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.

現在、無名メソッドの参照を参照を使用しているため、若干のオーバーヘッドが発生することはわかっていますが、コールバックの種類を変更する価値があるかどうか、または変更する価値があるかどうかについてはベンチマークを行っていません。今回ご紹介しているサンプルコードは、数字を表示することが目的となりますので、少し速度を落とす必要があると考えました。

またコールバックからの出力が関数呼び出しの結果と一致することを検証するためのユニットテストのコード例は以下の通りです。

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にアップスケールしたので、いくつかのバリエーションがあります。

athena-pi-day-4k-2033409-2
Kai 1.1 Now Available! What's Coming in RAD Studio 13.2 Florence

Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.

Start Free Trial   Upgrade Today

   Free Delphi Community Edition   Free C++Builder Community Edition

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

IN THE ARTICLES