检查数字是否是一个完美的平方


Answers:


117

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

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)

提示:它基于平方根的“巴比伦算法”,请参阅Wikipedia。它确实适用于任何正数,只要您有足够的内存来进行计算;-)。

编辑:让我们看一个例子...

x = 12345678987654321234567 ** 2

for i in range(x, x+2):
   print i, is_square(i)

根据需要(并且在合理的时间内;-)打印:

152415789666209426002111556165263283035677489 True
152415789666209426002111556165263283035677490 False

请在基于浮点中间结果提出解决方案之前,请确保它们在此简单示例上正确运行-并不是那么困难(您需要做一些额外的检查,以防计算出的sqrt有点差),只需要有点照顾。

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

OverflowError: long int too large to convert to float

当然,随着数字的不断增长,您将变得越来越聪明。

如果我在赶时间,当然,我会用gmpy -但后来,我显然有失偏颇;-)。

>>> import gmpy
>>> gmpy.is_square(x**7)
1
>>> gmpy.is_square(x**7 + 1)
0

是的,我知道,这很容易,就像作弊一样(有点像我对Python的一般感觉;-)-根本就没有聪明,只有完美的直接性和简单性(而且,在笨拙的情况下,速度却是绝对的) ;-)...


说出您想要的作者信息,gmpy听起来像是完成此任务的绝佳工具。
Mike Graham

3
巴比伦方法效果很好,但是您需要对0和1有特殊情况,以免被零除。
mpenkov 2011年

2
顺便说一句,set([x])={x}
奥斯卡·梅德罗斯

6
是不是setovekill?巴比伦难道不会收敛到int(sqrt(x))我们仅需检查是否 prev != next呢?
Tomasz Gandor 2014年

