首页 > 其他 > 详细

【学习笔记 1】快速幂

时间:2020-05-06 15:01:58      阅读:72      评论:0      收藏:0      [点我收藏+]

写在最前面:

这一寒假,因为假期的延长,学习了一下新的算法和数据结构。可能有些完全掌握了,但是有些确实有些不熟练。

在假期的最后一段时间,我会将在寒假及之前学习的一些知识逐渐整理为笔记,供自己复习巩固,也供他人学习了解。笔记的顺序按照我学习该知识点的时间排序。

笔记大部分会写的很详细,面向初学者还是很好懂的。虽然我的语文不好,但我会尽量用我的最好水平来写。

可能一个学 OI 还不到半年的蒟蒻的笔记确实没有什么看的价值,但我还是希望和大家一起慢慢变强。OI 的路还很长,一步一步走下去吧。

希望在某一天开学后,没有人后悔自己一整个寒假碌碌无为。


1.快速幂

目录:

  1. 什么是快速幂

  2. 快速幂的原理

  3. 快速幂的代码实现

  4. 快速幂的位运算优化


什么是快速幂?

快速幂是一种快速求幂的算法,面对大量数据时就会展现出它的优势。

朴素的求幂算法是用一个循环逐渐累乘,时间复杂度为 \(\Theta(n)\)\(n\) 为指数。代码如下:

#define mod 10000007
long long power(long long base,long long n)
{
	long long res=1;
	for(register long long i=1;i<=n;++i)
		res*=base,res%=mod;
	return res;
}

但是在处理正整数次幂时,可以使用一种时间复杂度为 \(\Theta(\log_2n)\) 的算法,就是我们要用的快速幂。


快速幂的原理

快速幂的思想是将指数拆分为若干个 \(2\) 次幂之和,然后分别进行计算。

为什么这样更快呢?我们想一下,如果在循环中乘上本身而不是乘上底数,即可得到若干的 \(2\) 次幂,求 \(2^i\) 次幂的时间复杂度就是 \(\Theta(i)\),而朴素的算法为 \(\Theta(2^i)\)

那么怎么将十进制转换为二进制?不会的人请看下面。

我们举一个例子,我们要求 \(a\)\(11\) 次幂。

我们将 \(11\) 转换为二进制数:

\[(11)_{10}=(1011)_2 \]

我们发现二进制中,数字为 \(1\) 的数位为第 \(0\) 位、第 \(1\) 位和第 \(3\) 位,分别对应 \(2^0,2^1,2^3\),而 \(11\) 正好等于 \(2^0+2^1+2^3\)

其实上述做法就是十进制转二进制最常用的方法:按权展开。

因此,并根据同底数幂乘法原理:

\[a^{11}=a^{2^0+2^1+2^3}=a^{2^0}\times a^{2^1}\times a^{2^3} \]

就可以实现用若干 \(2\) 次幂来表示指数再进行计算了。


快速幂的代码实现

如何用代码实现呢?一般有循环和递归两种。

while 循环实现

一般常用此方法,因为循环效率比递归高,个人也较喜欢此方法。下面的详细解释也以此方法为例。

在 while 循环中,指数不为 \(0\) 为循环条件,每次循环中将底数扩方,相应的指数除以 \(2\)。在遇到奇数时,代表一个二次幂分解完,将答案乘上当前的底数。

代码:

#define mod 10000007
long long qpower(long long base,long long n)  //base 为底数,n 为指数。
{
	long long res=1;  //res 为幂。
	while(n>0)
	{
		if(n%2==1)
			res=res*base%mod;
		base=base*base%mod;
		n/=2;
	}
	return res;
}

结合 Dev-c++ 的调试功能我们可以更清楚的了解实现的过程,我们以计算 \(2^5\) 为例:

进入循环,初始赋值:

技术分享图片

此时 \(n\) 为奇数,更新 res。同时更改 base(底数)、n(指数):

技术分享图片

然后的 \(n\) 为偶数,res 不作操作,其他同上。

技术分享图片

这是就是最后一次了,\(n\)\(1\),最后一次更新。

技术分享图片

然后 \(n\) 变成了 \(0\),跳出循环,此时 res 为 \(32\)

递归实现:

递归实现的思路其实和循环是一样的,我在这里就简单的贴一个代码:

#define mod 10000007
long long qpower(long long base,long long n)
{
    if(n==0) 
		return 1;
    if(n==1) 
		return base;
    long long res=qpower(base,n/2)%mod;
    res=res*res%mod;
    if(n%2==1) 
        res=res*base%mod;
    return res;
}

快速幂的位运算优化

前置知识:位运算基础。

我们知道,计算机有一个很神奇的运算,叫做位运算,它比普通运算快许多,那在快速幂中能不能用位运算优化呢?

肯定是能的。

首先 >> 表示右移,a>>i 表示将 \(a\) 在二进制下右移 \(i\) 位,低位丢弃。那么不难想到,n/2 就可以用 n>>1 来代替。

那么在判定奇偶性时怎么用位运算优化呢?首先,我们知道,如果一个数是奇数,那么它在二进制下,末位一定是 \(1\)。根据这个特点,我们可以用按位与 &这种运算来代替。

我们知道,\(1\) 的二进制数是 \(0000\ldots 0001\),那么将它和一个奇数按位与,结果肯定是 \(0000\ldots 0001\),转为十进制就是 \(1\),偶数则为 \(0\)。因此,代码中的 n%2==1 可以改为 n&1

优化过的代码:

#define mod 10000007
long long qpower(long long base,long long n)
{
	long long res=1;
	while(n>0)
	{
		if(n&1)
			res=res*base%mod;
		base=base*base%mod;
		n>>=1;
	}
	return res;
}

后记

这是第一篇笔记,可能有很多不足,我会逐渐改进。希望能越来越好吧。

【学习笔记 1】快速幂

原文:https://www.cnblogs.com/win10crz/p/12835715.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!