关于python:检查一个数字是否是一个完美的正方形

Check if a number is a perfect square

我怎样才能检查一个数字是否是一个完美的正方形?

现在,速度不重要,只是工作而已。


依赖任何浮点计算(math.sqrt(x)x**0.5的问题是,您不能真正确定它是否准确(对于足够大的整数x来说,它不会准确,甚至可能溢出)。幸运的是(如果不着急;-)有许多纯整数的方法,例如下面的……:

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有点偏差),只是需要注意一点。

然后尝试使用x**7,找到解决问题的巧妙方法,

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〕的话。math.sqrt(9)可以是3.0,但也可以是2.999993.00001之类的东西,因此立即调整结果是不可靠的。知道int取下限值,增加0.5的浮动值,首先意味着如果我们的范围内float仍然有足够的分辨率来表示接近我们要查找的数字,我们将得到我们要查找的值。


1
2
3
import math
if (math.sqrt(number)-int(math.sqrt(number))):
    print"it's not a perfect square"

完全平方是一个可以表示为两个相等整数的乘积的数。math.sqrt(number)返回floatint(math.sqrt(number))将结果投射到int上。

如果平方根是一个整数,例如3,那么math.sqrt(number) - int(math.sqrt(number))将是0,if语句将是False。如果平方根是3.2这样的实数,那么它将是True,并打印"它不是一个完美的平方"。


如果你感兴趣,我在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第三块代码执行一个简单的布尔位逻辑测试。任何完全平方的最小有效三位数(二进制)都是001。总是。除了四次幂产生的前导零,反正这已经被考虑过了。如果它没有通过测试,你马上就会知道它不是一个正方形。如果它通过了,你不能确定。好的。

另外,如果我们最后得到一个1作为测试值,那么测试号最初是4的幂,包括1本身。好的。

与第三个块一样,第四个块使用简单的模数运算符测试以十进制表示的位值,并倾向于捕获在前一个测试中滑动的值。还有一个mod 7、mod 8、mod 9和mod 13测试。好的。

第五块代码检查一些著名的完美方形图案。以1或9结尾的数字前面是4的倍数。以5结尾的数字必须以5625、0625、225或025结尾。我包括了其他人,但我意识到他们是多余的,或者从未实际使用过。好的。

最后,第六块代码非常类似于顶级答案——亚历克斯·马泰利——答案。基本上使用古巴比伦算法找到平方根,但将其限制为整数值,而忽略浮点。为速度和扩展可测试值的大小而执行。我使用集合而不是列表,因为它花费的时间要少得多,我使用位移位而不是二除,而且我聪明地选择了一个更有效的初始起始值。好的。

顺便说一句,我测试了亚历克斯·马泰利推荐的测试编号,以及一些比这大很多个数量级的数字,比如:好的。

1
2
3
x=1000199838770766116385386300483414671297203029840113913153824086810909168246772838680374612768821282446322068401699727842499994541063844393713189701844134801239504543830737724442006577672181059194558045164589783791764790043104263404683317158624270845302200548606715007310112016456397357027095564872551184907513312382763025454118825703090010401842892088063527451562032322039937924274426211671442740679624285180817682659081248396873230975882215128049713559849427311798959652681930663843994067353808298002406164092996533923220683447265882968239141724624870704231013642255563984374257471112743917655991279898690480703935007493906644744151022265929975993911186879561257100479593516979735117799410600147341193819147290056586421994333004992422258618475766549646258761885662783430625 ** 2
for i in range(x, x+2):
    print(i, isSquare(i))

打印以下结果:好的。

1
2
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False

它在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作为临时变量),以防它引起兴趣/使用:

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)


这可以通过使用decimal模块得到任意精度平方根,并容易检查"精确性"来解决:

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标准库的好方法。

当然,使用gmpy2并只测试gmpy2.mpz(x).is_square()会更快,但是如果第三方软件包不是你的东西,那么上面的工作就相当好了。


我的答案是:

1
def checkSquare(x):return x**.5%1==0

这基本上是做平方根,然后模乘1去掉整数部分,如果结果是0,返回True,否则返回False。在这种情况下,x可以是任意大的数字,而不是python可以处理的最大浮点数:1.7976931348623157E+308


这是我的方法

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

但正如其他人已经详细介绍过的那样,在大多数这种事情的非玩具例子中都会考虑浮点问题。


  • 决定号码的长度。
  • 取增量0.00000000000…….000001
  • 查看(sqrt(x))^2-x是否大于/等于/小于delta,并根据delta错误进行决定。

  • 我认为这是可行的,而且非常简单:

    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所指出的,为了防止像sqrt(4)这样的情况由于浮点数学而返回1.9999999...floor0.5被添加。)

    如果您感兴趣,曾经有一个非常好的讨论,讨论了确定整数平方根是否为整数的最快方法。

    为澄清而编辑。


    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

    如果函数的结果是奇数,则它是一个正方形。

    编辑:

    对于计算机程序来说,这可能不是最好的方法,但对于人类来说,这是一种很好的方法。