【问题标题】:Finding if n! + 1 is a perfect square寻找如果 n! + 1 是一个完美的正方形
【发布时间】:2015-08-07 12:04:40
【问题描述】:

我正在尝试编写一个程序来查找一个介于 0 和 100 之间的数字 n,使得 n! + 1 是一个完美的正方形。我正在尝试这样做,因为我知道只有三个,所以这是为了测试我的 Python 能力。

请参阅Brocard's problem

【问题讨论】:

  • 你为什么失败了?您遇到了什么错误或问题?
  • 这是数论中一个非常有趣的开放式问题:en.wikipedia.org/wiki/Brocard%27s_problem 看起来没有任何优雅的捷径,所以在 [0,100] 中强力搜索 n 可能是最好的选择可以期待。但正如一些答案所指出的那样,使用任意精度整数数学而不是浮点数很重要。

标签: python math perfect-square


【解决方案1】:

这是另一个仅使用整数的版本,通过添加 2 的递减幂来计算平方根,例如 intsqrt(24680) 将被计算为 128+16+8+4+1。

 def intsqrt(n):
    pow2 = 1
    while pow2 < n:
        pow2 *= 2
    sqrt = 0
    while pow2:
        if (sqrt + pow2) ** 2 <= n:
            sqrt += pow2
        pow2 //= 2
    return sqrt

factorial = 1
for n in range(1, 101):
    factorial *= n
    if intsqrt(factorial + 1) ** 2 == factorial + 1:
        print(n)

【讨论】:

    【解决方案2】:

    math.sqrt 总是返回一个float,即使这个浮点数恰好是4.0。正如文档所说,“除非另有明确说明,否则所有返回值都是浮点数。”

    所以,你对type(math.sqrt(x)) == int 的测试永远不会是真的。

    您可以尝试通过检查浮点数是否表示整数来解决此问题,如下所示:

    sx = math.sqrt(x)
    if round(sx) == sx:
    

    甚至有a built-in method 尽可能地做到了这一点:

    if sx.is_integer():
    

    但请记住,float 值并不是实数的完美表示,并且总是存在舍入问题。例如,对于一个太大的数字,sqrt 可能四舍五入 为整数,即使它确实不是一个完美的正方形。例如,如果math.sqrt(10000000000**2 + 1).is_integer()True,即使显然这个数字不是一个完美的平方。

    我可以告诉你这在你的价值观范围内是否安全,但你能说服自己吗?如果不是,你不应该只是假设它是。

    那么,有没有一种方法可以检查不受float 道路问题的影响?当然,我们可以使用整数算术来检查:

    sx = int(round(math.sqrt(x)))
    if sx*sx == x:
    

    但是,正如 Stefan Pochmann 所指出的,即使这个检查是安全的,这是否意味着整个算法都是安全的?不; sqrt 本身可能已经四舍五入到您失去整数精度的程度。

    因此,您需要一个准确的sqrt。您可以通过使用具有巨大配置精度的decimal.Decimal 来做到这一点。这将需要一些工作和大量内存,但这是可行的。像这样:

    decimal.getcontext().prec = ENOUGH_DIGITS
    sx = decimal.Decimal(x).sqrt()
    

    但是ENOUGH_DIGITS 是多少位数字?那么,100!+1 到底需要多少位数字?

    所以:

    decimal.getcontext().prec = 156
    while n <= 100:
        x = math.factorial(n) + 1
        sx = decimal.Decimal(x).sqrt()
        if int(sx) ** 2 == x:
            print(sx)
        n = n + 1
    

    如果您考虑一下,有一种方法可以将所需的精度降低到 79 位,但我将把它作为练习留给读者。


    您大概应该解决这个问题的方法是使用纯整数数学。例如,您可以通过使用Newton's method 来确定一个整数是否是对数时间的平方,直到您的近似误差小到可以检查两个相邻的整数。

    【讨论】:

    • 我正在玩decimalsqrt 并试图解决您的问题,但遇到了问题。为什么它告诉我 floor(sqrt(8)) 是 3? from decimal import *; getcontext().prec = 1; getcontext().rounding = ROUND_FLOOR; Decimal(8).sqrt()
    • @StefanPochmann:很有趣。你必须看看它是如何实现的(和/或运行一些其他测试——它是否给你1.20 的 sqrt 和1.10prec=3?),然后看看 IEEE 854 是如何实际定义 @ 987654352@ 以确保它不需要使用此方法或等效方法。但它绝对闻起来像虫子。 (当然这不会影响这种情况,因为int(3) ** 2 == 8 会是假的。)
    • @StefanPochmann:P.S.,自 2008 年 754 和 854 合并以来,您知道在哪里可以找到 IEEE 854 的最新草案(免费)标准吗? (我可以找到最终标准的副本,但这可能是侵犯版权,我不想链接到它……)
    • @StefanPochmann:知道了。 #1388949 已关闭,因为 sqrt 被明确定义为四舍五入,这是 IBM 的 General Decimal Arithmetic Specification 所要求的。显然decimal 模块遵循这个作为它的主要定义,而不是 IEEE 854-1987(它在第 4 节中说“......第 5 节中指定的每个操作。应该像它首先产生一个正确到无限精度的中间结果一样执行,并且具有无限范围,然后将结果四舍五入……”)。
    • 是的,我刚看到#1388949 和其他一些,谢谢。我仍然很不高兴the documentation for sqrt 没有提到它忽略了舍入设置(它只是说“将参数的平方根返回到完全精度。”)。
    【解决方案3】:

    对于非常大的数字,最好完全避免使用浮点平方根,因为您会遇到太多精度问题,甚至无法保证您会在正确答案的 1 个整数值范围内。幸运的是 Python 本身就支持任意大小的整数,因此您可以编写一个整数平方根检查函数,如下所示:

    def isSquare(x):
        if x == 1:
            return True
        low = 0
        high = x // 2
        root = high
        while root * root != x:
           root = (low + high) // 2
           if low + 1 >= high:
              return False
           if root * root > x:
              high = root
           else:
              low = root
        return True
    

    然后你可以像这样遍历从 0 到 100 的整数:

    n = 0
    while n <= 100:
        x = math.factorial(n) + 1
        if isSquare(x):
            print n
        n = n + 1
    

    【讨论】:

      【解决方案4】:

      math.sqrt 返回的数字绝不是 int,即使它是整数。How to check if a float value is a whole number

      【讨论】:

      • 为整数时为什么不返回整数类型?
      • @PythonNewbie,int 和 integer 不是一回事。前者只是代表后者的一种方式。浮点数也可以准确地表示整数,而且平方根函数总是返回相同的类型更简单、更一致。
      猜你喜欢
      • 2016-08-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-23
      • 1970-01-01
      • 2015-11-06
      相关资源
      最近更新 更多