web-dev-qa-db-ja.com

Collat​​z予想Python-2兆を超える誤った出力(のみ!)

Collat​​z推測を計算する基本的なスクリプトをPython3で作成しました。入力として正の整数を取り、シーケンスが1に下がるまでのステップ数を返します。

私のスクリプトは、整数入力〜2兆未満に対して完全に機能しますが、このしきい値を超えると出力が小さすぎます。

例として、いくつかの入力、スクリプトの出力、および実際の正しい出力を次に示します。

Integer Input          Script Output     Correct Output
   989,345,275,647        1,348             1,348 
 1,122,382,791,663        1,356             1,356 
 1,444,338,092,271        1,408             1,408 
 1,899,148,184,679        1,411             1,411 
 2,081,751,768,559          385             1,437 
 2,775,669,024,745          388             1,440 
 3,700,892,032,993          391             1,443 
 3,743,559,068,799          497             1,549 `

正しい出力値はこのリンクに基づいています: http://www.ericr.nl/wondrous/delrecs.html

私のスクリプトの出力は、常に2兆を超える入力の正しい出力よりも正確に1,052少なくなりますが、何が原因であるかはわかりません。

誰が間違っているのか、すべての入力に対して正しく動作するようにスクリプトを更新/修正する方法を説明できますか?私はPythonは問題なく任意の大きな数を受け入れることができると思いました...

ありがとうございました!

# Python Code for the Collatz Conjecture
# Rules: Take any integer 'n' and assess:
# If integer is even, divide by 2 (n/2)
# If integer is odd, multiply by 3 and add 1 (3n+1)
# Result: a list of all steps until 'n' goes down to 1

while True:
    print("Please enter a positive integer:")
    n = input("")
    if n == 'q':
        print("Until next time ...\n")
        break
    try:
        n = int(n)
        if n > 0:
            i = 0
            while n > 1:
                if n % 2 == 0:
                    n = int(n/2)
                    i += 1
                else:
                    n = int((3*n)+1)
                    i += 1
            print("# of steps to reach '1' = ", str(i), "\n")
        else:
            print("Sorry, that's not a valid entry. Please try again!\n")
    except ValueError:
        print("Sorry, that's not a valid entry. Please try again!\n")
38
ronster1000

この行:

_n = int(n/2)
_

nをfloatに変換し、そのfloatを2で除算し、小数部分を破棄してintに戻します。

_2**52_までの整数の場合、floatへの変換は無損失ですが、それより大きい場合は、最も近い53ビットの数値に丸める必要があり、情報が失われます。

もちろん、2兆は、浮動小数点精度の_2**53_制限を十分に下回っていますが、Nで始まるCollat​​zシーケンスは、Nをはるかに上回って頻繁に移動します。 _2**53_、その下のごく少数の数字はそうです。ちょうど2兆個から始まる長い数字のシーケンス全体が_2**53_を超えることもありますが、その下の単一の数字はそうではありません。しかし、最大2兆個のすべての数のシーケンス全体を構築せずに、そのようなことを証明する方法がわかりません。 (証拠がある場合、それはおそらく、私の給与等級を超えるさまざまな異なる条件下での推測の既存の部分的証拠に大きく依存するでしょう...)

とにかく、解決策は簡単です:あなたは整数除算を使用したい:

_n = n // 2
_

以下に例を示します。

_>>> n = 2**53 + 3
>>> n
9007199254740995
>>> int(n/2)
4503599627370498
>>> n//2
4503599627370497
_

これが実際にコードで発生していることを確認するには、これを試してください:

_def collatz(n):
    overflow = False
    i = 0
    while n > 1:
        if n > 2**53:
            overflow=True
        if n % 2 == 0:
            n = int(n/2)
            i += 1
        else:
            n = int((3*n)+1)
            i += 1
    return i, overflow

if __== '__main__':
    import sys
    for arg in sys.argv[1:]:
        num = int(arg.replace(',', ''))
        result, overflow = collatz(num)
        print(f'{arg:>30}: {result:10,} {overflow}')
_

これを実行すると:

_$ python3 collatz.py 989,345,275,647 1,122,382,791,663 1,444,338,092,271 1,899,148,184,679 2,081,751,768,559 2,775,669,024,745 3,700,892,032,993 3,743,559,068,799
_

…それは私に与えます:

_           989,345,275,647:      1,348 False
         1,122,382,791,663:      1,356 False
         1,444,338,092,271:      1,408 False
         1,899,148,184,679:      1,411 False
         2,081,751,768,559:        385 True
         2,775,669,024,745:        388 True
         3,700,892,032,993:        391 True
         3,743,559,068,799:        497 True
_

そのため、間違った答えを得たのとまったく同じ場合に_2**53_を過ぎました。

修正を確認するには、int(n/2)を_n//2_に変更します。

_           989,345,275,647:      1,348 False
         1,122,382,791,663:      1,356 False
         1,444,338,092,271:      1,408 False
         1,899,148,184,679:      1,411 False
         2,081,751,768,559:      1,437 True
         2,775,669,024,745:      1,440 True
         3,700,892,032,993:      1,443 True
         3,743,559,068,799:      1,549 True
_

それでは、なぜ同じ量だけ常にオフになるのでしょうか?

まあ、それはほとんどあなたが使っている特定の数字の単なる偶然の一致です。

_2**53_を_3n+1_経由で渡すと、最後のビットまたは最後の2ビットのいずれかを0に変換します。つまり、通常はチェーンの大部分を切り離して置き換えますちょうど1つまたは2つの部門で。しかし、ジャンプするチェーンが正しいチェーンよりも長いである場合、明らかにいくつかの数字があります。実際、それを見つけるのに3回しかかかりませんでした。_3,743,559,068,799,123_は326ステップかかるはずですが、370かかります。

私は、多くの大きな数字が375前後で同じ範囲になり、(対数的に)大きくなるにつれて少し短くなると疑っています(しかし、証明する方法さえ想像できません)。どうして?まあ、丸めることができる数は非常に多く、それらのほとんどは、おそらく、切り捨て除算を開始するお互いのサイクルにあります。したがって、_2**53_の近くのほぼすべての数値は50を少し超える丸めサイクル長を持ち、兆単位の範囲のほとんどの数値は300を少し超えるステップで_2**53_の範囲に到達するとします。それらは約375になります(もちろん、これらの数字は薄い空気から引き出されますが、モンテカルロシミュレーションを実行して、現実からどれだけ離れているかを確認できます…)

63
abarnert