计算机快速计算,2^N是如何实现的?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算机快速计算,2^N是如何实现的?相关的知识,希望对你有一定的参考价值。

计算乘方是有快速算法的,并不是一个一个蛮力乘上去的。比如想算2^10000,计算机先算2^5000,再算一次平方,即两个数的乘法。而为了计算2^5000,计算机会先算2^2500再算一次平方。这个算法叫快速幂算法,对于2^N的计算,如果认为每次乘法的时间复杂度是O(1)的话,那整体的时间复杂度只有O(logN)级别。

一般来说,为了实现快速幂算法,首先把指数做二进制表示,比如你要算A的23次方,可以把23分解为16+4+2+1。然后计算B=A^2,C=B^2=A^4,D=(C^2)^2=A^16。最终结果为ABCD相乘。

但这里乘法的复杂度并不是O(1),因为它是无限精度的,也就是所谓的大数乘法。大数乘法也有很多算法,最朴素的,类似手算的方法,复杂度是O(N^2),其他一些方法有分治法,复杂度O(N^1.58),FFT方法,复杂度O(N logN loglogN)等。快速幂的O(logN)次大数乘法中,最复杂的只有最后一次,也就是2^5000的那次,前面的复杂度几何级数衰减,所以整体复杂度也就是最后一次计算的复杂度。如果你用FFT方法的话,复杂度也就是比线性多了一点点,一般计算机上随便算算就出来了。

CPU没有全速运行是因为这个程序只用了1个核心在做计算,而你显示的是总的使用率,所以大概会保持在四分之一的水平。

是否用到了移位操作涉及Python大数运算的具体设计,我不是很懂就不多讲了。但原理上讲也是很有可能的,如果用比特串存储大数的话,那么计算2^N只需要在数组的第N位设置一个1,其余设置为0即可,那么转换到十进制是这段代码中最消耗计算量的部分。

参考技术A

其实远不用2秒,你大量的时间是耗费在结果的输出,也就是第二行命令上的。你要看究竟有多块,在ipython下用命令:%time a = 2**1000000,你会发现耗时仅仅是纳秒级(单位应该是微秒,计算2^1000000大约需要10微秒,说得太夸张了,谢谢
@lixin liu
的指正。)级的。
以2.4GHz的i5 520为例,一个cycle占用1/(2.4GHz),那么运行2.4万个cycle耗时越为10万分之一秒,约为10微秒。
大整数按二进制存,乘方速度其实很快,只是打印出来要转十进制。
而实际上,刨去打印时间,耗费第二的时间应该是在做除法。
如果你有兴趣你还可以学学大整数转十进制的log级别(大概是可能有两个log)算法。

参考技术B

首先,99%时间花在输出上了,实际耗时我估计是在微秒单位上。
其次,虽然python内置无限精度计算,但依旧怀疑python的性能,而且这里是简单的单线程。如果用GMP + std::async的话,再高上一个数量级都不奇怪

不管指数是多少,都可以将其分解为 2 的倍数的和,因为任何整数都能够写成 2 进制的形式,比如 62 = 00111110B。

以上算法中,随着迭代 n 会变成 x, x^2, x^4, x^8,…,我们只需要在合适的时候让它和 ans 相乘即可。合适的时刻就是 N 的二进制表示的相应位上为 1 的时候,这里使用了右移,只需要判断最低位是不是 1 就好了。

这个算法是 O(logN) 的。之所以输入 2**100000 很久没有出现答案,那是因为显示出来要花费大量时间,输入输入 `a = 2**1000000` 那么就立刻执行完毕了。

快速实现字符n-gram for word

我编写了以下用于计算字符双字母的代码,输出就在下面。我的问题是,如何获得排除最后一个字符(即t)的输出?有没有更快更有效的计算字符n-gram的方法?

b='student'
>>> y=[]
>>> for x in range(len(b)):
    n=b[x:x+2]
    y.append(n)
>>> y
['st', 'tu', 'ud', 'de', 'en', 'nt', 't']

以下是我想得到的结果:['st','tu','ud','de','nt]

提前感谢您的建议。

答案

要生成双字母:

In [8]: b='student'

In [9]: [b[i:i+2] for i in range(len(b)-1)]
Out[9]: ['st', 'tu', 'ud', 'de', 'en', 'nt']

概括为不同的n

In [10]: n=4

In [11]: [b[i:i+n] for i in range(len(b)-n+1)]
Out[11]: ['stud', 'tude', 'uden', 'dent']
另一答案

试试zip

>>> def word2ngrams(text, n=3, exact=True):
...   """ Convert text into character ngrams. """
...   return ["".join(j) for j in zip(*[text[i:] for i in range(n)])]
... 
>>> word2ngrams('foobarbarblacksheep')
['foo', 'oob', 'oba', 'bar', 'arb', 'rba', 'bar', 'arb', 'rbl', 'bla', 'lac', 'ack', 'cks', 'ksh', 'she', 'hee', 'eep']

但请注意它的速度较慢:

import string, random, time

def zip_ngrams(text, n=3, exact=True):
  return ["".join(j) for j in zip(*[text[i:] for i in range(n)])]

def nozip_ngrams(text, n=3):
    return [text[i:i+n] for i in range(len(text)-n+1)]

# Generate 10000 random strings of length 100.
words = [''.join(random.choice(string.ascii_uppercase) for j in range(100)) for i in range(10000)]

start = time.time()
x = [zip_ngrams(w) for w in words]
print time.time() - start

start = time.time()
y = [nozip_ngrams(w) for w in words]
print time.time() - start        

print x==y

[OUT]:

0.314492940903
0.197558879852
True
另一答案

该函数为n = 1到n提供了ngrams:

def getNgrams(sentences, n):
    ngrams = []
    for sentence in sentences:
        _ngrams = []
        for _n in range(1,n+1):
            for pos in range(1,len(sentence)-_n):
                _ngrams.append([sentence[pos:pos+_n]])
        ngrams.append(_ngrams)
    return ngrams

以上是关于计算机快速计算,2^N是如何实现的?的主要内容,如果未能解决你的问题,请参考以下文章

如何快速自己实现Map

如何快速的计算出一个数的n次方

矩阵快速幂小记

STL系列之七 快速计算x的n次幂 power 的实现

巧用ELK快速实现网站流量监控可视化

如何计算在Android的声音频率是多少