python正整数平方根_数学-python中的整数平方根

这是一个非常简单的实现:

def i_sqrt(n):

i = n.bit_length() >> 1 # i = floor( (1 + floor(log_2(n))) / 2 )

m = 1 << i # m = 2^i

#

# Fact: (2^(i + 1))^2 > n, so m has at least as many bits

# as the floor of the square root of n.

#

# Proof: (2^(i+1))^2 = 2^(2i + 2) >= 2^(floor(log_2(n)) + 2)

# >= 2^(ceil(log_2(n) + 1) >= 2^(log_2(n) + 1) > 2^(log_2(n)) = n. QED.

#

while m*m > n:

m >>= 1

i -= 1

for k in xrange(i-1, -1, -1):

x = m | (1 << k)

if x*x <= n:

m = x

return m

这只是二进制搜索。 将值k初始化为2的最大幂,且不超过平方根,然后检查是否可以设置每个较小的比特,同时保持结果不大于平方根。 (以降序一次检查一位。)

对于k的较大值(例如大约m << 1或大约(m<<1) + (1<

比user448810描述的牛顿方法实现更快。

比我的其他答案中的k内置方法慢得多,而且慢得多。

与nibot描述的Longhand平方根相当,但速度稍慢。

所有这些方法都可以在这种大小的输入上成功,但是在我的机器上,此功能大约需要1.5秒,而@Nibot大约需要0.9秒,@ user448810大约需要19秒,而gmpy2内置方法花费的时间不到一毫秒。 (!)。 例:

>>> import random

>>> import timeit

>>> import gmpy2

>>> r = random.getrandbits

>>> t = timeit.timeit

>>> t('i_sqrt(r(20000))', 'from __main__ import *', number = 5)/5. # This function

1.5102493192883117

>>> t('exact_sqrt(r(20000))', 'from __main__ import *', number = 5)/5. # Nibot

0.8952787937686366

>>> t('isqrt(r(20000))', 'from __main__ import *', number = 5)/5. # user448810

19.326695976676184

>>> t('gmpy2.isqrt(r(20000))', 'from __main__ import *', number = 5)/5. # gmpy2

0.0003599147067689046

>>> all(i_sqrt(n)==isqrt(n)==exact_sqrt(n)[0]==int(gmpy2.isqrt(n)) for n in (r(1500) for i in xrange(1500)))

True

可以轻松地泛化此函数,尽管它并不是很好,因为我对k的初始猜测还不够精确:

def i_root(num, root, report_exactness = True):

i = num.bit_length() / root

m = 1 << i

while m ** root < num:

m <<= 1

i += 1

while m ** root > num:

m >>= 1

i -= 1

for k in xrange(i-1, -1, -1):

x = m | (1 << k)

if x ** root <= num:

m = x

if report_exactness:

return m, m ** root == num

return m

但是,请注意k也具有m << 1方法。

实际上,该方法可以被修改并应用于任何(负的,递增的)函数k,以确定“ m << 1的整数逆”。 但是,要选择(m<<1) + (1<

编辑:感谢@Greggo指出k函数可以重写以避免使用任何乘法。 这带来了令人印象深刻的性能提升!

def improved_i_sqrt(n):

assert n >= 0

if n == 0:

return 0

i = n.bit_length() >> 1 # i = floor( (1 + floor(log_2(n))) / 2 )

m = 1 << i # m = 2^i

#

# Fact: (2^(i + 1))^2 > n, so m has at least as many bits

# as the floor of the square root of n.

#

# Proof: (2^(i+1))^2 = 2^(2i + 2) >= 2^(floor(log_2(n)) + 2)

# >= 2^(ceil(log_2(n) + 1) >= 2^(log_2(n) + 1) > 2^(log_2(n)) = n. QED.

#

while (m << i) > n: # (m<

m >>= 1

i -= 1

d = n - (m << i) # d = n-m^2

for k in xrange(i-1, -1, -1):

j = 1 << k

new_diff = d - (((m<<1) | j) << k) # n-(m+2^k)^2 = n-m^2-2*m*2^k-2^(2k)

if new_diff >= 0:

d = new_diff

m |= j

return m

请注意,通过构造,未设置m << 1的第k位,因此按位或可用于实现(m<<1) + (1<

>>> t('improved_i_sqrt(r(20000))', 'from __main__ import *', number = 5)/5.

0.10908999762373242

>>> all(improved_i_sqrt(n) == i_sqrt(n) for n in xrange(10**6))

True

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值