Javaで効率的なアルゴリズムを見つけようとしています。2つの整数a
とb
の繰り返しの小数部を見つけるためにa/b
。
例えば。 5/7 = 0.714258 714258 ...
私は現在、長除算法しか知りません。
ここには2つの一般的なアプローチがあると思います。本質的に「ブルートフォース」で最長の繰り返し文字列を探すか、数論の問題として解くことができます。
私がこの問題に遭遇して久しぶりですが、特別なケース(1/n)はProject Eulerの問題#26なので、その特定の名前の効率的な解決策を検索することで、より多くの情報を見つけることができる場合があります。 1回の検索でEli BenderskyのWebサイトが表示され、彼は 彼の解決策 について説明します。 Mathworldの Decimal Expansionsページの理論の一部を以下に示します :
非正規の小数_
m/n
_は周期的であり、m
とは無関係のピリオドlambda(n)
を持ちます。これは、最大_n-1
_桁です。n
が10に比較的素数である場合、_m/n
_のピリオドlambda(n)
はphi(n)
の約数であり、最大でphi(n)
桁です。ここで、phi
はトーティエント関数です。lambda(n)
は10の 乗法次数 であることがわかります(modn
)(Glaisher 1878、Lehmer 1941) 。有理数の10進展開の繰り返し部分の桁数は、分母の乗数の次数から直接求めることもできます。
私の数論は今のところ少し錆びているので、私ができる最善のことはあなたをその方向に向けることです。
_n < d
_とすると、_n/d
_の繰り返し部分を理解しようとしています。 p
を繰り返し部分の桁数とすると、n/d = R * 10^(-p) + R * 10^(-2p) + ... = R * ((10^-p)^1 + (10^-p)^2 + ...)
になります。括弧で囲まれた部分は、1/(10^p - 1)
に等しいジオメトリックシリーズです。
つまり、n / d = R / (10^p - 1)
です。 R = n * (10^p - 1) / d
を取得するために並べ替えます。 Rを見つけるには、p
を1から無限大にループし、d
がn * (10^p - 1)
を均等に分割したらすぐに停止します。
Pythonでの実装は次のとおりです。
_def f(n, d):
x = n * 9
z = x
k = 1
while z % d:
z = z * 10 + x
k += 1
return k, z / d
_
(k
は繰り返しシーケンスの長さを追跡するため、たとえば、1/9と1/99を区別できます)
この実装は、(皮肉にも)10進展開が有限の場合は永久にループしますが、無限の場合は終了します。ただし、_n/d
_は、2または5以外のd
のすべての素因数がn
にも存在する場合に限り、有限の10進数表現を持つため、このケースを確認できます。
長割り? :/
結果を文字列に変換し、それに このアルゴリズム を適用します。文字列が通常の型で十分に長くない場合は、BigDecimalを使用します。