眩しさだけは、忘れなかった。|

【算法设计-分治】快速幂与龟速乘、矩阵乘与矩阵快速幂

1. 快速幂

算法原理:

  • 计算311
    • 311=(35)2×3
    • 35=(32)2×3
    • 32=3×3
    • 仅需计算 3 次,而非 11 次
  • 计算 310
    • 310=(35)2
    • 35=(32)2×3
    • 32=3×3
    • 仅需计算 3 次,而非 10 次

算法思路:

  • 若指数是偶数,则将底数平方,指数除以 2。
  • 若指数是奇数,则将底数平方,指数除以 2,再乘上底数。

算法代码:

typedef unsigned long long uLL;

// 快速幂 a^b
uLL power (uLL a, uLL b){
	uLL r = 1;
	while (b != 0){
		if (b & 1) 	// (b % 2 == 1)
			r = r * a;
		b = b >> 1; // (b = b / 2)
		a = a * a;
	}
	return r;
}

举例:

  • 初始值:a = 3,b = 11
  • 第 1 轮:(11 % 2 == 1)r=1x3=3,b=5,a=32=9
  • 第 2 轮:(5 % 2 == 1)r=3x32=33=27,b=2,a=(32)2=34=81
  • 第 3 轮:(2 % 2 == 0)r 不变,b=1,a=(34)2=38
  • 第 4 轮:(1 % 2 == 1)r=33x38=311,b=0,a=(38)2=316
  • 得到 r = 33×38 = 311

2. 龟速乘

算法原理:将其中一个乘数分解成 2 的幂次相加。

12×a=23×a+21×a

算法代码:

typedef unsigned long long uLL;

// 龟速乘 a*b
uLL mul (uLL a, uLL b){
	uLL r = 0;
	while (b != 0){
		if (b & 1) 	// (b % 2 == 1)
			r = r + a;
		b = b >> 1; // (b = b / 2)
		a = a + a;
	}
	return r;
}

3. 快速幂取模

初等数论中有如下公式:

(a × b) % m = ((a % m) × (b % m)) % m

推广:

(a × b × c...) % m = ((a % m) × (b % m) × (c % m) × ... ) % m

(a^b) % m = (a × a × a...) % m = ((a % m) × (a % m) × (a % m) × ... ) % m

算法代码:

typedef unsigned long long uLL;

// 快速幂取模 (a^b) % p
uLL powerMod (uLL a, uLL b, uLL p){
	uLL r = 1;
	while (b != 0){
		if (b & 1) 	// (b % 2 == 1)
			r = (r * a) % p;
		b = b >> 1; // (b = b / 2)
		a = (a * a) % p;
	}
	return r;
}

4. 龟速乘取模

算法原理:(a × b) % m = ((a % m) × (b % m)) % m

算法代码:

// 龟速乘取模 (a*b) % p
uLL mulMod (uLL a, uLL b, uLL p){
	uLL r = 0;
	while (b != 0){
		if (b & 1) 	// (b % 2 == 1)
			r = (r + a) % p;
		b = b >> 1; // (b = b / 2)
		a = (a + a) % p;
	}
	return r;
}

5. 快速幂取模优化

算法原理:注意到快速幂取模算法中的相乘操作可能会超出数据范围,因此可以将相乘操作转化为龟速乘取模。

原理依然是此公式:(a × b) % m = ((a % m) × (b % m)) % m,其中((a % m) × (b % m))即为龟速乘取模。

算法思路:快速幂 + 龟速乘结合。

// 快速幂取模防止爆炸 (a^b) % p
uLL powerModBig (uLL a, uLL b, uLL p){
	uLL r = 1;
	while (b != 0){
		if (b & 1) 	// (b % 2 == 1)
			r = mulMod(a, b, p) % p;
		b = b >> 1; // (b = b / 2)
		a = mulMod(a, a, p) % p;
	}
	return r;
}

6. 矩阵乘

#include <cstdio>
#include <iostream>
using namespace std;

#define MAX 100

struct Martix{
	int row;	// 行 
	int col;	// 列 
	int martix[MAX][MAX];
	Martix (int r, int c): row(r), col(c) {} // 构造函数,此时不能使用typedef! 
};

Martix Multiply (Martix x, Martix y){
	Martix z(x.row, y.col);
	
	for (int i = 0; i < z.row; i++){
		for (int j = 0; j < z.col; j++){
			z.martix[i][j] = 0;
			for (int k = 0; k < x.col; k++)
				z.martix[i][j] += x.martix[i][k] * y.martix[k][j];
		}
	}
			
	return z;
}

