【算法】快速幂

引入

求幂运算

        求幂运算作为基本的运算方法,想必大家都不陌生,对于求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次。那么这个算法的时间复杂度为O(N),为了削减时间开支,我们不得不去寻找一个时间复杂度更小的算法,快速幂就是一个符合我们要求的算法。


正文

快速幂的思想

        快速幂总体上就是运用二分的思想,将指数不断的二分,直到指数为0为止。

2^{10}=2^{2^{5}}=4^{5}=4*4^{4}=4*4^{2^{2}}=4*16^{2}=4*16^{2^{1}}=4*256*16^{1}=4*256*16*16^{0}=4*256*16*1=1024

        通过不断的二分,我们可以将运算次数显著的下降,时间复杂度仅为O(\log_{2}N ),即当指数为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的倍数。

        右移一位就除2^{1},右移两位则除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;
}
retbaseindex
起始点1210
第一轮145
第二轮1*4162
第三轮1*42561
第四轮1*4*256256*2560

 

        在实现基础功能的基础上,我们还可以增加当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;
}


总结 

        快速幂是一个以二分为思想的快速求幂算法,有着很小的时间复杂度,具有实际价值,值得学习。

        以上均为个人的看法与思考,如有思考不周或出现错误还行各位指正!

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值