【算法学习】计算n次方——变治法


在计算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


二进制求n的伪代码:

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.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值