欢迎光临散文网 会员登陆 & 注册

【仅供测试】快速幂与龟速乘

2023-08-16 20:49 作者:Mount256  | 我要投稿

1. 快速幂2. 龟速乘3. 快速幂取模4. 龟速乘取模5. 快速幂取模优化6. 矩阵乘7. 矩阵快速幂8. 矩阵快速幂用于递推式斐波那契数列递推式的一些套路参考资料

1. 快速幂

算法原理:

  • 计算
  • 仅需计算 3 次,而非 11 次
  • 计算
  • 仅需计算 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==9
  • 第 2 轮:(5 % 2 == 1)r=3x==27,b=2,a===81
  • 第 3 轮:(2 % 2 == 0)r 不变,b=1,a==
  • 第 4 轮:(1 % 2 == 1)r=x=,b=0,a==
  • 得到 r = =

2. 龟速乘

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

算法代码:

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. 矩阵快速幂用于递推式

斐波那契数列

数列递推式:

矩阵递推式:

矩阵公式:

#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(22);
        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)数列递推式:

矩阵递推式:

(2)数列递推式:

矩阵递推式:

(3)数列递推式:

矩阵递推式:

参考资料

基础算法—快速幂详解

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

KY21 递推数列

【仅供测试】快速幂与龟速乘的评论 (共 条)

分享到微博请遵守国家法律