检查一个数字是不是是一个完美的正方形

Posted

技术标签:

【中文标题】检查一个数字是不是是一个完美的正方形【英文标题】:Check if a number is a perfect square检查一个数字是否是一个完美的正方形 【发布时间】:2011-01-30 04:08:46 【问题描述】:

我如何检查一个数字是否是一个完美的正方形?

速度无关紧要,目前,只要工作即可。

【问题讨论】:

对于非常大的数字,有一个快速随机算法:petr-mitrichev.blogspot.com/2017/12/a-quadratic-week.html 我不知道该方法能以多快的速度判断整数是否为正方形。此方法不使用平方根或牛顿法。可以在这里找到:math.stackexchange.com/questions/4226869/… 【参考方案1】:

依赖任何浮点计算(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 的情况下, 绝对的速度;-)...

【讨论】:

巴比伦方法效果很好,但是你需要有 0 和 1 的特殊情况以避免被零除。 顺便说一句,set([x]) = x 不是set ovekill 吗?巴比伦人不只是收敛到int(sqrt(x)),我们只需要检查prev != next吗? “我知道,这太容易了,感觉就像在作弊(有点像我对 Python 的总体感觉”。太真实了;) 供参考,这里是gmpy (v2)的链接:pypi.org/project/gmpy2【参考方案2】:

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

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

import math

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

【讨论】:

【参考方案3】:

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

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 仍然具有足够精细分辨率来表示的范围内,我们将获得我们正在寻找的值接近我们正在寻找的数字。

【讨论】:

如果int 充当我们关心的数字的floor ,那么只使用if int(root + 0.5) ** 2 == integer: 会稍微好一些。 @David Johnstone,我更改了这篇文章以使用该实现,我同意这比我以前的方法更好。无论如何,这里提到的其他一些技术甚至更好、更可靠。 我知道 FP 是近似值,但 math.sqrt(9) 真的可以是 2.99999 吗? Python 的 float 映射到 C 的 double,但我认为即使是 16 位 FP 类型也比这具有更高的精度,所以如果你有一个使用 8 位 FP(“minifloats”)作为其 @987654338 的 C 编译器@ 类型?我想这在技术上是可行的,但在我看来,今天任何运行 Python 的计算机都不太可能出现这种情况。 @Ken,我说“类似”是为了表明我正在理解基本概念;不能保证您获得的价值不会略低于确切价值。我无法想象math.sqrt(9) 会在任何特定系统上返回2.99999,但实际结果取决于系统,不能指望准确。 这个函数对于152415789666209426002111556165263283035677489这样的大正方形是不正确的。【参考方案4】:

如果您有兴趣,我会在math stackexchange, "Detecting perfect squares faster than by extracting square root" 对类似问题进行纯数学回复。

我自己的 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,而是找到已按 4 的幂按比例缩小的 k

第三个代码块执行一个简单的布尔位逻辑测试。在二进制中,任何完美正方形的最低有效三位数字都是 001。总是。无论如何,除了由 4 的幂产生的前导零,这已经被解释过了。如果它未通过测试,您立即知道它不是正方形。如果它通过了,你不能确定。

此外,如果我们以 1 作为测试值结束,那么测试数最初是 4 的幂,可能包括 1 本身。

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

第五个代码块检查一些众所周知的完美正方形图案。以 1 或 9 结尾的数字前面是 4 的倍数。并且以 5 结尾的数字必须以 5625、0625、225 或 025 结尾。我曾包括其他数字,但意识到它们是多余的或从未实际使用过。

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

顺便说一下,我确实测试了 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 亿。大量时间浪费在检查数据类型和积极性上。消除前两次检查,我将实验缩短了一分钟。必须假设用户足够聪明,知道负数和浮点数不是完美的正方形。

【讨论】:

为了简单起见,很难超越公认的答案。性能方面,你的应该更好。我对通过小素数的平方幂减少目标的价值持怀疑态度,但计算小素数的雅可比符号应该是一个胜利。数字越大,这个答案的优势就越大。 通过小素数的幂的减少对于 jacobi 符号计算提供确定性结果是必要的。否则,它充其量是概率性的,或非方形的确定性,但不验证方形性。这就是为什么我用平方的幂来分解的部分原因;我计算的唯一 jacobi 符号是我考虑的相同的小素数。我也这样做只是为了减少测试数的大小,以使稍后使用的巴比伦方法更快(但这值得商榷)。 嗯,这肯定是一个很好且独特的答案,如果我将来有时间我想玩这个,尝试一些改变小素数数量的时间,看看是否是最优的可以在给定的位大小处找到数字。 无论如何,测试我的代码。打破它。我不是专业的程序员,我是数学专业的。 Python 只是一种爱好。我很好奇它是否平均效率更高。 如果您仍然感兴趣,基本上有一个重复的问题here 有一些有趣的答案,尤其是A.Rex's answer。【参考方案5】:
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 来说,这很重要 @JamesKPolk 为什么会这样? 我很确定 sqrt - int(sqrt) 与 sqrt%1 相同。您的整个函数可能只是 return math.sqrt(n)%1==0【参考方案6】:

