CodeIQ 巨大な整数の演算に挑戦!の僕の解答です!
本来は、ちゃんと Advent Calendar 開催中に公開する予定でしたが、この問題の締め切りが 12/21 まで延びたので公開を延期していました。
問題の本文は
コラッツの問題が巨大な正の整数でも成り立つことを実証しましょう。 コラッツの問題が「9が50個連続する巨大な50桁の整数」でも成り立つこと(最終的に1になること)を確認できるWindows向けコンソールプログラムを作成してください。 ソースコードは、50行以内且つ、DelphiまたはAppmethodに標準搭載されているデータ型、クラス、ライブラリのみを使用して記述してください。 ※コラッツの問題・・ 自然数nに対して、nが奇数なら3をかけて1を加える。 偶数なら2で割る。 この処理を繰り返すとすべての自然数が1になる。 http://ja.wikipedia.org/wiki/コラッツの問題でした。
これに対して、僕の最初の解答がこちら。
Delphi でお手軽に巨大な整数を扱うと言えばでおなじみの Data.FmtBcd を使いました。program CollatzMin;usesData.FmtBcd;varBcd: TBcd;Sup: Integer;beginBcd := StrToBcd(StringOfChar('9', 50));while (Bcd > 1) dobeginSup := Bcd.Precision and 1;if((Bcd.Fraction[Bcd.Precision shr 1 + Sup - 1] shr (Sup shl 2) and 1) > 0)thenBcd := Bcd * 3 + 1elseBcd := Bcd / 2;Writeln(BcdToStr(Bcd));end;Readln;end.
本来は2進化10進数を扱うためのクラスです(2進化10進数についてはリンク先でどうぞ)。
このクラスを使うと 64 桁までの数に対応できます。
問題の本文にあるように今回は 50 桁なので、TBcd で余裕です。
TBcd には operator が定義されているので普通の数のように加減乗除できます。
TBcd を使った結果、上の解答になりました。
ですが、これ 3n + 1 って「必ず偶数になるんじゃね!」→「偶数なら2で割り切れるんじゃね!」と考えて最適化した結果が次のコードです。
結構スッキリしました。program CollatzBcd;usesData.FmtBcd;varN: TBcd;Od: Integer;beginN := StrToBcd(StringOfChar('9', 50));while (N > 1) dobeginOd := BcdPrecision(N) and 1;Od := N.Fraction[BcdPrecision(N) shr 1 + Od - 1] shr (Od shl 2) and 1;N := (N * (1 + Od shl 1) + Od) / 2;Writeln(BcdToStr(N));end;Readln;end.
ただ、こちらのコードは最初の一回目の演算結果が表示されないのですが…
とはいえ、解答としてはオッケーでした。
Data パッケージにあるからなのか、あまり知られていない TBcd クラス。
かなり簡単に使えるので、知って置いて損は無いと思います。
:
:
最後に蛇足。
自前で整数演算を実装した物も提出していました。
こちらは、普通に乗算をして繰り上がりや繰り下がりの演算をするものです。
こちらは49行でギリギリでした!program CollatzBytes;usesSystem.SysUtils;constCOUNT = 50;DIGIT = 9;varBytes: TBytes;i, H, L: Integer;function ToString: String;vari: Integer;beginResult := '';for i := High(Bytes) downto Low(Bytes) doResult := Result + Bytes[i].ToString;Result := Result.TrimLeft(['0']);end;beginSetLength(Bytes, COUNT * 2);for i := 0 to COUNT - 1 doBytes[i] := DIGIT;while (ToString.Length > 1) or (Bytes[0] > 1) dobeginif (Odd(Bytes[0])) thenbeginH := 1;for i := Low(Bytes) to High(Bytes) dobeginL := Bytes[i] * 3 + H;H := L div 10;Bytes[i] := L - H * 10;end;endelse beginL := 0;for i := High(Bytes) downto Low(Bytes) dobeginH := Bytes[i];Bytes[i] := Bytes[i] shr 1 + L;L := (H and 1) * 5;end;end;Writeln(ToString);end;Readln;end.
{$APPTYPE CONSOLE} を入れたら50行ちょうどでした!あぶない!
0 件のコメント:
コメントを投稿