快速幂(百科)

快速幂:


 

快速幂顾名思义,就是快速算某个数的多少次幂。其时间复杂度为 O(log2N), 与朴素的O(N)相比效率有了极大的提高。


 以下以求a的b次方来介绍

1、快速幂 - 原理

把b转换成2进制数
该2进制数第i位的权为a^(2^(i-1))
例如
a^11=a^(2^0+2^1+2^3)
11的二进制是1 0 1 1
11 = 2^3*1 + 2^2*0 + 2^1*1 + 2^0*1
因此,我们将a^11转化为算a^(2^0)*a^(2^1)*a^(2^3)

 

 2、实现

快速幂可以用位运算这个强大的工具实现
b and 1 //也就是取b的二进制最末位
b shr 1 //就是去掉b的二进制最末位
有了这个强大的工具,快速幂就好实现了!
var
a,b,n:int64;
function f(a,b,n:int64):int64;
var
t,y:int64;
begin
t:=1;y:=a;
while b<>0 do
begin
if (b and 1)=1 then t:=t*y mod n;
y:=y*y mod n; //这里用了一个很强大的技巧,y*y即求出了 a^(2^(i-1)) ←不知道这是什么的看原理
b:=b shr 1;
end;
exit(t);
end;
begin
read(a,b,n); // n是模
write(f(a,b,n));
end.

3、代码比较

 常规求幂

int pow1( inta, int b ) {
int r = 1;
while( b-- )
r *= a;
return r;
}

 


快速求幂(一般)

int pow2( int a, int b ) {
int r = 1, base = a;
while( b != 0 ) {
if( b % 2 )
r *= base;
base *= base;
b /= 2;
}
return r;
}

 


快速求幂(位操作)

int pow3( int a, int b ) {
int r = 1, base = a;
while( b != 0 ) {
if( b & 1 )
r *= base;
base *= base;
b >>= 1;
}
return r;
}

 


 

 


实际代码应用

Description:


 兔子题

You must know the famous problem which the answer is Fibonacci Number: “A newly born pair of rabbits, one male, one female, are put in a field; rabbits are able to mate at the age of one month so that at the end of its second month a female can produce another pair of rabbits; rabbits never die and a mating pair always produces one new pair (one male, one female) every month from the second month on. How many pairs will there be after n months?”

Chino is interested in such problems. She thinks that the rabbits in this problem have 2 stages in their life: the first stage lasts one month that rabbits don’t produce rabbits in this stage; and the second stage lasts forever, each pair of rabbits produces one pair of rabbits every month in the second stage. Now she is considering a more complicated situation: the rabbits have m stages. In the first stage, they don’t produce rabbits. In the i-th stage, each pair of rabbits produces one pair of rabbits every i-1 month(s). The i-th stage lasts Ki month(s), and the rabbits die at the end of the last month of the m-th stage. The question is the same: how many pairs of rabbits will there be after n months?

As for detail, rabbits produce rabbits at the beginning of a month (different from the classic problem in the first paragraph), die at the end of a month, and we count the rabbits at the middle of a month. Also, the rabbits will produce rabbits at the first month of each stage (except the first stage)

 

Input Format

The input contains 2 lines:

There are 2 integers in the first line: m n, 0 < m < 6, 0 <= n <= 1e9

There are m integers in the second line: K1 K2 ... Km, 0 < Ki <= 40

 

Output Format

Only one integer: the answer mod 10007 (since the answer may be very large).

And don’t forget the ‘\n’.

 

Sample Input 1

4 5

1 2 4 8

 

Sample Output 1

29

 

Sample Input 2

4 1000000000

1 2 4 8

 

Sample Output 2

8435

 

Sample Input 3

3 7

1 2 4

 

Sample Output 3

103

样例3解释图:

样例3解释

 

Hint:


 

1) (a+b)%P = ((a%P)+(b%P))%P;   (ab)%P = ((a%P)(b%P))%P

2) 直接推递推公式也会有困难,建议计算出转换矩阵,使得Ai+1=TAi

3) 在以上基础上推Tn计算TnA0,推Tn可用快速幂

 standard answer:

 


 

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
#define P 10007
#define MAXm 5
 
int m, n, k[MAXm], s[MAXm];
int INT;
int * INTP;
 
int ** matNew(int k);
void matFree(int **, int);
int ** matPow(int **, int, int);
 
int main() {
    scanf("%d%d", &m, &n);
    if (n > 0) {
        int i, j;
        for (i = 0; i < m; ++i)
            scanf("%d", k + i);
        s[0] = k[0]-1;
        for (i = 1; i < m; ++i)
            s[i] = s[i-1] + k[i];
        int ** T = matNew(s[m-1]+1);
        for (i = 1; i < m; ++i)
            for (j = s[i-1]; j < s[i]; j += i)
                T[j][0] = 1;
        for (i = 1; i <= s[m-1]; ++i)
            T[i-1][i] = 1;
        int ** Tn = matPow(T, n, s[m-1]+1);
        int ans = 0;
        for (i = 0; i <= s[m-1]; ++i)
            ans = (ans + Tn[0][i]) % P;
        printf("%d\n", ans);
        matFree(Tn, s[m-1]+1);
        matFree(T, s[m-1]+1);
    } else {
        printf("1\n");
    }
    return 0;
}
 
int ** matNew(int k) {
    int ** M = (int**)malloc(k * sizeof(INTP));
    int i;
    for (i = 0; i < k; ++i) {
        M[i] = (int*)malloc(k * sizeof(INT));
        memset(M[i], 0, k * sizeof(INT));
    }
    return M;
}
 
void matFree(int ** M, int k) {
    int i;
    for (i = 0; i < k; ++i)
        free(M[i]);
    free(M);
}
 
int ** matPow(int ** T, int n, int k) {
    int ** ans = matNew(k);
    int i, j;
    if (n == 1) {
        for (i = 0; i < k; ++i)
            for (j = 0; j < k; ++j)
                ans[i][j] = T[i][j];
    } else {
        int ** temp = matPow(T, n >> 1, k);
        int h;
        for (i = 0; i < k; ++i)
            for (j = 0; j < k; ++j)
                for (h = 0; h < k; ++h)
                    ans[i][j] = (temp[i][h] * temp[h][j] + ans[i][j]) % P;
        matFree(temp, k);
        if (n & 1) {
            temp = ans;
            ans = matNew(k);
            for (i = 0; i < k; ++i)
                for (j = 0; j < k; ++j)
                    for (h = 0; h < k; ++h)
                        ans[i][j] = (temp[i][h] * T[h][j] + ans[i][j]) % P;
            matFree(temp, k);
        }
    }
    return ans;
}

 

 

 

转载于:https://www.cnblogs.com/pora/p/5056683.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值