提问人: 提问时间:3/22/2010 最后编辑:Karl Knechtel 更新时间:9/24/2023 访问量:223722
检查一个数字是否是一个完美的平方
Check if a number is a perfect square
答:
您可以二进制搜索四舍五入的平方根。对结果进行平方,查看它是否与原始值匹配。
FogleBirds的答案可能会更好 - 但要小心,因为浮点运算是近似的,这可能会使这种方法失败。原则上,您可能会从大于完美平方的大整数中得到误报,例如,由于精度损失。
依赖任何浮点计算(或)的问题在于,你不能真正确定它是准确的(对于足够大的整数,它不会,甚至可能溢出)。幸运的是(如果不着急的话;-)有许多纯整数方法,例如: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)
提示:它基于平方根的“巴比伦算法”,参见维基百科。它确实适用于任何具有足够内存的正数,以便计算继续进行完成;
编辑:让我们看一个例子......
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 的情况下,纯粹的速度;-)......
评论
set([x])
= {x}
set
int(sqrt(x))
prev != next
使用牛顿的方法快速将最接近的整数平方根归零,然后将其平方,看看它是否是您的数字。请参阅 isqrt。
Python ≥ 3.8 具有 math.isqrt
。如果使用旧版本的 Python,请在此处查找 “” 实现。def isqrt(n)
import math
def is_square(i: int) -> bool:
return i == math.isqrt(i) ** 2
由于在处理浮点计算(例如这些计算平方根的方法)时,您永远不能依赖精确的比较,因此不易出错的实现是
import math
def is_square(integer):
root = math.sqrt(integer)
return integer == int(root + 0.5) ** 2
想象一下. 可能是 ,但它也可能是类似 或 的东西,因此立即平方结果是不可靠的。知道这需要底值,首先增加浮点值意味着如果我们在一个范围内仍然具有足够精细的分辨率来表示我们正在寻找的数字,我们将得到我们正在寻找的值。integer
9
math.sqrt(9)
3.0
2.99999
3.00001
int
0.5
float
评论
if int(root + 0.5) ** 2 == integer:
int
floor
math.sqrt(9)
2.99999
float
double
double
math.sqrt(9)
2.99999
- 确定该号码的长度。
- 取增量 0.0000000000000.......000001
- 查看 (sqrt(x))^2 - x 是否大于 / 等于 /小于 delta,并根据 delta 误差做出决定。
这个回答与你陈述的问题无关,而是与我在你发布的代码中看到的一个隐含问题有关,即“如何检查某物是否为整数?
对于这个问题,你通常会得到的第一个答案是“不要!的确,在 Python 中,类型检查通常不是正确的做法。
但是,对于那些罕见的例外情况,与其在数字的字符串表示形式中查找小数点,不如使用 isinstance 函数:
>>> isinstance(5,int)
True
>>> isinstance(5.0,int)
False
当然,这适用于变量而不是值。如果我想确定该值是否为整数,我会这样做:
>>> x=5.0
>>> round(x) == x
True
但是,正如其他人已经详细介绍的那样,在大多数非玩具示例中,都需要考虑浮点问题。
评论
我只是在另一个线程(寻找完美正方形)上发布了上面的一些示例的轻微变化,并认为我会包括我在这里发布的内容的轻微变化(使用 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)来说,这是不正确的。
import math
def is_square(n):
sqrt = math.sqrt(n)
return (sqrt - int(sqrt)) == 0
完美平方是一个可以表示为两个相等整数的乘积的数字。 返回 . 将结果转换为 。math.sqrt(number)
float
int(math.sqrt(number))
int
如果平方根是整数,例如 3,则为 0,语句为 。如果平方根是像 3.2 这样的实数,那么它将是并打印“它不是一个完美的正方形”。math.sqrt(number) - int(math.sqrt(number))
if
False
True
对于较大的非正方形(如 152415789666209426002111556165263283035677490),它会失败。
评论
if (math.sqrt(number)-int(math.sqrt(number))):
a=math.sqrt(number)
if a-int(a):
这可以使用 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()
如果你想在一个范围内循环,并对每个不是完美正方形的数字做一些事情,你可以做这样的事情:
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))
import math
def is_square(n):
sqrt = math.sqrt(n)
return sqrt == int(sqrt)
对于较大的非正方形(如 152415789666209426002111556165263283035677490),它会失败。
评论
这是我的方法:
def is_square(n) -> bool:
return int(n**0.5)**2 == int(n)
取数字的平方根。转换为整数。以广场为例。如果数字相等,那么它就是一个完美的正方形,否则就不是。
对于像152415789666209426002111556165263283035677489这样的大正方形来说,这是不正确的。
评论
如果你有兴趣,我在math 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 结尾的数字前面是 4 的倍数。以 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 的所有幂除以 4 的幂,将所有测试数字减少到奇数,确实加速了巴比伦测试。
我做了一个时间比较测试。我连续测试了从 1 到 1000 万的所有整数。仅使用巴比伦方法本身(根据我特别定制的初始猜测),我的 Surface 3 平均花费了 165 秒(准确率为 100%)。仅使用我的算法中的逻辑测试(不包括巴比伦),它花了 127 秒,它拒绝了 99% 的整数作为非平方,而没有错误地拒绝任何完美的平方。在那些通过的整数中,只有 3% 是完美的平方(密度要高得多)。使用上面同时采用逻辑测试和巴比伦根提取的完整算法,我们有 100% 的准确性,测试只需 14 秒即可完成。前 1 亿个整数大约需要 2 分 45 秒来测试。
编辑:我已经能够进一步缩短时间。我现在可以在 1 分 40 秒内测试从 0 到 1 亿的整数。大量时间浪费在检查数据类型和阳性性上。去掉前两个检查,我把实验时间缩短了一分钟。必须假设用户足够聪明,知道负数和浮点数不是完美的正方形。
评论
我认为这有效并且非常简单:
import math
def is_square(num):
sqrt = math.sqrt(num)
return sqrt == int(sqrt)
对于较大的非正方形(如 152415789666209426002111556165263283035677490)来说,这是不正确的。
评论
我的回答是:
def is_square(x):
return x**.5 % 1 == 0
它基本上做一个平方根,然后取模 1 去掉整数部分,如果结果为 0,则返回,否则返回。在这种情况下,x 可以是任何大数字,只是不如 python 可以处理的最大浮点数大:1.7976931348623157e+308True
False
对于较大的非正方形(如 152415789666209426002111556165263283035677490)来说,这是不正确的。
如果除以平方根剩下的模数(余数)为 0,则它是一个完美的平方。
def is_square(num: int) -> bool:
return num % math.sqrt(num) == 0
我用一个高达 1000 的完美方块列表来检查这一点。
@Alex Martelli 解决方案的变体,没有set
什么时候是 :x in seen
True
- 在大多数情况下,它是最后一个添加的序列,例如 1022 产生序列 511、256、129、68、41、32、31、31
x
; - 在某些情况下(即,对于完美正方形的前身),它是倒数第二个添加的,例如 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 倍。
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')
评论
这个想法是从 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;
}
评论
如果从 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
如果它是一个完美的平方,它的平方根将是一个整数,小数部分将是 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)
在 kotlin 中:
这很容易,并且也通过了所有测试用例。
真的要感谢>>https://www.quora.com/What-is-the-quickest-way-to-determine-if-a-number-is-a-perfect-square
fun isPerfectSquare(num: Int): Boolean {
var result = false
var sum=0L
var oddNumber=1L
while(sum<num){
sum = sum + oddNumber
oddNumber = oddNumber+2
}
result = sum == num.toLong()
return result
}
def isPerfectSquare(self, num: int) -> bool:
left, right = 0, num
while left <= right:
mid = (left + right) // 2
if mid**2 < num:
left = mid + 1
elif mid**2 > num:
right = mid - 1
else:
return True
return False
一个简单的方法(比第二个更快):
def is_square(n):
return str(n**(1/2)).split(".")[1] == '0'
另一种方式:
def is_square(n):
if n == 0:
return True
else:
if n % 2 == 0 :
for i in range(2,n,2):
if i*i == n:
return True
else :
for i in range(1,n,2):
if i*i == n:
return True
return False
这是一个优雅、简单、快速和任意的解决方案,适用于 Python 版本 >= 3.8:
from math import isqrt
def is_square(number):
if number >= 0:
return isqrt(number) ** 2 == number
return False
可能最简单的方法是说
def perfectSquare(x):
return (x == math.isqrt(x) ** 2)
这个算法已经相当快了,因为它使用牛顿方法来查找整数平方根。但是,人们可以识别出很多永远不会是正方形的数字。例如,当您采取 时,只需要根据该方法检查剩余部分。如果 isqrt 方法不可用,也可以使用巴比伦方法的思想,并将其与模思想相结合来提出x % 16
0, 1, 4, 9
isqrt(x)
def perfectSquare(n):
m = n % 16
if m != 0 and m != 1 and m != 4 and m != 9:
return False
x = n
while x * x > n:
x = (x + n // x) // 2
return x * x == n
评论