1
“我知道,这很容易,就像作弊一样(有点像我对Python的一般感觉。”
太好了

38

使用牛顿方法快速将最近的整数平方根归零,然后对其平方,看看是否是您的数字。参见isqrt

Python≥3.8具有math.isqrt。如果使用旧版本的Python,请在此处查找“ def isqrt(n)”实现。

import math

def is_square(i: int) -> bool:
    return i == math.isqrt(i) ** 2

20

由于在处理浮点计算(例如这些计算平方根的方法)时永远不能依赖精确的比较,因此不太容易出错

import math

def is_square(integer):
    root = math.sqrt(integer)
    return integer == int(root + 0.5) ** 2

想象integer9math.sqrt(9)可能是3.0,但也可能类似2.999993.00001,因此立即对结果求平方是不可靠的。知道要int取下限值,0.5首先增加浮点值就意味着,如果我们在一个float仍具有足够好的分辨率来表示我们要寻找的数字附近的范围内,我们将得到想要的值。。


5
if int(root + 0.5) ** 2 == integer:如果int按照floor我们关心的数字行事,那会更好些。
大卫·约翰斯通

@David Johnstone,我更改了这篇文章以使用该实现,我同意比我以前使用的方法更好。无论如何,这里其他人提到的其他一些技术甚至更好,更可靠。
Mike Graham 2010年

我了解FP是近似值,但math.sqrt(9)真的可以2.99999吗?Python的float映射到C的映射double,但是我认为即使16位FP类型的精度也要更高,因此,如果您有一个使用8位FP​​(“ minifloats”)作为其double类型的C编译器?我认为这在技术上是可行的,但对我而言,今天运行Python的计算机上似乎并非如此。

@Ken,我说“类似”表示我正在理解基本概念。无法保证您获得的价值不会比实际价值略小。我无法想象它math.sqrt(9)2.99999在任何特定系统上返回,但是实际结果取决于系统,并且不能期望精确。
Mike Graham

1
此功能对于大正方形(例如152415789666209426002111556165263283035677489)不正确。–
Acumenus

12

如果您感兴趣,我对数学stackexchange上的类似问题有一个纯数学的回答,即“检测理想平方比提取平方根更快”

我自己的isSquare(n)实现可能不是最好的,但是我喜欢它。我花了几个月的时间学习数学理论,数字计算和python编程,将自己与其他贡献者进行比较,等等,以便真正使用这种方法。我喜欢它的简单性和效率。我没有看的更好。告诉我你的想法。

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滑过为True(必要或否则下一个块是无限循环)。

下一个代码块使用位移和位逻辑运算在非常快速的子算法中系统地去除4的幂。最终,我们最终找不到原始n的isSquare,但如果可能,则将k <n减小4的幂。这减小了我们正在处理的数量的大小,并确实加快了巴比伦方法的速度,但同时也使其他检查更快。

第三段代码执行简单的布尔位逻辑测试。任意平方的最小有效三位数(二进制)是001。始终。无论如何,除因4的幂导致的前导零外,这已经被考虑了。如果测试失败,您将立即知道它不是正方形。如果通过,则无法确定。

同样,如果我们以测试值结尾为1,则测试号最初是4的幂,可能本身包括1。

像第三个块一样,第四个块使用简单的模运算符测试十进制的一位数值,并且倾向于捕获通过先前测试的值。同样是mod 7,mod 8,mod 9和mod 13测试。

第五段代码检查一些众所周知的完美正方形图案。以1或9结尾的数字前面是四的倍数。以5结尾的数字必须以5625、0625、225或025结尾。

最后,第六段代码与最高答案者Alex Martelli的答案非常相似。基本上使用古老的巴比伦算法来找到平方根,但是在忽略浮点的同时将其限制为整数值。既要提高速度,又要扩展可测试值的大小。我使用集合而不是列表,因为它花费的时间少得多,我使用了移位而不是除以2,并且我巧妙地选择了一个更有效的初始起始值。

顺便说一句,我确实测试了Alex Martelli的推荐测试编号,以及一些数量级大几个数量级的数字,例如:

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

打印了以下结果:

1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890625 True
1000399717477066534083185452789672211951514938424998708930175541558932213310056978758103599452364409903384901149641614494249195605016959576235097480592396214296565598519295693079257885246632306201885850365687426564365813280963724310434494316592041592681626416195491751015907716210235352495422858432792668507052756279908951163972960239286719854867504108121432187033786444937064356645218196398775923710931242852937602515835035177768967470757847368349565128635934683294155947532322786360581473152034468071184081729335560769488880138928479829695277968766082973795720937033019047838250608170693879209655321034310764422462828792636246742456408134706264621790736361118589122797268261542115823201538743148116654378511916000714911467547209475246784887830649309238110794938892491396597873160778553131774466638923135932135417900066903068192088883207721545109720968467560224268563643820599665232314256575428214983451466488658896488012211237139254674708538347237589290497713613898546363590044902791724541048198769085430459186735166233549186115282574626012296888817453914112423361525305960060329430234696000121420787598967383958525670258016851764034555105019265380321048686563527396844220047826436035333266263375049097675787975100014823583097518824871586828195368306649956481108708929669583308777347960115138098217676704862934389659753628861667169905594181756523762369645897154232744410732552956489694024357481100742138381514396851789639339362228442689184910464071202445106084939268067445115601375050153663645294106475257440167535462278022649865332161044187890626 False

它在0.33秒内完成了此操作。

在我看来,我的算法与Alex Martelli的算法一样,具有所有优点,但是还具有高效的简单测试拒绝功能,可节省大量时间,更不用说减少测试次数,从而节省了很多时间。 4,可提高速度,效率,准确性和可测试数字的大小。在非Python实现中可能尤其如此。

甚至在执行巴比伦根提取之前,所有整数的大约99%被拒绝为非平方,并且在2/3的时间里巴比伦拒绝整数。尽管这些测试并不能显着加快处理速度,但通过将所有4的幂除以所有测试数而得出的奇数确实可以加速巴比伦测试。

我做了一次时间比较测试。我连续测试了从1到1000万的所有整数。仅使用巴比伦方法本身(根据我的特别定制的初步猜测),我的Surface 3平均花费了165秒(准确度为100%)。仅使用我的算法中的逻辑测试(不包括巴比伦人),就花费了127秒,它拒绝了所有整数的99%为非平方,而没有错误地拒绝了任何完美的平方。在通过的那些整数中,只有3%是完美的平方(更高的密度)。使用以上同时使用逻辑测试和巴比伦根提取的完整算法,我们具有100%的准确性,并且只需14秒即可完成测试。前1亿个整数大约需要2分45秒进行测试。

编辑:我已经能够缩短时间。现在,我可以在1分40秒内测试0到1亿之间的整数。检查数据类型和正性会浪费大量时间。取消了前两次检查,我将实验缩短了一分钟。必须假定用户足够聪明,才能知道负数和浮点数不是理想的平方。


至于简单性,很难超越公认的答案。在性能方面,您应该更好。我对以小素数的平方幂来减少目标的价值表示怀疑,但计算小素数的雅各比符号应该是一个胜利。数字越大,此答案的优势越大。
总统詹姆斯·波尔克(James K. Polk)

1
为了使雅各比符号计算提供确定的结果,必须减小小质数的幂。否则,它充其量是概率性的,或非平方性的确定性,但不能验证平方性。这就是为什么我要进行平方幂分解的部分原因。我计算出的唯一雅各比符号是针对相同的小素数。我也只是为了减小测试数的大小而做,以便稍后使用巴比伦方法更快(但这值得商is)。
CogitoErgoCogitoSum

好吧,这肯定是一个很好且独特的答案,如果我将来有一段时间想解决这个问题,请尝试一些计时以改变小质数的数量,以查看是否可以在给定的位大小下找到最佳数量。
总统詹姆斯·波尔克(James K. Polk)

一定要测试我的代码。打破它。我不是专业的程序员,而是数学专业的。Python只是一种爱好。如果它的平均效率更高,我会感到好奇。
CogitoErgoCogitoSum

1
如果您仍然对此感兴趣,那么这里本质上是一个重复的问题其中包含一些有趣的答案,尤其是A.Rex的答案
总统詹姆斯·波尔克(James K. Polk)

12
import math

def is_square(n):
    sqrt = math.sqrt(n)
    return (sqrt - int(sqrt)) == 0

理想平方是一个数字,可以表示为两个相等整数的乘积。math.sqrt(number)返回一个floatint(math.sqrt(number))将结果投向int

如果平方根是整数,例如3,math.sqrt(number) - int(math.sqrt(number))则将为0,并且if语句将为False。如果平方根是像3.2这样的实数,则它将是True并且打印“它不是理想的平方”。

对于大型非正方形(例如152415789666209426002111556165263283035677490),它会失败


更改if (math.sqrt(number)-int(math.sqrt(number))):a=math.sqrt(number)接着又行:if a-int(a):。这是因为它只需要计算一次平方根,这对于大n而言是重要的
unseen_rider

@JamesKPolk为什么?
user1717828'2

我非常确定sqrt-int(sqrt)与sqrt%1相同。您的整个函数可能只是返回math.sqrt(n)%1 == 0
CogitoErgoCogitoSum

6

我的答案是:

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

它基本上执行平方根运算,然后以1取模以去除整数部分,如果结果为0,则返回True否则False。在这种情况下,x可以是任意大数,只是不如python可以处理的最大浮点数大:1.7976931348623157e + 308

对于较大的非正方形,例如152415789666209426002111556165263283035677490,这是不正确的。


5

可以使用decimal模块来获取任意精度的平方根并轻松检查“精确度”来解决此问题:

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]

