题目描述
7.a.应用从左到右二进制幂算法来计算 a 17 a^{17} a17
本题目来源:《算法设计与分析基础》(美)莱维汀Leviti,A.)著;潘彦译.一3版.北京:清华大学出版社,2015 (2020. 12重印) 一书中第五章(分治法)课后习题6.5的第三题的a小问,对应原书P186(读者可自行对照)注:本章节(第六章)主要讲述的是变治法,并对霍纳法则的特性以及复杂度进行了一定的分析,从左到右的二进制幂算法是基于霍纳法则算法而演变出来的,下面对霍纳法则进行讨论。
一、二进制幂的计算
当使用霍纳法则来计算 a n a^{n} an时,霍纳法则(又称秦九韶算法)的高效率的优点就被抹掉了,在对 a n a^{n} an的计算时,无论如何提出公因子来进行计算,都无法避免退化成对a进行蛮力自乘的算法,因此,我们引入了二进制幂计算的算法。
不太懂霍纳法则的同志可戳这里百度百科:霍纳法则
这里以从左至右计算
a
13
a^{13}
a13为例(对应原书P185页例2),这里
n
=
13
=
110
1
2
n\,\,=\,\,13 =\,\,1101_2
n=13=11012
(将十进制转化为二进制),因此,我们有
n的二进制位 | 1 | 1 | 0 | 1 |
---|---|---|---|---|
累乘器 | a a a | a 2 × a = a 3 a^{2}\times a = a^{3} a2×a=a3 | ( a 3 ) 2 \left( a^3 \right) ^2 (a3)2 | ( a 6 ) × a \left( a^6 \right) \times a (a6)×a |
观察上述的算法,我们可以得到规律如下,用product表示累成器暂时的计算结果
p
r
o
d
u
c
t
i
=
{
p
r
o
d
u
c
t
i
=
(
p
r
o
d
u
c
t
i
−
1
)
2
×
a
,
n
的第
i
位二进制为
1
p
r
o
d
u
c
t
i
=
(
p
r
o
d
u
c
t
i
−
1
)
2
,
n
的第
i
位二进制为
0
(1)
product_i\,\,=\,\,\begin{cases} product_i\,\,=\,\,\left( product_{i-1} \right) ^2\times a\,\,, n\text{的第}i\text{位二进制为}1\\ product_i\,\,=\,\,\left( product_{i-1} \right) ^2\,\,, n\text{的第}i\text{位二进制为}0\\ \end{cases}\tag{1}
producti={producti=(producti−1)2×a,n的第i位二进制为1producti=(producti−1)2,n的第i位二进制为0(1)
这样子0101的间隔乘法其实最后会导致幂次的奇数和偶数的差异, 具体推导过程见原书第六章
即对应代码中的部分如下
for (int j = I-1; j >= 0; j--)
{//由于前面while存储循环的时候是从左->右对应低位->高位
//故这里的代码是从后往前计算
if (*(p + j) == 1)product = product * product * a;
else product = product * product;
}
二、题解
1.源代码
注:本代码实现过程仅仅使用int来对结果进行存储,对于一些较大幂次的计算可能不适用,读者可以自行定义能储存更多结果的数据结构或者使用高精度乘法,或者大数乘法来进行计算。
/*
Author: FeverTwice
Date: 2021 - 04 - 22
Function: 计算a的17次方
PS:Homework of Algorithm lesson, Chapter 6
*/
#include <iostream>
#include <cstdio>
#include <string>
#include <cmath>
using namespace std;
int dec2bin(int b)
{//十进制转二进制
int res = 0,j=1;
while (b>0){
res = res + j * (b % 2);
b /= 2;
j *= 10;
}
return res;
}
int leftBrinaryExp(int a, int b) {
int I = floor(log(b) / log(2));//计算b转化为二进制之后的位数,向下取整
int *p = new int[I+1]; //定义动态数据组
int b2 = dec2bin(b); //十进制转化为二进制
int i = 0;
while (b2>0)
{//将二进制数逐位分离
*(p + i) = b2 % 10;
b2 /= 10;
i++;
}
int product = a; //二进制的最高位一定为1,初始化product
for (int j = I-1; j >= 0; j--)
{//由于前面while存储循环的时候是从左->右对应低位->高位
//故这里的代码是从后往前计算
if (*(p + j) == 1)product = product * product * a;
else product = product * product;
}
return product;
}
int main()
{
cout << "请输入底数a与次数b" << endl;
int a, b;//a为底数,b为次数
cin >> a >> b;
if (b == 0)cout << 1 << endl;
else {
int res = leftBrinaryExp(a, b);
cout << res << endl; //输出结果
}
return 0;
}
2.源程序测试
注:本题源程序测试环境Visual Studio 2019
1.Test set 1
2.Test set 2
3.Test set 3
4.Test set 4
写在最后
各位看官,都看到这里了,麻烦动动手指头给博主来个点赞8,您的支持作者最大的创作动力哟! <(^-^)>
才疏学浅,若有纰漏,恳请斧正
本文章仅用于各位同志作为学习交流之用,不作任何商业用途,若涉及版权问题请速与作者联系,望悉知