我怎么能检查一个数字是一个完美的广场?
我怎么能检查一个数字是一个完美的广场?
目前来说,速度无关紧要。
依赖任何浮点运算( 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)
提示:它是基于平方根的“巴比伦algorithm”,参见维基百科 。 它适用于任何你有足够的内存进行计算的正数,以完成;-)。
编辑 :让我们看一个例子…
x = 12345678987654321234567 ** 2 for i in range(x, x+2): print i, is_square(i)
根据需要(以及在合理的时间内)打印;-):
152415789666209426002111556165263283035677489 True 152415789666209426002111556165263283035677490 False
请在根据浮点中间结果提出解决scheme之前,确保它们在这个简单的例子中正确工作 – 这并不困难(只需要一些额外的检查,以防sqrt计算结果稍微偏离),只需要一个一点关心。
然后尝试用x**7
find巧妙的方法来解决你会遇到的问题,
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的情况下,速度很快; – )…
用牛顿的方法快速调入最接近的整数平方根,然后将其平方,看看它是否是你的数字。 见isqrt 。
由于在处理浮点计算时(例如这些计算平方根的方法),您决不可能依赖精确的比较,所以不太容易出错的实现
import math def is_square(integer): root = math.sqrt(integer) if int(root + 0.5) ** 2 == integer: return True else: return False
想象一下integer
是9
。 math.sqrt(9)
可以是3.0
,但也可以是2.99999
或3.00001
类的东西,所以将结果平方即可。 知道int
取最低值,首先将float值增加0.5
意味着如果我们处于一个范围内, float
仍然有足够精细的分辨率来表示附近的值我们在看。
import math if (math.sqrt(number)-int(math.sqrt(number))): print "it's not a perfect square"
一个完美的正方形是一个数字,可以表示为两个相等的整数的乘积。 math.sqrt(number)
返回一个float
。 int(math.sqrt(number))
将结果转换为int
。
如果平方根是一个整数,比如3,那么math.sqrt(number) - int(math.sqrt(number))
将是0, if
语句将是False
。 如果平方根是一个像3.2这样的实数,那么它将是True
并打印出“这不是一个完美的方形”。
我是Stack Overflow的新手,并且快速浏览一下以find解决scheme。 我刚刚在另一个线程上发现了一些上面的例子( find完美的方块 ),并认为我会在这里发布一些细微的变化(使用nsqrt作为临时variables),以防感兴趣/使用:
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
模块来解决,以获得任意精度的平方根,并且容易检查“正确性”:
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
如果你增加被testing值的大小,最终会变得很慢(对于一个200,000位的方块来说,接近一秒),但是对于更温和的数字(比如说20000位),它仍然比人们注意到的要快个人价值观(我的机器〜〜33毫秒)。 但是由于速度并不是你最关心的问题,所以这是用Python的标准库来完成的好方法。
当然,使用gmpy2
会更快,只要testinggmpy2.mpz(x).is_square()
,但如果第三方软件包不是你的东西,上面的工作就很好。
你可以二进制search圆angular的平方根。 将结果平方以查看它是否与原始值匹配。
用FogleBirds的答案你可能会更好 – 虽然要小心,因为浮点算术是近似的,可以抛弃这种方法。 你原则上可以从一个比完美平方更大的整数中得到一个假的正数,例如,由于失去了精度。
- 决定这个号码会有多长。
- 采取一个三angular形0.000000000000 ……. 000001
- 查看(sqrt(x))^ 2 – x是否大于/等于/小于delta,并根据delta误差来决定。
这个回应与你所说的问题无关,但是对于我在你发布的代码中看到的一个隐含的问题,即“如何检查是否是一个整数?
你通常会回答这个问题的第一个答案是“不要!” 确实,在Python中,types检查通常是不正确的。
但是,对于那些罕见的例外情况,不要在数字的string表示中寻找小数点,而是使用isinstance函数:
>>> isinstance(5,int) True >>> isinstance(5.0,int) False
当然这适用于variables而不是价值。 如果我想确定值是一个整数,我会这样做:
>>> x=5.0 >>> round(x) == x True
但是,正如其他人已经详细介绍的那样,在这类事情的大部分非玩具例子中都有浮点问题需要考虑。
如果你想遍历一个范围,并为每个不是完美的方格的数字做点事情,你可以这样做:
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))
我自己的isSquare(n)的实现可能不是最好的,但我喜欢它。 在math理论,数字计算和python编程方面,花了我好几个月的时间学习,比较自己和其他贡献者等,用这个方法真正点击。 我喜欢它的简单性和效率。 我没有看到更好的。 告诉我你的想法。
def isSquare(n): ## Trivial checks if n != n//1: return False if n < 0: return False if n < 2: return True ## Reduction by powers of 4 with bit-logic while n&65535 == 0: n=n>>16 if n&255 == 0: n=n>>8 if n&15 == 0: n=n>>4 if n&3 == 0: n=n>>2 if n==1: return True ## even power of 2 ## Simple bit-logic test. if n&7 != 1: return False ## Simple modulo equivalency test if n % 10 in [2, 3, 7, 8]: return False ## Not 1,4,5,6,9 in mod 10 if n % 3 == 2: return False ## Not 0,1 mod 3 if n % 7 in [3, 5, 6]: return False ## Not 1,2,4 mod 7 ## Factor out the squares of small primes, if possible. ## Simple factor-quantity test. ## Also ensures coprimality for the Jacobi test. ps = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, \ 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ] # 3 to 97 for i in ps: while n%(i*i)==0: n = n // (i*i) if n%i == 0: # odd power of a factor return False if n==1: return True # even power of small primes # The jacobi symbol (n / i). Easy, fast algorithm. Uses bit operations. # for i relatively prime to n, if ever -1, verified non-square. for i in ps: if jacobi(n,i) == -1: return False ## jacobi non-square ## Babylonian Algorithm. Finding the integer square root. x = n // 2 A = [x, n] while x * x != n: x = (x + (n // x)) // 2 if x in A: return False A += [x] return True
非常直截了当。 首先它检查我们有一个整数,并在那个正整数。 否则没有意义。 它让0和1滑落为真(不知道是否有必要,但更早的实现在这些值失败)。
下一个代码块使用位移和位逻辑操作,以非常快的子algorithm系统地去除4的幂。 如果可能的话,我们最终没有find原来的n的isSquare,而是一个k <n的isSquare,这个k已经被4的幂数缩小了。
第三块代码执行简单的布尔位逻辑testing。 任何完美平方的最不重要的三位数是001.总是。 除了权力4导致的零,无论如何,这已经占了。 如果testing失败,你马上知道它不是一个正方形。 如果它通过,你不能确定。
像第三个方块一样,第四个方法用简单的模数运算符来testing十进制的位置值,并且倾向于捕获通过前一个testing的值。 另外一个mod 3和mod 7testing。
第五块试图分解所有小于100的小数的平方。减less数量的大小,是一个相对简单的检查。 无论如何,这个位对于下一个代码是必需的。
第六块代码是对雅可比符号的调用,并且与二次残差有关。 它使用前面代码块中使用的主要列表作为基本值。 前面的分块因子是必要的,因为我们需要雅可比testing的互质关系才有价值。
最后,第七块代码非常类似于顶级答复者 – Alex Martelli – 的答案。 基本上使用古巴比伦algorithmfind平方根,但将其限制为整数值而忽略浮点。 为了速度和扩大可testing的值的大小,都要做好。
有趣的是,在前1500万(至less)正整数,我的代码从来没有得到这么多。 我几乎不必强迫非方形的平方根。 事实上,我不能完全确定我曾经有过。 这段代码唯一的一次贯穿 – 根据我的经验 – 当我有一个完美的方形时。 所以我几乎可以跳过它,并返回一个非常可能的真实。
顺便说一下,我testing了Alex Martelli推荐的testing编号,以及几个数量级的大小。
x= ** 2 for i in range(x, x+2): print(i, isSquare(i))
打印结果如下:
True False
它在0.8秒内完成了。
在我看来,我的algorithm和Alex Martelli的algorithm一样,具有所有的好处,但是还有一个额外的好处,即通过4次幂乘以琐碎的检查来减小大小,这提高了速度,效率,准确性和数字大小是可testing的。 在非Python实现中可能尤其如此。 然后,它还执行另一个简单的布尔逻辑testing,在甚至将自己的问题转移到更复杂或计算成本更高的算术方法之前,几乎立即将其余8个中的7个作为明确的非方块移除等等。
这并不是说我的方法更好。 我将计算时间与巴比伦方法本身进行了比较,并对我的全部函数进行了比较,实际情况是有时直的平方根计算速度要快得多,有时它的速度要慢得多。 就像我以前说过的,我没有理由实际运行该函数的平方根计算部分。 这是为了保险,但我有有效的结果,不负责任的大数字。 虽然我的algorithm本身有时候是巴比伦algorithm的两倍,但是如果你把巴比伦人剪出来的话,在这种情况下几乎可以把运行时间减半。
Jacobi
def jacobi(m=5, n=0): ## (m / n) n = int(n//1) m = int(m//1) if m == 0: if n==1: return 1 return 0 elif m == 1: return 1 elif m == 2: return 1 if (n&7) in (1,7) else -1 elif m&1 == 0: return jacobi(2,n) * jacobi(m/2, n) elif n < m: return jacobi(m%n, n) elif m&3 == 3 and n&3 == 3: return -jacobi(n, m) else: return jacobi(n, m)
此外,Jacobi和小素数的分解也可能是可以忽略的。 我特别推荐它,如果你的isSquare的目的是首先find素数 – 不需要循环推理。
我不确定Python,但你可以做一些事情:
function isSquare(x) = x == floor(sqrt(x) + 0.5)^2
也就是说,取一个数字,find平方根,将其四舍五入到最接近的整数,将其平方,并testing它是否与原始数字相同。 (正如Mike Graham所指出的那样,为了防止像sqrt(4)
返回1.9999999...
由于浮点math问题, floor
和0.5
被完成。)
如果你有兴趣,有一次很好的讨论, 以确定一个整数的平方根是一个整数 。
编辑澄清。
这是我的方法
int(n**0.5)**2 == int(n)
把数字的平方根转换为整数,然后取平方,如果数字相等,那么它是一个完美的正方形,否则不是。
我认为这个工作很简单:
from math import sqrt def is_perfect_square(num): return int(sqrt(num)) == sqrt(num)
这在数字上就像你可能拥有的解决scheme一样天真。 它适用于小数目。
def is_perfect_square(n): return (n ** .5).is_integer()
很明显,它失败了很多,如152415789666209426002111556165263283035677490。
我的答案是:
def checkSquare(x):return x**.5%1==0
这基本上是做一个平方根,然后用1来取整整数部分,如果结果是0,返回True
否则返回False
。 在这种情况下,x可以是任何大数字,只是不如python可以处理的最大浮点数大:1.7976931348623157e + 308
a = math.sqrt(n) b = int(a) a == b
有一个非常简单的方法来做到这一点。 找出这个数字有多less个因素(包括一个和它本身)。 如果它有一个奇数的因素,这是一个正方形。
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
如果函数的结果是奇数,则是方形的。
编辑:
这可能不是计算机程序的最佳方法,但对于人类来说这是一个很好的方法。
使用巴比伦方法,我对原始解决scheme略有改进。 不是使用一个集合来存储每个先前生成的近似值,而是只存储最近的两个近似值,并根据当前的近似值进行检查。 这节省了大量的时间浪费检查整个先前的近似集。 我使用的是java而不是python和BigInteger类,而不是普通的原始整数。
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; }