引入
求幂运算
求幂运算作为基本的运算方法,想必大家都不陌生,对于求m的n次方,大家肯定是信手拈来,比如我们就可以通过循环的方式来解决这个问题。
long long my_pow(const int base, const int index)
{
long long ret = 1;
int i = 0;
for (i = 0; i < index; i++)
{
ret *= base;
}
return ret;
}
答案毫无疑问的是对的
但是我们有一个疑问,如果指数非常大会出现什么现象呢?
如果指数为1000,这个循环会执行一千次、如果指数为100 0000,这个循环会执行100 0000次。那么这个算法的时间复杂度为,为了削减时间开支,我们不得不去寻找一个时间复杂度更小的算法,快速幂就是一个符合我们要求的算法。
正文
快速幂的思想
快速幂总体上就是运用二分的思想,将指数不断的二分,直到指数为0为止。
通过不断的二分,我们可以将运算次数显著的下降,时间复杂度仅为,即当指数为1024时只需要计算10次,指数为100 0000时只需要计算20次,指数越大,快速幂的优势就越明显。
快速幂的实现
根据以上思想,我们需要不断地判断index的奇偶和将index除2,为了进一步增加效率,我们可以用效率更高的位运算。
注:在c语言中整数除以整数的结果也为整数,所以当index为奇数时进行除2操作会导致数据损失,我们要自行将一个基数移至ret保存。
判断奇偶:
index&1可以帮我们判断index的二进制形式的最后一位为1还是0。如果为1,计算的结果就为1(真),就会进入if语句,将一个基数分给ret。反之(假)则跳过if语句。
除2操作:
当对数据进行右移操作时就等于将数据除2或2的倍数。
右移一位就除,右移两位则除
。
10(10)=(1010)(2)右移一位的结果为 (0101)(2)=5(10)。
10(10)=(1010)(2)右移两位的结果为 (0010)(2)=2(10)。
代码如下:
double Fast_Power(double base, int index)
{
//用来记录答案
double ret = 1.0;
//记录index是否为负数
//1为是,0为不是
int flag = 0;
if (index < 0)
{
//将index设为正数,方便计算
index = -index;
flag = 1;
}
while (index > 0)
{
//判断index是否为奇数
//如果为奇数则分一个基数给ret存贮
if (index & 1)
{
ret *= base;
}
//指数除以二
index >>= 1;
//基数平方
base *= base;
}
if (flag)
{
//flag为真则让ret取倒数,达到index为负数时的结果
ret = 1.0 / ret;
}
return ret;
}
ret | base | index | |
起始点 | 1 | 2 | 10 |
第一轮 | 1 | 4 | 5 |
第二轮 | 1*4 | 16 | 2 |
第三轮 | 1*4 | 256 | 1 |
第四轮 | 1*4*256 | 256*256 | 0 |
在实现基础功能的基础上,我们还可以增加当base为实数和index为负数的情况,增加代码的健壮度。
拓展
快速幂取模
由于幂的性质,导致求幂运算的结果很容易超出计算机的最大存贮范围。这时如果出现了题目让我们求一个这样的数的后几位,那该如何是好呢?
接下来我们引入一个结论:
(a * b) % p = (a % p * b % p) % p
我们可以借助这个结论来完成这个看似不可能的结论。为了方便观察我们选取一些较小的数据。
注:取模操作符的两边必须为整形
假设我们要求数据的后3位,
代码如下:
int Fast_Power(int base, int index)
{
//用来记录答案
int ret = 1;
//记录index是否为负数
//1为是,0为不是
int flag = 0;
if (index < 0)
{
//将index设为正数,方便计算
index = -index;
flag = 1;
}
while (index > 0)
{
//判断index是否为奇数
//如果为奇数则分一个基数给ret存贮
if (index & 1)
{
ret = ret * base % 1000;
}
//指数除以二
index >>= 1;
//基数平方
base = base * base % 1000;
}
if (flag)
{
//flag为真则让ret取倒数,达到index为负数时的结果
ret = 1.0 / ret;
}
return ret;
}
总结
快速幂是一个以二分为思想的快速求幂算法,有着很小的时间复杂度,具有实际价值,值得学习。
以上均为个人的看法与思考,如有思考不周或出现错误还行各位指正!