我的答案是:

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

它基本上做一个平方根,然后以 1 取模以去除整数部分,如果结果为 0,则返回 True,否则返回 False。在这种情况下,x 可以是任何大数字,只是没有 python 可以处理的最大浮点数那么大:1.7976931348623157e+308

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

【讨论】:

【参考方案7】:

这可以使用the decimal module 来解决,以获得任意精度的平方根并轻松检查“精确度”:

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() 会快得多,但如果您不喜欢第三方软件包,上述方法也很有效。

【讨论】:

【参考方案8】:

我刚刚在另一个线程 (Finding perfect squares) 上发布了上面一些示例的细微变化,并认为我会在此处包含我在此处发布的内容的细微变化(使用 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。

【讨论】:

【参考方案9】:

这是我的方法:

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

取数字的平方根。转换为整数。拿广场。如果数字相等,那么它是一个完美的正方形,否则不是。

对于 152415789666209426002111556165263283035677489 这样的大正方形是不正确的

【讨论】:

不适用于负数,但仍然是一个很好的解决方案!【参考方案10】:

如果除以平方根剩下的模数(余数)为0,那么它是一个完美的平方。

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

我对照一个完美的正方形列表检查了这个,最高可达 1000。

【讨论】:

【参考方案11】:

您可以对四舍五入的平方根进行二分搜索。将结果平方以查看它是否与原始值匹配。

使用 FogleBirds 的答案可能会更好 - 但要小心,因为浮点算术是近似的,这可能会使这种方法失效。原则上,您可能会从比完美平方大一的大整数中得到误报,例如,由于精度损失。

【讨论】:

【参考方案12】:

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

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

【讨论】:

【参考方案13】:

如果一个从 n 的平方根以上开始,则可以通过观察连续项形成一个递减序列来改进巴比伦方法。

def is_square(n):
    assert n > 1
    a = n
    b = (a + n // a) // 2
    while b < a:
        a = b
        b = (a + n // a) // 2
    return a * a == n

【讨论】:

【参考方案14】:
    决定号码的长度。 取一个增量 0.000000000000.......000001 查看 (sqrt(x))^2 - x 是否大于/等于/小于 delta 并根据 delta 误差做出决定。

【讨论】:

【参考方案15】:

此回复与您陈述的问题无关,而是与我在您发布的代码中看到的一个隐含问题有关,即“如何检查某事物是否为整数?”

您通常会得到的第一个答案是“不要!”确实,在 Python 中,类型检查通常不是正确的做法。

不过,对于那些罕见的例外,与其在数字的字符串表示中查找小数点,不如使用 isinstance 函数:

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

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

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

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

【讨论】:

“这适用于变量而不是值”是什么意思?您可以毫无问题地使用 round(5.0) == 5.0 和 isinstance(x, int)。 (而 OOWTDI 只是调用 x.is_integer()。)【参考方案16】:

如果你想遍历一个范围并对每个不是完美平方的数字做一些事情,你可以这样做:

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))

【讨论】:

【参考方案17】:

我认为这很有效,而且非常简单:

import math

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

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

【讨论】:

这与上面的答案相同。【参考方案18】:
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')

【讨论】:

虽然这段代码可能会解决问题,但一个好的答案还应该解释代码的什么以及它如何提供帮助。【参考方案19】:

如果它是一个完全平方,它的平方根是一个整数,小数部分是0,我们可以使用取模运算符来检查小数部分,并检查它是否为0,对于某些数字它确实失败了,所以,对于安全,即使小数部分为0,我们也会检查它是否是平方根的平方。

import math

def isSquare(n):
    root = math.sqrt(n)
    if root % 1 == 0:
        if int(root) * int(root) == n:
            return True
    return False

isSquare(4761)

【讨论】:

【参考方案20】:
import math

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

对于大的非正方形(例如 152415789666209426002111556165263283035677490)失败

【讨论】:

这是一个代码唯一的答案。请提供一点推理。 您无法通过@hotzst 推理?这很有意义,我什至不是 python 专家。它不是最伟大的测试,但它在理论上和小案例中都是有效的。 @CogitoErgoCogitoSum:你不明白。使用 google 等搜索引擎进行搜索时,无法找到纯代码答案。能否理解答案无关紧要。【参考方案21】:

这个想法是从 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; 
 

【讨论】:

这是一个 Python 问题

以上是关于检查一个数字是不是是一个完美的正方形的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 R 中的 mpfr 包检查一个数字是不是是一个完美的正方形?

如何在不使用 sqrt 的情况下检查整数是不是是完美的正方形 [重复]

在字符串中找到完美的正方形

确定输入是不是是完美正方形的好算法是啥? [复制]

当 Python 中的输入是大数时,如何有效地在一个范围内找到完美的正方形

Python - 在给定的大数范围内找到所有完美正方形的最快方法