int main(){
	int r, c;
	
	printf("请输入第一个矩阵的行和列:\n");
	scanf("%d %d", &r, &c);
	Martix x(r, c);
	printf("请输入%d行%d列的矩阵:\n", r, c);
	for (int i = 0; i < x.row; i++)
		for (int j = 0; j < x.col; j++)
			scanf("%d", &x.martix[i][j]);
	
	printf("请输入第二个矩阵的行和列:\n");
	scanf("%d %d", &r, &c);
	Martix y(r, c);
	printf("请输入%d行%d列的矩阵:\n", r, c);
	for (int i = 0; i < y.row; i++)
		for (int j = 0; j < y.col; j++)
			scanf("%d", &y.martix[i][j]);
	
	Martix result = Multiply(x, y);
	
	printf("结果是:\n");
	for (int i = 0; i < result.row; i++){
		for (int j = 0; j < result.col; j++)
			printf("%d ", result.martix[i][j]);
		printf("\n");
	}
	
	return 0;
}

7. 矩阵快速幂

#include <cstdio>
#include <iostream>
using namespace std;

#define MAX 100

struct Martix{
	int row;	// 行 
	int col;	// 列 
	int martix[MAX][MAX];
	Martix (int r, int c): row(r), col(c) {} // 构造函数 
};

Martix Multiply (Martix x, Martix y){
	Martix z(x.row, y.col);
	
	for (int i = 0; i < z.row; i++){
		for (int j = 0; j < z.col; j++){
			z.martix[i][j] = 0;
			for (int k = 0; k < x.col; k++)
				z.martix[i][j] += x.martix[i][k] * y.martix[k][j];
		}
	}
			
	return z;
}

Martix Power (Martix x, int k){
	Martix r(x.row, x.col);
	
	// 单位矩阵构建 
	for (int i = 0; i < r.row; i++){
		for (int j = 0; j < r.col; j++){
			if (i == j)
				r.martix[i][j] = 1;
			else
				r.martix[i][j] = 0;
		}
	}
	
	// 矩阵快速幂 
	while (k != 0){
		if (k & 1)
			r = Multiply(x, r);
		k = k >> 1;
		x = Multiply(x, x);
	}
	
	return r;
}

int main(){
	int r, k;
	
	printf("请输入行(列):\n");
	scanf("%d", &r);
	Martix x(r, r);
	printf("请输入%d行%d列的矩阵:\n", r, r);
	for (int i = 0; i < x.row; i++)
		for (int j = 0; j < x.col; j++)
			scanf("%d", &x.martix[i][j]);
			
	printf("请输入指数k:\n");
	scanf("%d", &k);
	
	Martix result = Power(x, k);
	
	printf("结果是:\n");
	for (int i = 0; i < result.row; i++){
		for (int j = 0; j < result.col; j++)
			printf("%d ", result.martix[i][j]);
		printf("\n");
	}
	
	return 0;
}

8. 矩阵快速幂用于递推式

斐波那契数列

数列递推式:fn=fn1+fn2

矩阵递推式:[fnfn1]=[1110]×[fn1fn2]

矩阵公式:[fn+1fnfnfn1]=[1110]n

#include <cstdio>
#include <iostream>
using namespace std;

#define MAX 100

struct Martix{
	int row;	// 行 
	int col;	// 列 
	int martix[MAX][MAX];
	Martix (int r, int c): row(r), col(c) {} // 构造函数 
};

Martix Multiply (Martix x, Martix y){
	Martix z(x.row, y.col);
	
	for (int i = 0; i < z.row; i++){
		for (int j = 0; j < z.col; j++){
			z.martix[i][j] = 0;
			for (int k = 0; k < x.col; k++)
				z.martix[i][j] += x.martix[i][k] * y.martix[k][j];
		}
	}
			
	return z;
}

Martix Power (Martix x, int k){
	Martix r(x.row, x.col);
	
	// 单位矩阵构建 
	for (int i = 0; i < r.row; i++){
		for (int j = 0; j < r.col; j++){
			if (i == j)
				r.martix[i][j] = 1;
			else
				r.martix[i][j] = 0;
		}
	}
	
	// 矩阵快速幂 
	while (k != 0){
		if (k & 1)
			r = Multiply(x, r);
		k = k >> 1;
		x = Multiply(x, x);
	}
	
	return r;
}

int main(){
	int k;
		
	while (scanf("%d", &k) != EOF){	
		// 斐波那契数列的递推矩阵构建 
		// [1, 1]
		// [1, 0]
		Martix e(2, 2);
		e.martix[0][0] = 1; e.martix[0][1] = 1;
		e.martix[1][0] = 1; e.martix[1][1] = 0;
		
		Martix result = Power(e, k);
		
		for (int i = 0; i < result.row; i++){
			for (int j = 0; j < result.col; j++)
				printf("%d ", result.martix[i][j]);
			printf("\n");
		}
	}
		
	return 0;
}

