Collat​​z Conjecture Python - 超过 2 万亿的错误输出(仅限!)

Posted

技术标签:

【中文标题】Collat​​z Conjecture Python - 超过 2 万亿的错误输出(仅限!)【英文标题】:Collatz Conjecture Python - Incorrect Output Above 2 Trillion (Only!) 【发布时间】:2019-01-21 18:11:43 【问题描述】:

我用 Python3 编写了一个计算 Collat​​z 猜想的基本脚本。它需要一个正整数作为输入,并返回步数,直到序列下降到 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")

【问题讨论】:

欢迎使用电脑! :D 【参考方案1】:

这一行:

n = int(n/2)

... 将n 转换为浮点数,将该浮点数除以 2,然后通过丢弃小数部分转换回整数。

对于最大为 2**52 的整数,转换为浮点数是无损的,但对于更大的整数,它必须四舍五入到最接近的 53 位数字,这会丢失信息。

当然,2 万亿远低于 2**53 浮点精度的限制——但从 N 开始的 Collat​​z 序列经常比 N 高得多。2 万亿左右的许多数字有序列过去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 __name__ == '__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

那么,为什么它总是以相同的数量关闭?

嗯,这主要是您碰巧使用的特定数字的巧合。

当您通过3n+1 传递2**53 时,您会将最后一位或最后2 位转换为0,这意味着您通常会切断链的很大一部分并将其替换为只有1或2个部门。但显然会有一些数字,您最终跳转到的链比正确的链。事实上,我只尝试了 3 次就找到了一个:3,743,559,068,799,123 应该需要 326 步,但需要 370 步。

我怀疑(但同样,我什至无法想象如何证明)许多大数字最终会在 375 左右的相同范围内,随着它们(对数)变大而变短一点。为什么?好吧,您可以四舍五入的数字只有这么多——而且它们中的大多数可能彼此处于循环中,您开始进行截断除法。所以,假设几乎2**53 附近的每个数字都有一个超过 50 的舍入周期长度,而万亿范围内的大多数数字在 300 多步内达到2**53 范围......那么他们中的大多数将最终大约是 375。(当然,这些数字是凭空得出的,但您可以进行蒙特卡罗模拟,看看它们与现实的实际差距有多大……)

【讨论】:

我正在检查我的项目 euler 解决方案以查看类似 n //= 2 的内容。不过,在 python 2 中,代码应该是正确的 @RoryDaulton 问题大约是 2 万亿,而不是 200 万。但无论哪种方式,从 N 开始的 Collat​​z 序列在此过程中通常比 N 高得多。在此过程中,接近 2 万亿的许多数字将达到高于 2**53 的数字,但低于该数字的数字很少(或没有?),这一点一点也不令人难以置信。 如果n 的值高于2**53 并使用值2,081,751,768,5592_775_669_024_745 作为输入,我修改了OPs 代码以打印注释,并且注释确实被打印了.所以这增加了你的答案的可信度。 +1 感谢@abarnert 成功了!有趣的是,使用浮点除法时,2**53 以上的所有输出恰好比正确输出低 1,052 次迭代 - 向下舍入 1 减少了很多步骤 :) @RonNahshon 查看我的编辑。您可以找到其他示例,其中它减少了不同数量的步骤,这些步骤可能要少得多,甚至它添加步骤。

以上是关于Collat​​z Conjecture Python - 超过 2 万亿的错误输出(仅限!)的主要内容,如果未能解决你的问题,请参考以下文章

python Collat​​z序列

最大化 Collat​​z 猜想程序 Python 的效率

python 最长的Collat​​z序列(Euler#14)

c_cpp 使用libgmp进行Collat​​z。

Collat​​z C++ 代码的问题

[POJ2262] Goldbach’s Conjecture