例如求3^75
O(n)求法,直接将3连续乘75次
75的2进制 1001011
3^75 = 3^64 * 3^8 * 3^2 * 3^1 ,乘法次数将减少
对比程序
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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;
}
https://www.nowcoder.com/practice/1a834e5e3e1a4b7ba251417554e07c00?tpId=13&tqId=11165&tPage=1&rp=1&ru=%2Fta%2Fcoding-interviews&qru=%2Fta%2Fcoding-interviews%2Fquestion-ranking
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;
}
};
都能写成 递推的矩阵相乘的情况,且矩阵的系数可以由前面的几项推出来,是确定的。
依次类推。。。
这样就可以根据前面几项 * 矩阵次幂 得到后面的项
问题将转化为矩阵的乘幂运算,即快速幂问题,可以将时间复杂度优化为 O(logn)
题目地址
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;
}
};
题目地址
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;
}
};
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