更快的求整数幂算法

相信整数幂运算作为一个算法演变的例子是再合适不过的了为了节省访客们宝贵的学习时间省去介绍递归等可能涉及到的初级概念的定义。同时如果发现文中有错误的地方请敞开衣服指正。

因为在测试性能时合适的测试数据是必要的,所以本文用C++的大数类进行演示。

点击获取C++大数类源码

这里我们先列一下会提到的算法分析技术:

动态规划

减治法



测试平台:

  • Linux

  • g++ 4.7


原始递归方法 这就不花时间赘述什么了。

BigInteger pow(BigInteger x, int N) {
  if (N == 1) {
     return x;
  }
  if (N == 0) {
      return 1;
  }
  return pow(x, N - 1) * x;
}

190000

结果值围绕其上下波动,波动范围取决于操作系统的进程调度。显然其时间复杂度为O(N)。显然这个算法有改进空间,对于递归来说,一个递归算法在执行时,会根据其算法而消耗一定的递归调用栈空间(注意,这里并不考虑编译器的尾递归优化),并且会函数调用会进行参数压栈,存储必要信息等操作,带来一定的调用开销,因此其附带有空间复杂度O(N)咱们可以改为迭代版本的计算以节省掉这些开销,但是经过测试发现,时间上并没有明显的提高,所以咱们可以试着将其优化为更好版本的迭代计算,节省开销。于是,我们接下来的改进还是用递归,怎么样,是不是觉得我琢磨不透?不要在意这些细节。



更好的递归计算方法

减治法的定义在文章开始处已经给出,与分治法类似,就不赘述了。(?ˉωˉ?) 维基的描述:

A divide and conquer algorithm works by recursively breaking down a problem into two or more sub-problems of the same (or related) type, until these become simple enough to be solved directly

对此问题,思路可以从此公式开始:

十分抱歉,由于还没搞清楚在live writer上怎么用插件生成Latex公式,所以暂时别扭的着看,您千万别介意。

对于X^64,我们可以将其分解为X^32?X^32,这样为了得到X^64的值,我们只需要计算X32的值,然后将其相乘,同样地,为了得到X^32的值,我们需要得到X^16的值,以此类推,这样我们从就可以得到一个过程:

X^2=X?X

X^4=X^2?X^2

X^8=X^4?X^4

X^16=X^8?X^8

X^32=X^16?X^16

X^64=X^32?X^32

于是,可以看到原本需要64次的乘法,这里只需要6次,对于幂为奇数时的推导就不浪费笔墨了,其推导过程比为偶数时要复杂点,大伙儿可以自己按照公式推导一下。

可以知道,算法的规模实际上取决于幂的大小,于是我们可以将算法整体的分析简化为对幂的分析,设幂为N,则有2i=N,其中i为乘法次数,则有

i=logN

当然这只是理论上来说的,具体的分析稍显复杂。

最后我们给出实现代码:

BigInteger pow(BigInteger x, int N) {
  if (N == 1) {
      return x;
  }
  if (N == 0) {
      return 1;
  }
  if (N % 2 == 0) {
      return pow(x * x, N >>= 2);
  } else {
      return x*pow(x*x, N>>=/2);
  }
}

测试x=13,N=6000得到的运行时间 30000 可以看到时间减少了很多,并且空间复杂度O(logN)相对来说可以忽略不计了,大家可以在自己的机器上测试一下。总而言之,我们的算法得到了改善。但是这还不够,毕竟函数调用仍然有开销,能够将其优化为迭代计算吗?会节省多少时间呢?



迭代版本的求幂

在动手实现迭代版本之前,我们需要考虑几个问题。 1. 既然是迭代,只能从x?x开始算起,可是这和原始递归版本有什么区别呢? 2. 要用多线程吗?可是咱们在优化算法本身啊。 3. 打幂的主意试试?好像改进的递归版本已经做过了,x本身有没有什么可以下手的地方呢?

回头想想,既然要迭代,又要快速,必须得做到像改进的递归算法那样保存中间结果了(参考动态规划),可是保存了中间结果又如何?我们怎么知道一个数需要用哪些中间结果呢?思绪至此,想不出来没关系,咱们可以请教大师前辈们,这也是一种情怀啊~~~经过大师指点,原来,与改进递归版本的一样,x^n的表示方式不需要任何变化,依然是

X2=X?X

X4=X2?X2

X8=X4?X4

X16=X8?X8

X32=X16?X16

X64=X32?X32

并且n可以表示为一定数量的2相加,若n为奇数,则减1后依然还是原问题。至此,这就可以当成是n的二进制表示转换成对应十进制的运算么?既然知道方向了,可得以下解法。

BigInteger quick_pow(BigInteger x, int N) {
//boundary condition
 if (x == 0) {
     return 0;
 }
 if (x == 1 || N == 0) {
     return 1;
 }

//store x,x^2,x^4...until x^2^logN
 int n = log2(N);
 BigInteger arr[n + 1];
 arr[0] = x;
 for (int i = 1; i <= n; i++) {
     arr[i] = arr[i - 1] * arr[i - 1];
 }
 BigInteger result = 1;
 int count = 0;
 while (N != 1) //Calculating result according to the binary representation of N.
 {
     switch (N % 2) {
     case 1:
     result *= arr[count];
     break;
     case 0:
     break;
 }
     count += 1;
     N >>= 2;
 }
     result *= arr[count];
     return result;
}

至此改进就完毕了,省去了一些隐性的开销,运行时间经过测试与改进版递归相差不大。代码写的不好,有待重写,若有更好的可以共享。

