快速幂,大数处理,递归优化

快速幂

例如求3^75
O(n)求法,直接将3连续乘75次

75的2进制 1001011
3^75 = 3^64 * 3^8 * 3^2 * 3^1 ,乘法次数将减少

对比程序

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <set>
#include <map>
#include <ctime>

using namespace std;

const int INF = 0x7fffffff;
const int MIN = -INF - 1;

typedef long long LL;

// 时间复杂度 O(n)
LL f1(LL a, int n)
{
    LL rs = 1;
    for (int i = 0; i < n; i++)
    {
        rs *= a;
    }
    return rs;
}

// 时间复杂度 O(logn)
LL f2(LL x, int n)
{
    int Max = 1000000007;
    LL res = 1;
    while (n>0)
    {
        if (n & 1) // 判断最低位是否是1
            res = (res*x) % Max;
        x = (x*x) % Max;// x变大
        n >>= 1; // 右移一位(或者除以2)
    }
    return res;
}

int main()
{
    int n = 1e7;
    clock_t start, finish;
    double totaltime;
    start = clock();

    cout << f2(1,n) << endl;

    finish = clock();
    //totaltime = (double)(finish - start) / CLOCKS_PER_SEC;
    totaltime = (double)(finish - start) ;
    cout << totaltime << endl;

    //
    start = clock();

    cout << f1(1, n) << endl;

    finish = clock();
    totaltime = (double)(finish - start);
    cout << totaltime << endl;

    return 0;
}

数值的整数次方

题目地址 (牛客网 剑指offer)

https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking

ac代码
class Solution {
public:
    double Power(double base, int exponent) {
        if(exponent == 0)
            return 1;

        bool flag = true;
        if(exponent < 0)
        {
            flag = false;
            exponent = - exponent;
        }
        bool flag2 = true;
        if(base < 0){
            flag2 = false;
            base = - base;
        }

        double sign = 1.0;
        if(flag2 == false && exponent % 2 == 1)
            sign = -1.0;

        double ans = 1; 
        double base2 = base;
        int n = exponent;
        while(n != 0)
        {
            int tmp = n & 1;
            n = n >> 1;
            if(tmp != 0)
                ans = ans * base2;  
            base2 *= base2;
        }
        if(flag == false)
            return sign * 1.0 / ans;
        return sign * ans;
    }
};

一般的规律(线性代数)

满足如下的线性关系表达式
这里写图片描述

都能写成 递推的矩阵相乘的情况,且矩阵的系数可以由前面的几项推出来,是确定的。

2阶情况
这里写图片描述

3阶情况
这里写图片描述

依次类推。。。

这样就可以根据前面几项 * 矩阵次幂 得到后面的项

例如
这里写图片描述

问题将转化为矩阵的乘幂运算,即快速幂问题,可以将时间复杂度优化为 O(logn)


372. Super Pow (leetcode)

题目地址
https://leetcode.com/problems/super-pow/

ac代码:

const int mod = 1337;

class Solution {
public:

    // a^n % 1337 其中 n < 10
    int npow(int a, int n)
    {
        a = a % mod;
        vector<int> apn(11);
        apn[0] = 1;
        apn[1] = a;
        for (int i = 2; i <= n; i++)
        {
            apn[i] = (apn[i - 1] * a) % mod;
        }
        return apn[n];
    }

    int superPow(int a, vector<int>& b) {
        a = a % mod;

        int len = b.size();
        if (len == 1)
        {
            return npow(a, b[0]);
        }

        int ans = 1;
        int n = a;
        for (int i = len - 1; i >= 0; i--){

            if (b[i] > 0)
            {
                int btmp = npow(n, b[i]);
                ans = (ans * btmp) % mod;
            }
            n = npow(n, 10); //n = n^10  即 a,a^10,a^100,a^1000 
        }

        return ans;
    }
};

50. Pow(x, n)

题目地址
https://leetcode.com/problems/powx-n/

ac代码如下,需要注意特殊数据,正负数等的处理

class Solution {
public:
    double myPow(double x, int n) {
        int INF = 0x7fffffff;
        int MIN = -INF - 1;
        if (n == 0)
            return 1;

        if (n == MIN) // 最大负数特殊处理
        {
            if (x < 0) // x是负数,也将变成正数
                x = -x;
            double ans = 1;
            n = INF;
            while (n != 0)
            {
                if ((n & 1) == 1)
                    ans *= x;
                x = x*x;
                n = n >> 1;// 带符号右移
            }
            ans *= x;
            return 1.0 / ans;
        }
        bool flag = true;
        if (n < 0)
        {
            n = -n;
            flag = false;
        }
        int sign = 1;
        if (x < 0)
        {
            x = -x;
            if ((n & 1) == 1) // 奇数
                sign = -1;
        }

        double ans = 1;
        while (n != 0)
        {
            if ((n & 1) == 1)
                ans *= x;
            x = x*x;
            n = n >> 1;// 带符号右移
        }
        if (flag)
            return sign * ans;
        else
            return sign * (1.0 / ans);
    }
};

斐波拉契加强版

