快速幂与快速乘 (●´ω`●)ゞ

快速幂与快速乘 (●´ω`●)ゞ

前一篇文章幺半群中介绍了幺半群的概念,本文中我来讲讲一个比较基础的应用——快速幂。

问题定义

给定幺半群G中任意元素a和一个非负整数k,求a的k次幂

a的k次幂定义为\[a^k = 
\begin{cases}
e&{k=0}\\

\underbrace{a\cdots a}_{k}&{k\geq 1}\\
\end{cases}
\],(e是单位元喔



先讲一讲这个大家都会的栗子吧:

考虑幺半群\langle \mathbb{Z}^+,* \rangle\mathbb{Z}^+是正整数,*是乘法)

这显然是个幺半群对吧233(显然封闭,显然满足结合律,幺元为1,done!)

首先我们处理k = 0的case(显然直接return 1就行了233

现在我们假设k>0

因为任意正整数k我们都可以拆成至多O(\log(k))个二进制下的1,所以我们可以计算出a^{2^0},a^{2^1},a^{2^2}\ldots(通过结合律递推)然后使用O(\log(k))次乘法合并这些幂次(因为满足结合律

//鉴于有同学表示不会快速幂,我来解释一下

首先a^{2^0} = a, 后面的每一项都是前面那项的平方

这个就是普通快速幂的原理啦(相信大家都会吧:)

附代码:

ll fastExp(ll a,ll k){
	ll ans = 1;
	while(k){
		if(k%2==1){
			k--; 
			ans = ans * a;
		}else{
			a = a * a;
			k/=2;
		}
	}
	return ans;
}

我们看一下在这个过程中我们用到了什么性质

  1. “乘法”满足封闭性
  2. “乘法”满足结合律
  3. “乘法”下有单位元(其实这条不一定要,因为可以初始化ans到a(不过anyway我们假设一开始是单位元吧

看这不就是幺半群的要求嘛233

那么我们现在可以把之前正整数上的乘法魔改成任意幺半群上的“乘法”

应用

比如整数与加法构成的群\langle\mathbb{Z},+\rangle

学习Rabin-Miller算法的时候我们为了防止求a*b \mod{m}的时候溢出,通常会使用一种叫做“快速乘”的算法。不得不承认我第一次见到这东西的时候确实是一脸懵逼的,下面我们来用之前的知识学习一下这个算法。

(请先忘记a*b = b*a

现在我们的任务是:

用O(log(b))次加法计算a*b%mod(因为直接计算a*b会溢出,而我们保证加法不会溢出)

例如:1e17*1e17 \mod 1e18

注意到一个小学生级别的事实:a*b = \underbrace{a+\cdots+a}_b

所以我们可以把a*b看成a的b次幂

现在我们check一下这个新的“幂运算”是否满足上面提到的3个条件

  1. a+a \in \mathbb{Z}封闭性显然满足
  2. (a+b)+c = a+(b+c)结合律满足
  3. 单位元为0

全部都满足呢!Y(^o^)Y

所以我们把上面的代码中的乘法改成加法就大功告成啦(当然单位元也是要改的233)!

ll fastMultiplication(ll a,ll b,ll mod){
	ll ans = 0;
	while(b){
		if(b%2==1){
			b--; 
			ans = ans + a;
                        ans %= mod;
		}else{
                        b /= 2;
			a = a + a;
                        a %= mod;
		}
	}
	return ans;
}
这就是快速乘算法啦!

总结

从这个例子中可以看出,不同的代数结构常常会有类似的性质(比如结合律,含单位元等等),于是我们就可以从这些相似的性质入手,而不是关注它方方面面(比如计算a*b的时候我们并没有利用交换律这条性质),这样得到的推论可能覆盖面更广,更普适。抽象の力量赛高!好啦这篇就写到这,下次写一写陪集和拉格朗日定理吧O(∩_∩)O。

P.S.

知道置换群的朋友可能知道所有长度为n的置换构成了一个群,可以撕烤一下怎么算一个置换的k次幂。

编辑于 2017-01-12