最后推荐一本书,书名《算法分析与设计基础》,从算法思想分类的角度主要介绍了算法的一些主要分析技术,挺新引,值得一看。

更快的求整数幂算法

时间: 2024-10-03 22:54:23

更快的求整数幂算法的相关文章

更快找到正确的机器学习算法

(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:机器学习无疑是现在一个高大上的热点,而且微软在Azure提供了机器学习的服务.那么如何更快找到正确的机器学习算法呢?微软也给大家提供了建议. 随着大数据的深入应用,机器学习也从学术界逐步走入到工业界,现在电子商务.社交.广告.智能硬件都有机器学习的身影.微软作为一家有着深厚科研实力的公司,不仅在Azure中提供了机器学习的服务供大家选用,还提供了一个名为Azure Machine Learni

算法笔记01--归纳法之整数幂

整数幂 算法1:对实数x的n次幂设计一个有效的算法.一种直接的方法是对x用迭代方法自乘n次,这种方法十分低效,因为它需要O(n)乘法.一个高效的方法可以用如下方法推出,令m=n/2,假设已经知道如何计算x^m.那么有两种情况:如果n是偶数,那么x^n = (x^m)^2:否则x^n = x(x^m)^2. 算法2:令n的二进制表示为dn-1.....d1,d0.从y=1开始,由n的高位至地位扫描,如果二进制数字为0,就对y平方:如果为1就对y平方并乘x.这就产生了递归算法EXPREC. 时间复杂

求幂算法

1.简单递归 最简单的求幂算法是根据xn=x*xn-1,使用递归: def foo(x,n): if n==0: return 1 else: return x*foo(x,n-1) 这样求x的n次方,会进行n-1次乘法运算,n较大时效率很低. 2.高效递归 一种更高效的算法,可以将运算次数降到LogN的级别,由于: xn=xn/2*xn/2 , n为偶数时 xn=x(n-1)/2*x(n-1)/2*x , n为奇数时 def foo(x,n): if n==0: return 1 else:

非递归快速求幂算法

快速求正整数次幂,当然不能直接死乘.举个例子:3 ^ 999 = 3 * 3 * 3 * … * 3直接乘要做998次乘法.但事实上可以这样做,先求出2^k次幂:3 ^ 2 = 3 * 33 ^ 4 = (3 ^ 2) * (3 ^ 2)3 ^ 8 = (3 ^ 4) * (3 ^ 4)3 ^ 16 = (3 ^ 8) * (3 ^ 8)3 ^ 32 = (3 ^ 16) * (3 ^ 16)3 ^ 64 = (3 ^ 32) * (3 ^ 32)3 ^ 128 = (3 ^ 64) * (3

西蒙算法在量子计算机上“跑”得更快

来自南非的一组研究团队近日成功地在量子计算机上运行了西蒙算法(Simon’s algorithm),且这一算法在量子计算机上的运行表现比电子计算机表现得更好.相关论文刊登在<物理评论快报>上. 量子计算机是一种基于量子力学的计算设备,用来存储数据的对象是量子比特而非经典计算机的电子比特. 科学界普遍认为,由于量子计算机能够利用量子纠缠和叠加等独特的量子物理属性,在运行某些特定算法时,它会比现有计算机更为快速和高效.遗憾的是,到目前为止,还没有办法在量子计算机上运行这些量子算法来检验这一理论.

随机素数测试(Miller_Rabin算法)和求整数素因子(Pollard_rho算法)

POJ1811 给一个大数,判断是否是素数,如果不是素数,打印出它的最小质因数 随机素数测试(Miller_Rabin算法) 求整数素因子(Pollard_rho算法) 科技题 1 #include<cstdlib> 2 #include<cstdio> 3 const int maxn=10005; 4 const int S=20; 5 int tot; 6 long long n; 7 long long factor[maxn]; 8 long long muti_mod(

hdu oj 1061 Rightmost Digit (快速幂算法)

这里首先要讲解一下快速幂算法: 快速幂取模算法 在网站上一直没有找到有关于快速幂算法的一个详细的描述和解释,这里,我给出快速幂算法的完整解释,用的是C语言,不同语言的读者只好换个位啦,毕竟读C的人较多~ 所谓的快速幂,实际上是快速幂取模的缩写,简单的说,就是快速的求一个幂式的模(余).在程序设计过程中,经常要去求一些大数对于某个数的余数,为了得到更快.计算范围更大的算法,产生了快速幂取模算法.[有读者反映在讲快速幂部分时有点含糊,所以在这里对本文进行了修改,作了更详细的补充,争取让更多的读者一目

各类求自然数幂和方法

高斯消元 我们知道: \[\sum_{i=1}^{n}i=\frac{n(n+1)}{2}\] 以及: \[\sum_{i=1}^{n}i^2=\frac{n(n+1)(2n+1)}{6}\] 以及: \[\sum_{i=1}^{n}i^3=(\sum_{i=1}^{n}i)^2=(\frac{n(n+1)}{2})^2\] 那我们可以猜想,自然数的\(k\)次幂和对应的公式是一个次数为\(k+1\)的没有常数项的多项式(实际上也是的). 证明吗,暂时不会... However,我们可以拿这个猜

北京大学Online Judge 之 “求高精度幂(ID1001)”解题报告

北京大学Online Judge 之 "求高精度幂(ID1001)"解题报告 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 题目描述: Description 对数值很大.精度很高的数进行高精度计算是一类十分常见的问题.比如,对国债进行计算就是属于这类问题. 现在要你解决的问题是:对一个实数R( 0.0 < R <99.999 ),要求写程序精确计算 R 的 n 次方(Rn),其中n 是整数并且 0 < n <