对于具有真正巨大价值的示范:

# 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

如果您增加要测试的值的大小,这最终会变得很慢(200,000位平方需要接近一秒钟的时间),但是对于更中等的数字(例如20,000位),它仍然比人类注意到的要快单个值(我的机器上约为33毫秒)。但是由于速度不是您的主要考虑因素,因此这是使用Python标准库实现此目标的好方法。

当然,使用它并进行gmpy2测试会更快得多gmpy2.mpz(x).is_square(),但是如果您不是第三方软件包,那么上面的方法就很好用了。


5

我刚刚在另一个线程上(上面找到一些完美的正方形)在上面的一些示例中发布了一些细微的变化,并认为我会在此处发布的内容中包含一些细微的变化(使用nsqrt作为临时变量),以防万一。使用:

import math

def is_square(n):
  if not (isinstance(n, int) and (n >= 0)):
    return False 
  else:
    nsqrt = math.sqrt(n)
    return nsqrt == math.trunc(nsqrt)

对于较大的非正方形,例如152415789666209426002111556165263283035677490,这是不正确的。


2

这是我的方法:

def is_square(n) -> bool:
    return int(n**0.5)**2 == int(n)

取数字的平方根。转换为整数。走广场。如果数字相等,则它是一个理想的平方,否则就不是。

对于较大的正方形,例如152415789666209426002111556165263283035677489,这是不正确的。


对于负数不起作用,但仍然是一个很好的解决方案!
里克M.19年

1

