在计算a^n次方时,先将n变一变,寻找新的计算路径,预处理就是变治法的根本!
如果单纯循环执行n次相乘,那么时间复杂度为O(n),n为指数;利用二进制幂大大改进效率。
利用二进制幂求解分两种方法:从左至右二进制幂 和 从右至左二进制幂。
从左至右二进制幂
变换:
a^n = a^(b[n]2^n + ... + b[0]2^0) == ((b[n]*2 + b[n-1])*X + ....)2 + b[0]
先求n的二进制串:
如 5 -> 1 0 1, 那么b[2] = 1, b[1] =0, b[0] =1
Horner(b[0...n], x)
k = b[n]
for i = n-1 downto 0 do
p = x*k + b[i]
return p
那么n用作a的指数时意义是什么样的呢:
a^p = a^1
for i = n - 1 downto 0 do
a^p = a^(2p+b[i])
从右至左二进制幂
n变换方法:
a^n = a^(b[n]2^n + ... + b[0]2^0) == ((b[n]*2 + b[n-1])*X + ....)2 + b[0]
然后从b[0] -> b[n]方向逐步求解
详细代码:
#include <stdio.h>
#include <stdlib.h>
/*
* 返回number二进制串
*/
int GetBinArray(int number, int arr[], int size)
{
int idx = 0;
while (number > 0)
{
if (number & 1)
arr[idx++] = 1;
else
arr[idx++] = 0;
if (idx == size)
break;
number = number >> 1;
}
return idx;
}
/*
* a^n = a^(b[n]2^n + ... + b[0]2^0) = a^(b[n]2^n) * ... * a^b[0]
* b数组元素不是1就是0
*/
int Pow_Bin_RightToLeft(int number, int p)
{
if (p == 0)
return 1;
int size = sizeof(int)*8;
int *pint = (int *)malloc(size);
int length = GetBinArray(p, pint, size);
int idx;
int item = number;
int ret = 1;
for (idx = 0; idx < length; idx++)
{
if (pint[idx] == 1)
ret = ret * item;
item *= item;
}
free(pint);
return ret;
}
/*
* a^n = a^(b[n]2^n + ... + b[0]2^0) == ((b[n]*2 + b[n-1])*X + ....)2 + b[0]
* b数组元素不是1就是0
*/
int Pow_Bin_LeftToRight(int number, int p)
{
if (p == 0)
return 1;
int size = sizeof(int)*8;
int *pint = (int *)malloc(size);
int length = GetBinArray(p, pint, size);
int ret = number;
int idx;
for (idx = length-1-1; idx >= 0; --idx)
{
ret *= ret;
if(pint[idx] == 1)
ret *= number;
}
free(pint);
return ret;
}
int main(int argc, char *argv[])
{
printf("Please input number and pow:\n");
int number,p;
scanf("%d%d", &number, &p);
int ret = Pow_Bin_RightToLeft(number, p);
int ret1 = Pow_Bin_LeftToRight(number, p);
printf("Pow_Bin_RightToLeft: %d^%d == %d\n", number, p, ret);
printf("Pow_Bin_LeftToRight: %d^%d == %d\n", number, p, ret1);
main(0, NULL);
return 0;
}
两个算法时间复杂度均为logn.