二百万から三百万桁の平方根の桁を生成したい。
私はNewton-Raphsonを認識していますが、biginteger がサポートされていないため、C または C++ でそれを実装する方法がわかりません。誰かが私を正しい方向に向けることができますか?
また、誰かがPythonでそれを行う方法を知っていれば(私は初心者です)、私も感謝します。
二百万から三百万桁の平方根の桁を生成したい。
私はNewton-Raphsonを認識していますが、biginteger がサポートされていないため、C または C++ でそれを実装する方法がわかりません。誰かが私を正しい方向に向けることができますか?
また、誰かがPythonでそれを行う方法を知っていれば(私は初心者です)、私も感謝します。
マッピングを使用してみることができます:
a/b -> (a+2b)/(a+b)
で始まりa= 1, b= 1
ます。これは sqrt(2) に収束します (実際には、連分数表現が得られます)。
重要なポイント: これは、行列の乗算として表すことができます (フィボナッチに似ています)。
a_n と b_n がステップの n 番目の数字の場合
[1 2] [a_n b_n] T = [a_(n+1) b_(n+1)] T
[1 1]
それは今私たちに与えます
[1 2] n [a_1 b_1] T = [a_(n+1) b_(n+1)] T
[1 1]
したがって、2x2 行列が A の場合、A nを計算する必要があります。これは、2 乗を繰り返すことで実行でき、整数演算のみを使用します (したがって、精度の問題について心配する必要はありません)。
また、得られる a/b は常に (gcd(a,b) = gcd(a+2b, a+b) のように) 縮小された形式になることに注意してください。結果、しないでください!
n 番目の分母は (1+sqrt(2))^n のようなものなので、300 万桁を取得するには、3671656番目の項まで計算する必要があります。
約 360 万番目の項を探している場合でも、2 乗を繰り返すと、O(Log n) の乗算と加算で n 番目の項を計算できることに注意してください。
また、これは Newton-Raphson などの反復的なものとは異なり、簡単に並列化できます。
EDIT : このバージョンは前のバージョンよりも好きです。これは、整数と小数の両方を受け入れる一般的なソリューションです。n = 2 で精度 = 100000 の場合、約 2 分かかります。Paul McGuire の提案とその他の提案を歓迎します。
def sqrt_list(n, precision):
ndigits = [] # break n into list of digits
n_int = int(n)
n_fraction = n - n_int
while n_int: # generate list of digits of integral part
ndigits.append(n_int % 10)
n_int /= 10
if len(ndigits) % 2: ndigits.append(0) # ndigits will be processed in groups of 2
decimal_point_index = len(ndigits) / 2 # remember decimal point position
while n_fraction: # insert digits from fractional part
n_fraction *= 10
ndigits.insert(0, int(n_fraction))
n_fraction -= int(n_fraction)
if len(ndigits) % 2: ndigits.insert(0, 0) # ndigits will be processed in groups of 2
rootlist = []
root = carry = 0 # the algorithm
while root == 0 or (len(rootlist) < precision and (ndigits or carry != 0)):
carry = carry * 100
if ndigits: carry += ndigits.pop() * 10 + ndigits.pop()
x = 9
while (20 * root + x) * x > carry:
x -= 1
carry -= (20 * root + x) * x
root = root * 10 + x
rootlist.append(x)
return rootlist, decimal_point_index
[1; 2, 2, ...]
おそらく最も良い方法は、2 の平方根の連分数展開を使用することです。
def root_two_cf_expansion():
yield 1
while True:
yield 2
def z(a,b,c,d, contfrac):
for x in contfrac:
while a > 0 and b > 0 and c > 0 and d > 0:
t = a // c
t2 = b // d
if not t == t2:
break
yield t
a = (10 * (a - c*t))
b = (10 * (b - d*t))
# continue with same fraction, don't pull new x
a, b = x*a+b, a
c, d = x*c+d, c
for digit in rdigits(a, c):
yield digit
def rdigits(p, q):
while p > 0:
if p > q:
d = p // q
p = p - q * d
else:
d = (10 * p) // q
p = 10 * p - q * d
yield d
def decimal(contfrac):
return z(1,0,0,1,contfrac)
decimal((root_two_cf_expansion())
すべての 10 進数の反復子を返します。 t1
およびt2
アルゴリズムでは、次の桁の最小値と最大値です。それらが等しい場合、その数字を出力します。
これは、連分数の負の数などの特定の例外的なケースを処理しないことに注意してください。
(このコードは、あちこちに散らばっている連分数を処理するための Haskell コードの適応です。)
作業用?図書館を使おう!
楽しみのために?よかったね :)
鉛筆と紙で行うことを模倣するプログラムを作成します。最初は 1 桁、次に 2 桁、次に 3、...、...
ニュートンや他の人のことは心配しないでください。あなたのやり方でやってください。
任意の大きな数については、The GNU Multiple Precision Arithmetic Library (for C/C++) を参照してください。
さて、以下は私が書いたコードです。私にとっては、約 60800 秒で 2 の平方根の小数点以下 100 万桁が生成されましたが、プログラムを実行しているときにラップトップがスリープしていたので、それよりも速いはずです。300 万桁の生成を試みることができますが、それを取得するには数日かかる場合があります。
def sqrt(number,digits_after_decimal=20):
import time
start=time.time()
original_number=number
number=str(number)
list=[]
for a in range(len(number)):
if number[a]=='.':
decimal_point_locaiton=a
break
if a==len(number)-1:
number+='.'
decimal_point_locaiton=a+1
if decimal_point_locaiton/2!=round(decimal_point_locaiton/2):
number='0'+number
decimal_point_locaiton+=1
if len(number)/2!=round(len(number)/2):
number+='0'
number=number[:decimal_point_locaiton]+number[decimal_point_locaiton+1:]
decimal_point_ans=int((decimal_point_locaiton-2)/2)+1
for a in range(0,len(number),2):
if number[a]!='0':
list.append(eval(number[a:a+2]))
else:
try:
list.append(eval(number[a+1]))
except IndexError:
pass
p=0
c=list[0]
x=0
ans=''
for a in range(len(list)):
while c>=(20*p+x)*(x):
x+=1
y=(20*p+x-1)*(x-1)
p=p*10+x-1
ans+=str(x-1)
c-=y
try:
c=c*100+list[a+1]
except IndexError:
c=c*100
while c!=0:
x=0
while c>=(20*p+x)*(x):
x+=1
y=(20*p+x-1)*(x-1)
p=p*10+x-1
ans+=str(x-1)
c-=y
c=c*100
if len(ans)-decimal_point_ans>=digits_after_decimal:
break
ans=ans[:decimal_point_ans]+'.'+ans[decimal_point_ans:]
total=time.time()-start
return ans,total
以下は、整数a の平方根を精度の桁数まで計算するための短いバージョンです。これは、2 x桁に 10 を掛けた後のaの整数の平方根を見つけることによって機能します。
def sqroot(a, digits):
a = a * (10**(2*digits))
x_prev = 0
x_next = 1 * (10**digits)
while x_prev != x_next:
x_prev = x_next
x_next = (x_prev + (a // x_prev)) >> 1
return x_next
いくつかの注意事項があります。
結果を文字列に変換し、正しい位置に小数点を追加する必要があります (小数点を表示したい場合)。
非常に大きな整数を文字列に変換するのは、それほど高速ではありません。
非常に大きな整数の除算も (Python では) あまり高速ではありません。
システムのパフォーマンスによっては、小数点以下 200 万から 300 万の平方根を計算するのに 1 時間以上かかる場合があります。
ループが常に終了することは証明していません。最後の桁が異なる 2 つの値の間で振動する場合があります。またはそうではないかもしれません。
Pythonはすでに箱から出して大きな整数をサポートしており、それがC / C ++であなたを阻む唯一のものである場合は、いつでも自分でクイックコンテナクラスを書くことができます。
あなたが言及した唯一の問題は、大きな整数の欠如です。そのためにライブラリを使用したくない場合は、そのようなクラスを作成するためのヘルプを探していますか?
これは、すべての場合に終了する、より効率的な整数平方根関数 (Python 3.x) です。平方根にはるかに近い数値から開始するため、ステップ数が少なくなります。int.bit_length には Python 3.1 以降が必要であることに注意してください。簡潔にするために、エラー チェックは省略されています。
def isqrt(n):
x = (n >> n.bit_length() // 2) + 1
result = (x + n // x) // 2
while abs(result - x) > 1:
x = result
result = (x + n // x) // 2
while result * result > n:
result -= 1
return result