如果n与m是正整数,那么m^n就是把m连乘n次,用到了n次乘法运算,这是一个很没有效率的算法,那么我们来进行一下改进。主要思想就是减少乘法的运算次数。
关键所在就是下面的定义式:
当n=0时 m^n=1
当n为偶数时 m^n=(m^k)^2
当n为奇数时 m^n=m*(m^2k)
所以在一个递归程序中分成3部分计算就可以了:第一部分看n是否为0;第二部分看n是否是偶数,如果是,就递归求m的n/2次方,算出的结果在平方就是答案了;第三部分看n是否为奇数,例如n=2k+1,m^(2k+1)=m*(m^2k),那么就递归去算m^2k,得出的结果在与m相乘就得到结果了。
每一次分成两个部分(m^k或m^2k)之后,都要用一个乘法把(m^k)(m^k)或m(m^2k)算出来,在最坏的情况下,就是n是奇数,m^n=m*(m^2k),不过,m^2k却是偶数了,下一层递归就会算m^k了,然后用(m^k)(m^k)再算m^2k。因此递归的层数最多就是1+logn层,也就是乘法运算的数目,在n慢慢变大的时候,很容易知道1+logn小于n,所以这个算法的效率比连乘n次要高很多。C语言的代码如下:
- unsigned long recursive_power(unsigned long m, unsigned long n)
- {
- unsigned long temp;
- if (n == 0)
- return 1;
- else if (n & 0x01UL == 0)
- {
- temp = recursive_power(m, n >> 1);
- return temp * temp;
- }
- else
- return m * recursive_power(m, n-1);
- }
测试程序如下面的代码所示:
- #include <stdio.h></stdio.h>
- #include <stdlib.h></stdlib.h>
- int main(void)
- {
- char line[100], *dummy_ptr;
- unsigned long m, n;
- printf("\nM^N Computation (M > 0 and N > 0)\n");
- printf("\nM = ");
- gets(line);
- m = strtoul(line, &dummy_ptr, 10);
- printf("\nN = ");
- gets(line);
- n = strtoul(line, &dummy_ptr, 10);
- printf("\n\nM^N = %lu", recursive_power(m, n));
- getchar();
- }
现在问题已经解决了,但是我们如果再多想一想的话,可不可以不用递归就把问题解决呢?
先以求m^45为例,45的二进制为101101,所以m^45=m^(1*2^5+0*2^4+1*2^3+1*2^2+0*2^1+1*2^0)=[m^(2^5)][m^(2^3)][m^(2^2)][m^(2^0)] 在101101中,值为1的那一位(比如第i位,从右往左为0,1,2,3 ...) ,在m的指数中就有m^(2^i)哪一项。正因为这个缘故,可以依次把m^(2^0)m^(2^1),... ,m^(2^i),m^(2^(i+1)),... 求出来,这正好对应着n的二进制表示中从右到左的顺序,如果在n的二进制表示中第i位是1,就把m^(2^i)乘到一个用来保存最终结果的变量中(初值为1),因此当n的每一位都查完之后,就有了答案。
如何计算m^(2^0),m^(2^1),... ,m^(2^i),m^(2^(i+1)),... 这些数据呢?看看下面的式子:
m^(2^(i+1))=m^((2^i)*2)=(m^(2^i))^2
在知道了m^(2^i)的前提下,把其平方就可以得到m^(2^(i+1))。但是m^(2^0)=m^1=m,所以求这些m^(2^i)只不过是求m的自乘而已,换句话说一开始值为m,下一次是m^2,再下次(m^2)(m^2)=m^4,接下去就是(m^4)(m^4)=m^8,... 然后就是看看如何求出n的二进制表示了。这倒是大家知道的常识了,把n用2去除的余数,就是最右面的一位,再用2去除上次得到的结果,商就是下一位了, ...
我们通过一个表格来看一下如何算m^45:
n m temp 说 明
----------------------------------------------------------------------------
45 m 1 初 值
22 m^2 m n为奇数,乘入temp
11 m^4 m n为偶数,temp不变
05 m^8 m^5 n为奇数,乘入temp
02 m^16 m^13 n为奇数,乘入temp
01 m^32 m^13 n为偶数,temp不变
00 m^64 m^45 n为奇数,乘入temp
------------------------------------------------------------------------------
C语言的代码如下:
- unsigned long iterative_power(unsigned long m, unsigned long n)
- {
- unsigned long temp = 1;
- while (n > 0)
- {
- if (n & 0x01UL == 1)
- temp *= m;
- m *= m;
- n >>= 1;
- }
- return temp;
- }
这个程序的while循环最多只会绕1+logn次,每绕一次,最多作两次乘法,一次m自乘,一次m乘入temp,所以最多只做2(1+logn)次乘法,所以速度是很快的,就以m^45为例,传统连乘方式要乘44次,但是利用此算法只乘了14次。
分享到:
相关推荐
其次,C语言中的算术运算符包括加(+), 减(-), 乘(*), 除(/)以及取模(%)。在数值算法中,正确理解和使用这些运算符是基础。需要注意的是,除法运算在处理整数时可能会导致结果被截断,而浮点数除法则会涉及到浮点数...
1. **基本操作**:包括变量定义、数据类型(如标量、向量、矩阵和数组)、输入输出以及基本算术运算(加、减、乘、除、指数、对数等)。 2. **矩阵与数组操作**:MATLAB以矩阵为基础,支持矩阵的创建、索引、转置、...
在标题和描述中提到的问题,即"当数值超过long位时的加减乘算法,表达式自动运算",主要涉及了大整数运算的处理方法。 首先,我们需要理解`long`类型的局限性。在Java中,`long`类型占用64位,可以表示的整数范围是...
C#中的`System.Numerics.Complex`类提供了对复数的支持,包括加、减、乘、除等基本操作,以及模长、共轭等特性。源代码可能包含复数的额外操作,如极坐标转换、复数函数(如欧拉公式)等。 2. **矩阵运算**:矩阵是...
进制转换的简单运算方法包括十进制转二进制整数部分除2取余,小数部分乘2取整等。 7 二进制转十进制 从二进制数求其十进制的值,逐位码权累加求和。例如,二进制数1011.101可以转换为十进制数11.625。 8 二到八或...
从提供的文件信息来看,这是一份关于C、C++数值计算程序的集合资料,该集合资料中包含了丰富的数值计算常用程序。然而,由于没有提供具体的数值计算程序实例或代码,我们无法直接对程序代码进行分析。不过,我们可以...
本文提出了一种预测乘用车轮胎不均匀磨损的数值技术。 通过3D图案轮胎模型的摩擦动态滚动分析,可以预测在车轮定位条件下随车速,外倾角和前束角产生的不均匀轮胎磨损。 通过油漆测试轮胎胎面表面磨损的方法来说明...
以上是关于"Java数值计算算法编程"的一些核心知识点,实际编程中还需要根据具体需求选择合适的库和算法,确保程序的高效性和准确性。在压缩包中的"Source"文件很可能包含了各种数值计算的示例代码,可供学习和参考。
在“这是用java开发的关于数值的程序”中,我们可以推测这个项目是专门为初学者设计的,旨在帮助他们理解如何在Java中处理和操作数值。 在Java中,数值类型主要分为两大类:基本类型(primitive types)和包装类型...
- **基本算术运算**:加(+)、减(-)、乘(*)、除(/)、指数(^)等。 - **数组与矩阵操作**:创建数组使用方括号 `[1, 2, 3]`;创建矩阵可以使用分号分隔行,如 `[1, 2; 3, 4]`。 - **函数调用**:如 `sin(x)` ...
通过对矩阵多次自乘,逐渐逼近最大或最小特征值。 7. **牛顿迭代法**:牛顿法是求解非线性方程根的一种迭代方法,利用函数的切线来逼近根。其速度快且收敛性好,但要求函数的导数信息。 8. **高斯-赛德尔迭代法**...
实验中,代码首先通过`size(A)`确定矩阵的维度,然后通过循环进行行交换和行倍乘操作,确保主对角线元素非零,并消除下方元素。最后,通过回代求得解。 2. 列主元消去法:这是一种优化的消元法,旨在减少计算过程中...
在这个类库中,开发者可以找到用于执行基本算术运算(如加、减、乘、除)以及更复杂的函数(如指数、对数、平方根)的函数。 2. 矩阵运算:矩阵是线性代数的基础,广泛应用于工程、物理、经济等领域。类库提供了...
根据给定内容,我们可以清晰地看到每个颜色环所对应的数值含义: - **黑色**:代表0,乘数为X10^0。 - **棕色**:代表1,乘数为X10^1,误差率为±1%,温度系数为100ppm/°C。 - **红色**:代表2,乘数为X10^2,误差...
1. **基本数学运算**:可能包括加、减、乘、除、取余等基本算术操作的封装,方便开发者调用。 2. **数值比较**:可能提供了大于、小于、等于等比较操作的函数,便于进行条件判断。 3. **数学函数**:可能包含对幂...
C++提供了`<complex>`库支持复数操作,包括加、减、乘、除、共轭等基本运算,以及复数的模、幅角和极坐标表示。 3. **矩阵特征值与特征向量的计算**:在量子力学、控制理论等学科中,理解一个矩阵的特征值和特征...
VB提供了复数类型,可以进行复数的加、减、乘、除及开方等操作。 8. **随机数生成**:在模拟和统计计算中,需要生成随机数。VB提供了Randomize和Rnd函数,可以生成0到1之间的伪随机数。 9. **数值优化**:如梯度...
矩阵自乘`p`次。 - 如果`p`不是整数,则可能涉及到特征值和特征向量。 除了上述基础运算,MATLAB还支持更复杂的运算,如矩阵的转置、逆、行列式、秩、特征值、特征向量等,以及各种数值解法,如解线性方程组的`\`...
例如,如果的相对误差为,那么的误差可以通过乘以来求得。同时,我们还学习了如何计算近似值的误差限,这对于评估计算精度至关重要。 2. 条件数:函数的条件数反映了输入变化对输出影响的敏感程度。例如,如果函数...