Check if a number is a perfect square
我怎样才能检查一个数字是否是一个完美的正方形?
现在,速度不重要,只是工作而已。
依赖任何浮点计算(
1 2 3 4 5 6 7 8 9 10 11 | def is_square(apositiveint): x = apositiveint // 2 seen = set([x]) while x * x != apositiveint: x = (x + (apositiveint // x)) // 2 if x in seen: return False seen.add(x) return True for i in range(110, 130): print i, is_square(i) |
提示:它基于平方根的"巴比伦算法",见维基百科。它适用于任何正数,您有足够的内存来完成计算;-)。
编辑:让我们看一个例子…
1 2 3 4 | x = 12345678987654321234567 ** 2 for i in range(x, x+2): print i, is_square(i) |
根据需要(也在合理的时间内)打印:
1 2 | 152415789666209426002111556165263283035677489 True 152415789666209426002111556165263283035677490 False |
请在提出基于浮点中间结果的解决方案之前,确保它们在这个简单的示例上正确工作——这并不难(您只需要进行一些额外的检查,以防计算出的sqrt有点偏差),只是需要注意一点。
然后尝试使用
1 | OverflowError: long int too large to convert to float |
当然,随着数字的不断增长,你必须变得越来越聪明。
当然,如果我赶时间的话,我会用gmpy——但是,我显然有偏见;-)。
1 2 3 4 5 | >>> import gmpy >>> gmpy.is_square(x**7) 1 >>> gmpy.is_square(x**7 + 1) 0 |
是的,我知道,这很容易让人觉得像作弊(我对python的总体感觉有点像;-)——一点也不聪明,只是完美的直接性和简单性(而且,在gmpy的情况下,绝对的速度;-)…
用牛顿的方法快速地把最接近的整数平方根归零,然后把它平方,看看它是不是你的数字。参见ISQRT。
由于在处理浮点计算(例如这些计算平方根的方法)时永远不能依赖于精确的比较,因此不太容易出错的实现是
1 2 3 4 5 6 7 | import math def is_square(integer): root = math.sqrt(integer) if int(root + 0.5) ** 2 == integer: return True else: return False |
假设以东十一〔8〕是以东十一〔9〕的话。
1 2 3 | import math if (math.sqrt(number)-int(math.sqrt(number))): print"it's not a perfect square" |
完全平方是一个可以表示为两个相等整数的乘积的数。
如果平方根是一个整数,例如3,那么
如果你感兴趣,我在MathStackExchange上对一个类似的问题有一个纯粹的数学响应,"检测完美的平方比提取平方根更快"。好的。
我自己对Issquare(n)的实现可能不是最好的,但我喜欢它。我花了几个月的时间学习数学理论、数字计算和Python编程,将自己与其他贡献者进行比较等,才真正使用这种方法。不过,我喜欢它的简单和高效。我没有看得更好。告诉我你的想法。好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | def isSquare(n): ## Trivial checks if type(n) != int: ## integer return False if n < 0: ## positivity return False if n == 0: ## 0 pass return True ## Reduction by powers of 4 with bit-logic while n&3 == 0: n=n>>2 ## Simple bit-logic test. All perfect squares, in binary, ## end in 001, when powers of 4 are factored out. if n&7 != 1: return False if n==1: return True ## is power of 4, or even power of 2 ## Simple modulo equivalency test c = n%10 if c in {3, 7}: return False ## Not 1,4,5,6,9 in mod 10 if n % 7 in {3, 5, 6}: return False ## Not 1,2,4 mod 7 if n % 9 in {2,3,5,6,8}: return False if n % 13 in {2,5,6,7,8,11}: return False ## Other patterns if c == 5: ## if it ends in a 5 if (n//10)%10 != 2: return False ## then it must end in 25 if (n//100)%10 not in {0,2,6}: return False ## and in 025, 225, or 625 if (n//100)%10 == 6: if (n//1000)%10 not in {0,5}: return False ## that is, 0625 or 5625 else: if (n//10)%4 != 0: return False ## (4k)*10 + (1,9) ## Babylonian Algorithm. Finding the integer square root. ## Root extraction. s = (len(str(n))-1) // 2 x = (10**s) * 4 A = {x, n} while x * x != n: x = (x + (n // x)) >> 1 if x in A: return False A.add(x) return True |
挺直的。首先,它检查我们是否有一个整数,以及一个正整数。否则就没有意义了。它允许0作为真值溜过去(否则下一个块是无限循环)。好的。
在使用位移位和位逻辑运算的非常快的子算法中,下一块代码系统地去除了4的幂。如果可能的话,我们最终找不到原始n的Issquare,而是一个k 另外,如果我们最后得到一个1作为测试值,那么测试号最初是4的幂,包括1本身。好的。 与第三个块一样,第四个块使用简单的模数运算符测试以十进制表示的位值,并倾向于捕获在前一个测试中滑动的值。还有一个mod 7、mod 8、mod 9和mod 13测试。好的。 第五块代码检查一些著名的完美方形图案。以1或9结尾的数字前面是4的倍数。以5结尾的数字必须以5625、0625、225或025结尾。我包括了其他人,但我意识到他们是多余的,或者从未实际使用过。好的。 最后,第六块代码非常类似于顶级答案——亚历克斯·马泰利——答案。基本上使用古巴比伦算法找到平方根,但将其限制为整数值,而忽略浮点。为速度和扩展可测试值的大小而执行。我使用集合而不是列表,因为它花费的时间要少得多,我使用位移位而不是二除,而且我聪明地选择了一个更有效的初始起始值。好的。 顺便说一句,我测试了亚历克斯·马泰利推荐的测试编号,以及一些比这大很多个数量级的数字,比如:好的。 打印以下结果:好的。 它在0.33秒内完成了这个。好的。 在我看来,我的算法和亚历克斯·马泰利的算法一样,具有所有的优点,但是它有额外的好处:高效的简单测试拒绝,节省了大量的时间,更不用说测试数量的大小减少了4的幂,这提高了速度、效率、准确性和可测试数量的大小。在非Python实现中尤其如此。好的。 在巴比伦根提取之前,大约99%的整数被当作非正方形拒绝,并且在2/3的时间内,巴比伦人将拒绝整数。尽管这些测试并不能显著加快这一过程,但将所有测试数除以4的所有幂,将所有测试数减少到奇数,确实加速了巴比伦测试。好的。 我做了一个时间比较测试。我连续测试了100万到1000万的整数。仅使用巴比伦人的方法(根据我特别定制的初步猜测),我的表面3平均需要165秒(100%准确度)。使用我的算法中的逻辑测试(不包括巴比伦人),它花费了127秒,它拒绝99%的整数为非平方,没有错误地拒绝任何完美的平方。在通过的整数中,只有3%是完全平方(密度更高)。使用上面的完整算法(同时使用逻辑测试和巴比伦根提取),我们有100%的准确性,测试只需14秒就完成。前1亿个整数的测试大约需要2分45秒。好的。 编辑:我已经能够进一步减少时间。我现在可以在1分40秒内测试0到1亿个整数。检查数据类型和积极性会浪费很多时间。去掉前两个检查,我把实验缩短了一分钟。必须假设用户足够聪明,知道负片和浮点数不是完美的正方形。好的。好啊。 我刚接触栈溢出,并快速浏览以找到解决方案。我刚刚在上面的一些例子上在另一个线程上发布了一个微小的变化(找到完美的正方形),并且认为我会包括我在这里发布的内容的微小变化(使用nsqrt作为临时变量),以防它引起兴趣/使用:
2
3
for i in range(x, x+2):
print(i, isSquare(i))
2
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False
1 2 3 4 5 6 7 8 | import math def is_perfect_square(n): if not ( isinstance(n, (int, long)) and ( n >= 0 ) ): return False else: nsqrt = math.sqrt(n) return nsqrt == math.trunc(nsqrt) |
这可以通过使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import math from decimal import localcontext, Context, Inexact def is_perfect_square(x): # If you want to allow negative squares, then set x = abs(x) instead if x < 0: return False # Create localized, default context so flags and traps unset with localcontext(Context()) as ctx: # Set a precision sufficient to represent x exactly; `x or 1` avoids # math domain error for log10 when x is 0 ctx.prec = math.ceil(math.log10(x or 1)) + 1 # Wrap ceil call in int() on Py2 # Compute integer square root; don't even store result, just setting flags ctx.sqrt(x).to_integral_exact() # If previous line couldn't represent square root as exact int, sets Inexact flag return not ctx.flags[Inexact] |
对于具有真正巨大价值的演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # I just kept mashing the numpad for awhile :-) >>> base = 100009991439393999999393939398348438492389402490289028439083249803434098349083490340934903498034098390834980349083490384903843908309390282930823940230932490340983098349032098324908324098339779438974879480379380439748093874970843479280329708324970832497804329783429874329873429870234987234978034297804329782349783249873249870234987034298703249780349783497832497823497823497803429780324 >>> sqr = base ** 2 >>> sqr ** 0.5 # Too large to use floating point math Traceback (most recent call last): File"<stdin>", line 1, in <module> OverflowError: int too large to convert to float >>> is_perfect_power(sqr) True >>> is_perfect_power(sqr-1) False >>> is_perfect_power(sqr+1) False |
如果增加被测值的大小,这最终会变得相当慢(对于200000位的平方来说,大约需要一秒钟),但是对于更适中的数字(比如20000位),它仍然比人类注意到的单个值要快(在我的机器上大约33毫秒)。但是,由于速度不是您主要关心的问题,所以这是一种使用Python标准库的好方法。
当然,使用
我的答案是:
1 | def checkSquare(x):return x**.5%1==0 |
这基本上是做平方根,然后模乘1去掉整数部分,如果结果是0,返回
这是我的方法
1 | int(n**0.5)**2 == int(n) |
取数字的平方根转换为整数,然后取平方如果数字相等,那么它是一个完全平方,否则不是。
可以用二进制搜索圆角平方根。将结果平方,看看它是否与原始值匹配。
你可能会对Foglebirds的答案有更好的理解——尽管要小心,因为浮点运算是近似的,这会使这种方法失效。原则上,你可以从一个大整数中得到一个假正,这个大整数比一个完美的正方形多一个,例如,由于精度下降。
如果要循环遍历一个范围,并对每一个不是完美平方的数字执行某些操作,可以执行以下操作:
1 2 3 4 5 6 7 8 9 | def non_squares(upper): next_square = 0 diff = 1 for i in range(0, upper): if i == next_square: next_square += diff diff += 2 continue yield i |
如果你想为每一个完美的平方数做点什么,生成器就更简单了:
1 | (n * n for n in range(upper)) |
这个回答与你所说的问题无关,而是与我在你发布的代码中看到的一个隐式问题有关,即"如何检查某个东西是否是整数?"
你通常会得到的第一个答案是"不要!"在Python中,类型检查通常不是正确的事情。
但是,对于这些罕见的异常,我们不需要在数字的字符串表示中查找小数点,而是要使用isInstance函数:
1 2 3 4 | >>> isinstance(5,int) True >>> isinstance(5.0,int) False |
当然,这适用于变量而不是值。如果我想确定该值是否为整数,我会这样做:
1 2 3 | >>> x=5.0 >>> round(x) == x True |
但正如其他人已经详细介绍过的那样,在大多数这种事情的非玩具例子中都会考虑浮点问题。
我认为这是可行的,而且非常简单:
1 2 3 4 | from math import sqrt def is_perfect_square(num): return int(sqrt(num)) == sqrt(num) |
在数字上,这是一个尽可能简单的解决方案。它适用于小数字。
1 2 | def is_perfect_square(n): return (n ** .5).is_integer() |
显然,它失败的原因很多,比如152415789666209426002111556165263283035677490。
我不确定Python,但你可以做如下的事情:
1 | function isSquare(x) = x == floor(sqrt(x) + 0.5)^2 |
也就是说,取一个数字,找到平方根,四舍五入到最接近的整数,然后平方,然后测试它是否与原始数字相同。(如Mike Graham所指出的,为了防止像
如果您感兴趣,曾经有一个非常好的讨论,讨论了确定整数平方根是否为整数的最快方法。
为澄清而编辑。
1 2 3 | a = math.sqrt(n) b = int(a) a == b |
使用巴比伦人的方法,我对原始解决方案略有改进。不使用集合存储以前生成的每个近似值,只存储最近的两个近似值,并对照当前近似值进行检查。这就节省了通过以前的整个近似集进行检查所浪费的大量时间。我用Java代替Python和BigType类,而不是普通的原始整数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | BigInteger S = BigInteger.ZERO; BigInteger x = BigInteger.ZERO; BigInteger prev1 = BigInteger.ZERO; BigInteger prev2 = BigInteger.ZERO; Boolean isInt = null; x = S.divide(BigInteger.valueOf(2)); while (true) { x = x.add(preA.divide(x)).divide(BigInteger.valueOf(2)); if (x.pow(2).equals(S)) { isInt = true; break; } if (prev1.equals(x) || prev2.equals(x)) { isInt = false; break; } prev2 = prev1; prev1 = x; } |
有一种非常简单的方法可以做到这一点。找出这个数字有多少个因子(包括一个和它本身)。如果它有奇数个因子,它就是一个平方。
1 2 3 4 5 6 7 | def getFactors(n): '''Code for counting factors of n.''' result = 1 # not forgetting to count the number itself for i in range(1, n // 2 + 1): if n % i == 0: result += 1 return result |
如果函数的结果是奇数,则它是一个正方形。
编辑:
对于计算机程序来说,这可能不是最好的方法,但对于人类来说,这是一种很好的方法。