输入和输出:

1
1 1
1 0
2
2 1
1 1
3
3 2
2 1
4
5 3
3 2
5
8 5
5 3
6
13 8
8 5
7
21 13
13 8
8
34 21
21 13
9
55 34
34 21
10
89 55
55 34

相关题目:(POJ3070)斐波那契数列f(n),输入n,求f(n) % 10000,n <= 1e9

解题思路:仅需在快速幂函数中添加% 10000即可。

递推式的一些套路

(1)数列递推式:fn=a×fn1+b×fn2+c

矩阵递推式:[fnfn1c]=[ab1100001]×[fn1fn2c]

(2)数列递推式:fn=a×fn1+b×fn2+cn

矩阵递推式:[fnfn1cn]=[abc10000c]×[fn1fn2cn1]

(3)数列递推式:fn=cnfn1

矩阵递推式:[fncn]=[1c0c]×[fn1cn1]

参考资料

基础算法—快速幂详解

矩阵快速幂(原理+模板)

KY21 递推数列

本文作者:漫舞八月(Mount256)

本文链接:https://www.cnblogs.com/Mount256/p/17159018.html

版权声明:本作品采用CC 4.0 BY-SA许可协议进行许可。

posted @   漫舞八月(Mount256)  阅读(65)  评论(0编辑  收藏  举报
历史上的今天:
2022-02-27 STM8驱动SPI接口OLED
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开
  1. 1 Main Menu Theme Syd Matters
  2. 2 Luminous Memory (Acyanxi Remix) Acyanxi
  3. 3 夏影 麻枝准
  4. 4 潮騒の香り 水月陵
  5. 5 stand still 井口裕香 (いぐち ゆか)
  6. 6 流星雨 麻枝准
  7. 7 Summer Fantasy 傅许
  8. 8 失う 米白
  9. 9 epilogue 霜月はるか
  10. 10 夏に君を待ちながら 小原好美
  11. 11 桜のような恋でした 鹿乃 (かの)
  12. 12 風は微かに、熱を残し… 水月陵
  13. 13 夏凪ぎ 麻枝准/やなぎなぎ
  14. 14 空に光る 戸越まごめ
  15. 15 木漏れ日 riya
  16. 16 Songbirds Homecomings (ホームカミングス)
  17. 17 宝物になった日 麻枝准/やなぎなぎ
  18. 18 夏影~あの飛行機雲を超えた、その先へ~ 雪桜草 (雪樱草)
  19. 19 快晴 Orangestar (蜜柑星P),初音未来 (初音ミク)
  20. 20 永遠 霜月はるか
  21. 21 Sion 天門
  22. 22 遙かな年月-piano- 麻枝准
  23. 23 夏恋慕 kobasolo/春茶
  24. 24 夏凪ぎ-piano ver.- MANYO/麻枝准
  25. 25 Goodbye Seven Seas -piano ver.- MANYO/麻枝准
  26. 26 Light Years 麻枝准/やなぎなぎ
  27. 27 優しさの記憶 鹿乃 (かの)
stand still - 井口裕香 (いぐち ゆか)
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

stand still - 井口裕香 (いぐち ゆか)

词:渡辺翔

曲:渡辺翔

泣いたってもうなにも戻ってこないって

何度言っても私の心 理解しない

描いた未来の二人が邪魔してくるんだ

痛いほどに

忘れたい答え あがいたって

忘れたい答え あがいたって

君はもういない 日々だった

何もできなくてありがとうも

言えず堪えた涙は落ちた

「あとほんの少し…」つけた指輪

このままでいてお願い

泣いたってもうなにも戻ってこないって

泣いたってもうなにも戻ってこないって

何度言っても私の心 理解しない

描いた未来の二人が邪魔してくるんだ

痛いほどに

思い出数え大切に 一つ一つ消してく

思い出数え大切に 一つ一つ消してく

少し先行く足早な君は

二度と振り向かないの?

二度と振り向かないの?

「あとほんの少し…」想っていたい

「あとほんの少し…」想っていたい

どんなに辛くても

そばにいたい 君の笑う顔はもう全部

そばにいたい 君の笑う顔はもう全部

記憶の中で抜け殻のように

いつも笑っているんだ

あの日からずっと進めないよ

二人出会ったこと後悔なんてしてない

二人出会ったこと後悔なんてしてない

幸せ沢山くれた君へ強がりのさよなら

幸せ沢山くれた君へ強がりのさよなら

泣いたってもうなにも戻ってこないって

泣いたってもうなにも戻ってこないって

何度言っても私の心 理解しない

描いた未来の二人が邪魔してくるんだ

痛いほど鮮明に

君の笑う顔はもう全部

記憶の中で抜け殻のように

いつも笑っているんだ

あの日からずっと進めないよ