`
hao3100590
  • 浏览: 128800 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

求幂的递归和非递归

阅读更多

本文的非递归部分转载自:http://www.cnblogs.com/wallace/archive/2009/12/27/1633683.html

先上算法

1.递归算法

 

//幂运算的递归算法
long pow(long x, int n){
	if(n == 0) return 1;
	if(n == 1) return x;
	if(n % 2 == 0){
		return pow(x*x, n/2);
	}else{
		return pow(x*x, n/2)*x;
	}
}

 

注意:在算法分析中还说明了:

用:

 

return pow(pow(x, 2), n/2);
return pow(pow(x, n/2), 2);
return pow(x, n/2)*pow(x, n/2)

 都是不行的,具体原因参考算法分析,主要就是明白,我们的目的是降次和分解

 

 

2.非递归算法

 

//幂运算的非递归运算,由低次幂逐渐升级即:x, x^2, x^4, x^8, ...
long pow2(long x, int n){
	long pw = 1;
  while (n > 0) {
  	//奇数二进制最后一位必定是1。如果是奇数,则乘以x(下面x在不断增大,到最后必定n=1,即奇数)
        if (n & 1)        // n & 1 等价于 (n % 2) == 1
            pw *= x;
        x *= x;
        n >>= 1;        // n >>= 1 等价于 n /= 2
  }
  return pw;
}

 里面都是用了位运算,这个能提高效率,需要掌握!具体的过程分析见下面

 

 

 

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

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)
while (n > 0)
2     do output (n mod 2)
3       n ← n / 2

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

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

还是看算法吧:

POWER_INTEGER(xn)
pow ← 1
while (n > 0)
3     do if (n mod 2 = 1)
4            then pow ← pow * x
5       x ← x * x
6       n ← n / 2
return pow

不难看出这个算法与前面算法的关系。在第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

 

zz from:http://www.cnblogs.com/wallace/archive/2009/12/27/1633683.html

 

分享到:
评论
1 楼 liuzhiqiangruc 2012-06-03  
恩,不错,很好啊

相关推荐

    c语言非递归快速幂demo

    c语言非递归快速幂demo

    表达式求值与幂运算算法

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

    Python递归与非递归算法的例子,七个练习

    创建一个函数 power来为任意数字做幂运算n ** i #递归 def power(n,i): if i==1: return n return n*power(n,i-1) print(power(2,4)) 练习2 创建一一个函数,用来检查一个任意的字符串是否是回文字符串 ,如果...

    Non-recursive CIC Filters with Integer Multiple Factors.pdf.pdf

    这篇文章讲述了CIC基本原理,并将CIC转换为非递归结构形式,对于非2的幂次倍抽取率,比如,24、40、180等非2次幂倍抽取,将抽取倍数分解为2P3K4M5T7R等等形式,每级为2/3/4/5/7/11/...等等倍抽取,这样将CIC设计难度...

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

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

    乘幂法求特征值matlab代码-OneSpectralClustering:使用逆幂方法对非线性特征问题进行基于图的聚类

    乘幂法求特征值matlab代码1-光谱聚类 该软件包包含使用逆幂方法 (IPM)来解决非线性特征问题的1 谱聚类的 Matlab 实现。 给定一个带有权重矩阵 W 的图,目标是找到图的分区,使给定的最优性标准最小化。 当前支持的...

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

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

    组合数学及其算法

    11.4 求最优树的破圈法和统观法 11.5 二分图中最大匹配与最佳匹配的算法 11.6 fleury算法 11.7 中国邮路问题及其算法 11.8 深度优先搜索法--dfs算法 11.9 项目网络与关键路径法 11.10 网络最大流算法 ...

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

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

    leetcode算法题主函数如何写-HangDian-OJ:杭电OJ刷题进度跟踪算法(c\c++\python)

    leetcode算法题主函数如何写 一.杭电OJ刷题分类 主流算法 1.搜索(回溯) 2.DP(动态规划)3....4.图论(Dijkstra、最小生成树、网络流) 5.数论 6.计算几何 7.组合数学 ...手把手撕LeetCode题目,扒各种...非递归改递归 B1

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

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

    程序员的SQL金典6-8

     5.1.23 求幂  5.2 字符串函数  5.2.1 计算字符串长度  5.2.2 字符串转换为小写  5.2.3 字符串转换为大写  5.2.4 截去字符串左侧空格  5.2.5 截去字符串右侧空格  5.2.6 截去字符串两侧的空格  5.2.7 取子...

    程序员的SQL金典7-8

     5.1.23 求幂  5.2 字符串函数  5.2.1 计算字符串长度  5.2.2 字符串转换为小写  5.2.3 字符串转换为大写  5.2.4 截去字符串左侧空格  5.2.5 截去字符串右侧空格  5.2.6 截去字符串两侧的空格  5.2.7 取子...

    亨氏函数及其在物理学中的一些应用

    对于这些方程,只要编写了幂级数解,就可以在三个或四个不同的值之间找到一个,而不是级数之间的双向递归关系。 也无法获得使用更简单函数的积分变换解决方案。 此方程在物理和数学文献中的使用在随后几年激增,与...

    程序员的SQL金典3-8

     5.1.23 求幂  5.2 字符串函数  5.2.1 计算字符串长度  5.2.2 字符串转换为小写  5.2.3 字符串转换为大写  5.2.4 截去字符串左侧空格  5.2.5 截去字符串右侧空格  5.2.6 截去字符串两侧的空格  5.2.7 取子...

    程序员的SQL金典4-8

     5.1.23 求幂  5.2 字符串函数  5.2.1 计算字符串长度  5.2.2 字符串转换为小写  5.2.3 字符串转换为大写  5.2.4 截去字符串左侧空格  5.2.5 截去字符串右侧空格  5.2.6 截去字符串两侧的空格  5.2.7 取子...

    程序员的SQL金典.rar

     5.1.23 求幂  5.2 字符串函数  5.2.1 计算字符串长度  5.2.2 字符串转换为小写  5.2.3 字符串转换为大写  5.2.4 截去字符串左侧空格  5.2.5 截去字符串右侧空格  5.2.6 截去字符串两侧的空格  5.2.7 取子...

    算法心得:高效算法的奥秘(原书第2版).[美]Henry S.Warren,Jr(带详细书签).pdf

    16.5 非递归的曲线生成算法 321 16.6 其他空间填充曲线 321 16.7 应用 322 16.8 习题 324 第17章 浮点数 325 17.1 IEEE格式 325 17.2 整数与浮点数互化 327 17.3 使用整数操作比较浮点数大小 331 17.4 估算...

    国家集训队2019论文集.zip

    1.4.1求向量列和矩阵列的最短递推式 考虑如何求出n维行向量列{o,1,12…}的线性递推式。假设考虑在模p意义下随机 个n维列向量ν,转而计算{toν,t1v,l2v…}这个标量序列的最短线性递推式。 由 Schwartz-zippel引理...

    C#写的表达式解析器,同时支持一元操作符和二元操作符,可自定义操作符,同时能设置表达式中的变量

    C#写的表达式解析器,支持多种操作符 如加减乘除幂模,同时还支持正负、三角函数,随机值等函数,可以支持自己扩展操作符,同时能支持...表达式使用的是逆波兰式(中缀表达式转换成的后缀表达式),非递归实现,执行效率非常高.

Global site tag (gtag.js) - Google Analytics