`
xitongyunwei
  • 浏览: 926831 次
文章分类
社区版块
存档分类
最新评论

非递归快速求幂算法

 
阅读更多

快速求正整数次幂,当然不能直接死乘。举个例子:

3 ^ 999 = 3 * 3 * 3 * … * 3

直接乘要做998次乘法。但事实上可以这样做,先求出2^k次幂:

3 ^ 2 = 3 * 3
3 ^ 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 ^ 64)
3 ^ 256 = (3 ^ 128) * (3 ^ 128)
3 ^ 512 = (3 ^ 256) * (3 ^ 256)

再相乘:

3 ^ 999
= 3 ^ (512 + 256 + 128 + 64 + 32 + 4 + 2 + 1)
= (3 ^ 512) * (3 ^ 256) * (3 ^ 128) * (3 ^ 64) * (3 ^ 32) * (3 ^ 4) * (3 ^ 2) * 3

这样只要做16次乘法。即使加上一些辅助的存储和运算,也比直接乘高效得多(尤其如果这里底数是成百上千位的大数字的话)。

我们发现,把999转为2进制数:1111100111,其各位就是要乘的数。这提示我们利用求二进制位的算法(其中mod是模运算):

REVERSE_BINARY(n)
1while(n> 0)
2dooutput (nmod 2)
3nn/ 2

这个算法给出正整数n的反向二制进位,如6就给出011(6的二进制表示为110)。事实上这个算法对任意的p进制数是通用的,只要把其中的2换成p就可以了。

如何把它改编为求幂运算?我们发现这个算法是从 低位向高位做的,而恰好我们求幂也想从低次幂向高次幂计算(参看前面的例子)。而且我们知道前面求出的每个2^k次幂只参与一次乘法运算,这就提示我们并 不把所有的中间结果保存下来,而是在计算出它们后就立即运算。于是,我们要做的就是把输出语句改为要做的乘法运算,并在n减少的同时不断地累积求2^k次 幂。

还是看算法吧:

POWER_INTEGER(x,n)
1pow← 1
2while(n> 0)
3doif(nmod 2 = 1)
4thenpowpow*x
5xx*x
6nn/ 2
7returnpow

不难看出这个算法与前面算法的关系。在第1步给出结果的初值1,在while循环内进行运算。3、4中的if语句就来自REVERSE_BINARY的输出语句,不过改成了如果是1则向pow中乘。5句则是不断地计算x的2^k次幂,如对前面的例子就是计算2^2、2^4、2^8、…、2^512。

应该指出,POWER_INTEGER比 前面分析的要再多做两次乘法,一次是向pow中第一次乘x,如2^1也要进行这个乘法;另一次则是在算法的最后,n除以2后该跳出循环,而前面一次x的自 乘就浪费掉了(也可以考虑改变循环模式优化掉它)。另外,每趟while循环都要进行一次除法和一次模运算,这多数情况下除法和模运算都比乘法慢许多,不 过好在我们往往可以用位运算来代替它。

相应的C++代码如下

NumberType pow_n(NumberType x, unsigned int n)
{
NumberType pw = 1;

while (n > 0) {
if ((pw % 2) == 1)
pw *= x;
x *= x;
n /= 2;

}

return pw;
}

进行简单的优化后则有:

NumberType optimized_pow_n(NumberType x, unsigned int n)
{
NumberType pw = 1;

while (n > 0) {
if (n & 1) // n & 1 等价于 (n % 2) == 1
pw *= x;
x *= x;
n >>= 1; // n >>= 1 等价于 n /= 2
}

return pw;
}

注1:快速求幂算法POWER_INTEGER常被写成递归的形式,算法实质完全相同,但却是无必要的。

注2:这个算法并不是做乘法数最少的,但多数情况下是足够快并且足够简单的。如果单纯追求做乘法数最少,则未必应该用2^k次幂进行计算。如果还允许做除法,则问题会进一步复杂化。

如:

x ^ 2 = x * x
x ^ 4 = (x ^ 2) * (x ^ 2)
x ^ 8 = (x ^ 4) * (x ^ 4)
x ^ 16 = (x ^ 8) * (x ^ 8)
x ^ 31 = (x ^ 16) * (x ^ 8) * (x ^ 4) * (x ^ 2) * x
要8次乘法。

x ^ 2 = x * x
x ^ 4 = (x ^ 2) * (x ^ 2)
x ^ 8 = (x ^ 4) * (x ^ 4)
x ^ 10 = (x ^ 8) * (x ^ 2)
x ^ 20 = (x ^ 10) * (x ^ 10)
x ^ 30 = (x ^ 20) * (x ^ 10)
x ^ 31 = (x ^ 30) * x
只要7次乘法。

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 ^ 31 = (x ^ 32) / x
只要6次乘或除法。

不过具体得出上述乘(除)法数更少的算法会变得相当复杂,在许多情况下时间收益还会得不偿失。因此往往并不实用。ACM Japan 2006中有一道题即要求计算最少乘法数,可参看:

http://acm.pku.edu.cn/JudgeOnline/problem?id=3134

分享到:
评论

相关推荐

    表达式求值与幂运算算法

    使用JS实现表达式求值与幂运算算法:1)求以a为底的n次幂递归与非递归实现;2)快速乘法递归与非递归实现;3)表达式求值计算

    非常好的快速幂基础项目资源,分享出来.zip

    快速幂 用java写的一些常用算法 如快速排序、归并排序、非递归的归并排序、堆排序等 后续还会加上其他常用算法 用java写的一些常用算法 如快速排序、归并排序、非递归的归并排序、堆排序等 后续还会加上其他常用算法...

    《妙趣横生的算法(C语言实现)》(杨峰 编著)

    7.13 递归函数的非递归求解 7.14 任意长度整数加法 第8章 数值计算问题 8.1 递推化梯形法求解定积分 8.2 求解低阶定积分 8.3 迭代法开平方运算 8.4 牛顿法解方程 8.5 欧拉方法求解微分方程 8.6 改进的欧拉方法求解...

    matlab中蝶形运算代码-fft_radix:FFT快速算法的MATLAB示例:可以提供C语言的实现思路

    第九章实现的FFT算法,包括五种FFT快速算法的递归实现和非递归实现。下面主要介绍递归的实现,非递归的代码参考网上(我也不记得在哪儿来的了),递归实现的函数简要介绍如下: fft_radix2t 是按时间抽选的基2-FFT...

    C/C++常用算法手册.秦姣华(有详细书签).rar

    “第2篇算法基本应用篇”详细讲解了算法在排序、查找、数值计算、数论、经典趣题和游戏中的应用;“第3篇算法高级应用篇”讲解了算法的一些高级应用技术,包括在密码学和数据压缩/解压缩中的应用。 《C/C++常用算法...

    一阶不确定系统的固定时间收敛扰动观测器

    针对一阶不确定系统中集总扰动的快速估计问题,基于误差放大策略和双极限齐次估计理论设计非递归形式的固定时间收敛扰动观测器,并提出利用幂次函数非线性特性削弱输入信号噪声影响的改进方案.误差放大策略是一种特殊...

    国家集训队2019论文集.zip

    我们只需要类似快速幂地倍增k,每次把多项式对S(x)取模。 x' mod s(x) 的次数不超过m-2,我们再由定义带入a的前m-1项求出G即可。 求两个次数O(m)的多项式取模结果在模域下可以做到O(mlog(m))-时间复东度(可 以参见...

    IOI国家集训队论文集1999-2019

    + [非完美算法](#非完美算法) + [提交答案题](#提交答案题) + [守恒思想](#守恒思想) + [极限法](#极限法) + [贪心](#贪心) + [压缩法](#压缩法) + [逆向思维](#逆向思维) + [穷举](#穷举) + [目标转换](#...

    C程序范例宝典(基础代码详解)

    实例211 求任意数n次幂 299 7.3 字符串、字符函数 300 实例212 函数实现字符匹配 300 实例213 任意大写字母转小写 301 实例214 字符串复制到指定空间 302 实例215 查找位置信息 303 7.4 其他函数 304 ...

    c语言经典案例

    实例122 求任意数的n次幂 161 实例123 固定格式输出当前时间 163 实例124 设计函数计算学生平均身高 164 实例125 求数组元素中的最小值 165 实例126 打印1~5的阶乘 166 实例127 求最大公约数和最小公倍数 167 实例...

    数据结构(C++)有关练习题

    利用堆栈类,将本题a和b的代码改成非递归的方式。 实验报告要求: 按要求写出完整的实验代码; <br>实验四 综合(课程设计) 内容及步骤: 1、假定一维数组a[n]中的每个元素值均在[0,200]区间内,用C++...

    Java范例开发大全 (源程序)

     实例183 求n的幂数与倍数 304  实例184 商品订单 306  实例185 多功能排序 310  第11章 Java常用类(教学视频:66分钟) 315  11.1 数学Math类 315  实例186 求圆周率∏值 315  实例187 求对数值 316 ...

    java范例开发大全(pdf&源码)

    实例183 求n的幂数与倍数 304 实例184 商品订单 306 实例185 多功能排序 310 第11章 Java常用类(教学视频:66分钟) 315 11.1 数学Math类 315 实例186 求圆周率∏值 315 实例187 求对数值 316 实例188 使用取整函数...

    java范例开发大全源代码

     实例183 求n的幂数与倍数 304  实例184 商品订单 306  实例185 多功能排序 310  第11章 Java常用类(教学视频:66分钟) 315  11.1 数学Math类 315  实例186 求圆周率∏值 315  实例187 求对...

    java范例开发大全

    实例183 求n的幂数与倍数 304 实例184 商品订单 306 实例185 多功能排序 310 第11章 Java常用类(教学视频:66分钟) 315 11.1 数学Math类 315 实例186 求圆周率∏值 315 实例187 求对数值 316 实例188 使用取整函数...

    Java范例开发大全(全书源程序)

    实例183 求n的幂数与倍数 304 实例184 商品订单 306 实例185 多功能排序 310 第11章 Java常用类(教学视频:66分钟) 315 11.1 数学Math类 315 实例186 求圆周率∏值 315 实例187 求对数值 316 实例188 使用...

Global site tag (gtag.js) - Google Analytics