寻找如果 n! + 1 是一个完美的正方形
Finding if n! + 1 is a perfect square
我正在尝试编写一个程序来查找一个介于 0 和 100 之间的数字 n,使得 n! + 1 是一个完美的正方形。我正在尝试这样做,因为我知道只有三个,所以它是为了测试我的 Python 能力。
数字 math.sqrt returns 永远不是整数,即使它是整数。How to check if a float value is a whole number
math.sqrt
总是 returns 一个 float
,即使那个浮动恰好是 4.0
。正如文档所说,"Except when explicitly noted otherwise, all return values are floats."
因此,您对 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 位,但我将把它留作 reader.
的练习。
你可能应该解决这个问题的方法是使用纯整数数学。例如,您可以通过使用 Newton's method 来确定一个整数是否是对数时间的平方,直到您的近似误差小到足以检查两个相邻的整数。
对于非常大的数字,最好完全避免使用浮点平方根,因为您会 运行 陷入太多的精度问题,您甚至不能保证您会在正确值的 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
这是另一个仅适用于整数的版本,通过添加 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)
我正在尝试编写一个程序来查找一个介于 0 和 100 之间的数字 n,使得 n! + 1 是一个完美的正方形。我正在尝试这样做,因为我知道只有三个,所以它是为了测试我的 Python 能力。
数字 math.sqrt returns 永远不是整数,即使它是整数。How to check if a float value is a whole number
math.sqrt
总是 returns 一个 float
,即使那个浮动恰好是 4.0
。正如文档所说,"Except when explicitly noted otherwise, all return values are floats."
因此,您对 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 位,但我将把它留作 reader.
的练习。你可能应该解决这个问题的方法是使用纯整数数学。例如,您可以通过使用 Newton's method 来确定一个整数是否是对数时间的平方,直到您的近似误差小到足以检查两个相邻的整数。
对于非常大的数字,最好完全避免使用浮点平方根,因为您会 运行 陷入太多的精度问题,您甚至不能保证您会在正确值的 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
这是另一个仅适用于整数的版本,通过添加 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)