http://www.nowcoder.com/practice/2393c500d43a4293aa7a662274aff4d1?tpId=49&tqId=29343&rp=4&ru=/ta/2016test&qru=/ta/2016test/question-ranking

对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - 1) + F(n - 2),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围内的非负整数,请设计一个高效算法,计算第n项F(n)。第一个斐波拉契数为F(0) = 1。
给定一个非负整数,请返回斐波拉契数列的第n项,为了防止溢出,请将结果Mod 1000000007。
测试样例:
3
返回:2
12
返回 233

const int INF = 0x7fffffff;
const int MIN = -INF - 1;

const int N = 2;
const int Mod = 1000000007;

typedef struct matrix
{
    long long data[N][N];
    matrix()
    {
        for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            data[i][j] = 0;
    }
}Ma;

// 矩阵相乘
Ma matrixMulti(Ma m1, Ma m2)
{
    Ma rs;
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            rs.data[i][j] = 0;
            for (int k = 0; k < N; k++)
            {
                rs.data[i][j] = (rs.data[i][j] + m1.data[i][k] * m2.data[k][j]) % Mod;
            }
        }
    }
    return rs;
}

// 矩阵的n次幂
Ma matrixN(Ma m, int n)
{
    Ma rs;
    for (int i = 0; i < N; i++) //rs成为单位矩阵
        rs.data[i][i] = 1;

    while (n > 0){
        if (n & 1)
            rs = matrixMulti(rs, m);
        m = matrixMulti(m, m);
        n >>= 1;
    }

    return rs;
}

class Fibonacci {
public:

    int getNthNumber(int n) { //fabonacci加强版
        // write code here
        if (n == 0 || n == 1)
            return 1;

        Ma m; // fabonacci的二阶矩阵
        m.data[0][0] = 1;
        m.data[0][1] = 1;
        m.data[1][0] = 1;
        m.data[1][1] = 0;

        // [fn fn-1] = [f1 f0] * matrix^(n-1)
        Ma rs = matrixN(m, n - 1);

        long long ans = 0;
        for (int i = 0; i < N; i++)
            ans = (ans + rs.data[i][0]) % Mod;

        return ans;
    }
};

3阶问题,同上

http://www.nowcoder.com/practice/f26698ae586b46428c87a5dbcdae272c?tpId=49&tqId=29345&rp=4&ru=/ta/2016test&qru=/ta/2016test/question-ranking

在农场中,奶牛家族是一个非常庞大的家族,对于家族中的母牛,从它出生那年算起,第三年便能成熟,成熟的母牛每年可以生出一只小母牛。即母牛从出生开始的第三年便能做妈妈。最开始农场只有一只母牛,它从第二年开始生小母牛。请设计一个高效算法,返回第n年的母牛总数,已知n的范围为int范围内的正整数。
给定一个正整数n,请返回第n年的母牛总数,为了防止溢出,请将结果Mod 1000000007。
测试样例:
6
返回:9

const int INF = 0x7fffffff;
const int MIN = -INF - 1;

const int N = 3;
const int Mod = 1000000007;

typedef struct matrix
{
    long long data[N][N];
    matrix()
    {
        for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            data[i][j] = 0;
    }
}Ma;

// 矩阵相乘
Ma matrixMulti(Ma m1, Ma m2)
{
    Ma rs;
    for (int i = 0; i < N; i++)
    {
        for (int j = 0; j < N; j++)
        {
            rs.data[i][j] = 0;
            for (int k = 0; k < N; k++)
            {
                rs.data[i][j] = (rs.data[i][j] + m1.data[i][k] * m2.data[k][j]) % Mod;
            }
        }
    }
    return rs;
}

// 矩阵的n次幂
Ma matrixN(Ma m, int n)
{
    Ma rs;
    for (int i = 0; i < N; i++) //rs成为单位矩阵
        rs.data[i][i] = 1;

    while (n > 0){
        if (n & 1)
            rs = matrixMulti(rs, m);
        m = matrixMulti(m, m);
        n >>= 1;
    }

    return rs;
}

class Cows {
public:

    int countSum(int n) { //fabonacci加强版
        // write code here
        if (n <= 3)// f1 = 1 f2 = 2 f3 = 3
            return n;

        Ma m; // fabonacci的二阶矩阵
        m.data[0][0] = 1;
        m.data[0][1] = 1;
        m.data[1][2] = 1;
        m.data[2][0] = 1;

        // fn = fn-1 + fn-3
        // [fn fn-1 fn-2] = [f3 f2 f1] * matrix^(n-3)
        Ma rs = matrixN(m, n - 3);


        long long ans = 0;
        for (int i = 0; i < N; i++)
            ans = (ans + (3 - i) * rs.data[i][0]) % Mod;

        return ans;
    }
};

参考学习:

矩阵 快速幂
http://www.cnblogs.com/yan-boy/archive/2012/11/29/2795294.html

POJ-3070Fibonacci(矩阵快速幂求Fibonacci数列)
http://www.cnblogs.com/dongsheng/archive/2013/06/02/3114073.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值