您可以对舍入的平方根进行二进制搜索。对结果求平方,看是否与原始值匹配。

使用FogleBirds答案可能会更好-尽管请注意,因为浮点算法是近似的,因此可以放弃这种方法。原则上,由于精度损失,您可能会从一个大整数(比理想平方大一个)获得误报。


1

如果除以平方根后剩下的模数(余数)为0,则它​​是一个理想平方。

def is_square(num: int) -> bool:
    return num % math.sqrt(num) == 0

我对照上升到1000的完美正方形列表进行了检查。


0
  1. 确定数字将有多长。
  2. 采取增量0.000000000000 ....... 000001
  3. 查看(sqrt(x))^ 2--x是否大于/等于/小于delta并根据delta误差进行确定。

0

此响应与您陈述的问题无关,但与您在您发布的代码中看到的隐式问题有关,即“如何检查某物是否为整数?”

通常,您对该问题的第一个答案是“不要!” 的确,在Python中,类型检查通常不是正确的选择。

但是,对于那些极少数的例外,可以使用isinstance函数,而不是在数字的字符串表示形式中查找小数点:

>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False

当然,这适用于变量而不是值。如果我想确定该是否为整数,可以这样做:

>>> x=5.0
>>> round(x) == x
True

但是,正如其他所有人都详细介绍过的那样,在大多数此类非玩具示例中都需要考虑浮点问题。


1
“这适用于变量而不是值”是什么意思?您可以使用round(5.0)== 5.0和isinstance(x,int)而不会出现问题。(并且OOWTDI只是调用x.is_integer()。)
Veky

0

如果要在一个范围内循环并为每个不是完美平方的数字做某事,则可以执行以下操作:

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

如果您想对每个都是完美平方的数字做某事,生成器将变得更加容易:

(n * n for n in range(upper))

0

我认为这有效并且非常简单:

import math

def is_square(num):
    sqrt = math.sqrt(num)
    return sqrt == int(sqrt)

对于较大的非正方形,例如152415789666209426002111556165263283035677490,这是不正确的。


这与以上答案相同。
科瓦尔斯基

0

@Alex Martelli解决方案的一种变体,没有 set

什么时候x in seenTrue

  • 在大多数情况下,它是最后一个加入,例如1022产生x的序列511,256,129,68,41,32,3131 ;
  • 在一些情况下(即,对于完美的正方形的前身),它是第二个到最后一个加入,例如1023产生511,256,129,68,41,32,31,32

因此,只要电流x大于或等于前一个,就立即停止:

def is_square(n):
    assert n > 1
    previous = n
    x = n // 2
    while x * x != n:
        x = (x + (n // x)) // 2
        if x >= previous:
            return False
        previous = x
    return True

x = 12345678987654321234567 ** 2
assert not is_square(x-1)
assert is_square(x)
assert not is_square(x+1)

与原始算法的等效性测试为1 <n <10 ** 7。在相同的时间间隔上,这个稍微简单一些的变体快大约1.4倍。


0
a=int(input('enter any number'))
flag=0
for i in range(1,a):
    if a==i*i:
        print(a,'is perfect square number')
        flag=1
        break
if flag==1:
    pass
else:
    print(a,'is not perfect square number')

尽管此代码可能会解决这个问题,一个很好的答案也应该解释什么代码的作用和如何它可以帮助。
BDL

0

这个想法是要运行一个从i = 1到floor(sqrt(n))的循环,然后检查平方是否等于n。

bool isPerfectSquare(int n) 
{ 
    for (int i = 1; i * i <= n; i++) { 

        // If (i * i = n) 
        if ((n % i == 0) && (n / i == i)) { 
            return true; 
        } 
    } 
    return false; 
} 

-3
import math

def is_square(n):
    sqrt = math.sqrt(n)
    return sqrt == int(sqrt)

对于大型非正方形(例如152415789666209426002111556165263283035677490),它会失败


2
这是仅代码答案。请提供一些理由。
hotzst

您无法通过@hotzst推理吗?这是很有意义的,我什至不是python专家。它不是最大的测试,但在理论上和小情况下都是有效的。
CogitoErgoCogitoSum

1
@CogitoErgoCogitoSum:你不明白。使用Google之类的搜索引擎进行搜索无法找到仅代码答案。是否能够理解答案是无关紧要的。
总统詹姆斯·波尔克(James K. Polk)'18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.