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 来确定一个整数是否是对数时间的平方,直到您的近似误差小到可以检查两个相邻的整数。