【算法与设计分析基础】大整数乘法int[]版+分治法求幂

原创 2015年07月11日 09:25:36
#include<stdio.h>
#include<iostream>
using namespace std;

#define NUM 700 //所要计算的数字的位数

/**
 * Time:201503281307
 * Use to deal with Long numbers.
 */
class BigDecimal{
private:
	static int min(int a,int b){//获取两数中的最小值
		return a^((a^b) & -(a>b));
	}
public:
	int r[NUM];//大数字
	int bit;//最高位在数组所在的位置
	/**
	 * r[NUM] ← 0
	 * 0 0 0 …… 0 1
	 *              bit=NUM-1
	 */
	BigDecimal(){
		memset(r,0,sizeof(r));
		r[NUM-1]=1;
		bit=NUM-1;
	}
	/**
	 * int To BigDecimal
	 * Maxint=2147483647
	 */
	/**
	 * if n = 0
	 *    The last of number r = 0
	 *    bit = The last index of number r
	 *    return
	 * i ← The last index of number r
	 * while n != 0
	 *    r[i] ← n % 10
	 *    i ← i - 1
	 *    n ← n / 10
	 * bit = The first index of number r
	 */
	void assign(int n){
		if(n==0){//如果赋值为0
			r[NUM-1]=0;//最后一位变0
			bit=NUM-1;//最高位指向最后一位
			return;
		}
		int i=NUM-1;//指向最后一位
		while(n){//如果n不为0
			r[i--]=n%10;//取n的个位数
			n/=10;//消去n的个位数
		}
		bit=i+1;//bit指向最高位
	}
	/**
	 * int[] to BigDecimal
	 */
	void assign(int a[],int n){
		int j=NUM-1;//指向最后一位
		for(;--n>=0;){//从右到左遍历数组a
			r[j--]=a[n];//从右到左给数组r赋值
		}
		bit=j+1;//最高位
	}
	void display(){
		int i=bit-1;
		for(;++i<NUM-1;){
			printf("%d",r[i]);
		}printf("%d\n",r[NUM-1]);
		cout<<"该数总共有"<<NUM-bit<<"位"<<endl;
	}
	/**
	 * minBit    用于存储两个数中最高位所在的位置
	 * i         用于向前推进
	 * temp      用于存储个位和进位
	 */
	/**
	 * minBit ← The first index of the longer number
	 * i ← The last index of number r 
	 * temp ← 0
	 * for i from NUM-1 to minBit (NUM-1 > minBit)
	 *       temp ← temp + r[i] + _A.r[i]
	 *       sum.r[i] ← temp % 10
	 *       temp ← temp / 10
	 * while temp != 0
	 *       sum.r[i] ← temp % 10
	 *       i ← i-1
	 *       temp ← temp / 10
	 * sum.bit ← i+1
	 * return sum;
	 */
	BigDecimal operator+(BigDecimal _A){
		BigDecimal sum;//存储结果的变量,用于返回值
		int minBit=min(bit,_A.bit),i=NUM-1,temp=0;//minBit获取两个加数中的最长数。
		if(minBit<=0){
			printf("结果位数太大,超出数组长度,请修改NUM的大小。\n");
			exit(0);
		}
		for(;i>=minBit;i--){// minBit ← NUM-1
			temp+=r[i]+_A.r[i];//单位与进位相加
			sum.r[i]=temp%10;//该位相加取个位数
			temp/=10;//进位
		}
		while(temp){//最后如果进位不为0
			sum.r[i--]=temp%10;//获取进位的个位
			temp/=10;//除去temp的个位
		}
		sum.bit=i+1;
		return sum;
	}
	/**
	 * minBit   被加数的最高位 和 加数的最高位左移S位 取两者最小
	 * i        左移S位后 那S位不用相加
	 * temp     存储进位
	 */
	/**
	 * minBit ← The smaller number between bit and _A.bit-S
     * i ← NUM-1-S
	 * temp ← 0
	 * for i ← from NUM-1-S to minBit (NUM-1-S > minBit)
	 *      temp ← temp + r[i] + _A.r[i+S]
	 *      r[i] ← temp % 10
	 *      temp ← temp / 10
	 * while temp != 0
	 *      r[i] = r[i] + temp % 10
	 *      temp = temp / 10;
	 * bit = i + 1;
	 */
	void add(BigDecimal _A,int S){//错开S位相加,用于乘法的时候数字移位相加
		int minBit=min(bit,_A.bit-S),i=NUM-1-S,temp=0;//使用的是移位相加,所以this % 10 * _A的最高位需要挪S格。
		if(minBit<=0){
			printf("结果位数太大,超出数组长度,请修改NUM的大小。\n");
			exit(0);
		}
		for(;i>=minBit;i--){
			temp+=r[i]+_A.r[i+S];
			r[i]=temp%10;//该位相加取最后一位数
			temp/=10;//进位
		}
		while(temp){
			r[i--]+=temp%10;
			temp/=10;
		}
		bit=i+1;
	}
	/**
     * n 跟 BigDecimal每一位都乘以一遍,然后结果取个位,其余的数字进位
	 */
	/**
	 * if n==0
	 *     sum.bit ← NUM -1
	 *     all of sum.r ← 0
	 *     return sum;
	 * i ← NUM - 1
	 * temp ← 0
	 * for i ← NUM-1 to bit  (NUM-1 > bit)
	 *     temp ← temp + r[i] * n
	 *     sum.r[i] ← temp % 10
	 *     temp ← temp / 10
	 * while temp !=0
	 *     sum.r[i] ← temp % 10
	 *     i ← i - 1
	 *     temp ← temp / 10
	 * sum.bit ← i + 1
	 */
	BigDecimal operator *(int n){
		BigDecimal sum;
		//当乘以0的时候
		if(n==0){
			sum.bit=NUM-1;
			memset(sum.r,0,sizeof(sum.r));
			return sum;
		}
		int i=NUM-1,temp=0;
		/**
		 * 乘数的每一位 乘以 被乘数(int),获取个位,进位赋给temp
		 */
		for(;i>=bit;i--){
			temp+=r[i]*n;
		    sum.r[i]=temp%10;
			temp/=10;
		}
		/**
		 * 如果进位有剩余就放在结果的最前面
		 */
		while(temp){
			sum.r[i--]=temp%10;
			temp/=10;
		}
		sum.bit=i+1;
		return sum;
	}
	/**
	 * 乘数 与 被乘数的每一位相乘,然后移位相加
	 */
	/**
	 * sum.r[NUM-1] ← 0
	 * i ← bit - 1
	 * j ← NUM - 1
	 * while i != j
	 *     temp ← _A * r[j]
	 *     sum ← sum + temp * 10^(NUM-1-j)
	 *     j ← j - 1
	 * return sum
	 */
	BigDecimal operator *(BigDecimal _A){//_A  0  this
		BigDecimal sum,temp;
		sum.r[NUM-1]=0;
		int i=bit-1,j=NUM-1;//i为乘数的最高位的左边一位,j用于访问乘数的每一位
		while(i!=j){
			temp=_A*r[j];//乘数与被乘数的其中一位相乘,然后赋给temp
			sum.add(temp,NUM-1-j);//每执行加法运算一次,下一次就左移一格
			j--;
		}
		return sum;
	}
	/**
	 * temp的值为0或-1,表示是否向高位借1
	 */
	/**
	 * minBit ← The smaller number between bit and _A.bit
	 * temp ← 0
	 * i ← NUM
	 * for i ← NUM-1 to minBit  (NUM-1 > minBit)
	 *     if r[i] (+0 or -1) >= _A.r[i]
	 *         sum.r[i] = r[i] +0 or -1 - _A.r[i]
	 *         temp ← 0
	 *     else
	 *         sum.r[i] = r[i] + 10 (+0 or -1) -_A.r[i]
	 *         temp ← -1
	 *     if The first index of r 's value = 0
	 *         sum.bit ← minBit + 1
	 *     else
	 *         sum.bit ← minBit
	 */
	BigDecimal operator -(BigDecimal _A){//减法,用于大整数乘法
		BigDecimal sum;
		int minBit=min(bit,_A.bit),temp=0;//获取两个数字中最长的数字,获取它最高位所在下标
		int i=NUM;//用于遍历被减数
		for(;--i>=minBit;){
			if(r[i]+temp>=_A.r[i]){//如果这一位(-1 或 +0后)比被减数要大
				sum.r[i]=r[i]+temp-_A.r[i];//得到这一位减后的值
				temp=0;//不用向高位借1
			}
			else//否则
			{
				sum.r[i]=r[i]+10+temp-_A.r[i];//加10后再减
				temp=-1;//向高位借1
			}
		}
		if(minBit!=NUM-1 && sum.r[minBit]==0){//如果最高位不为NUM-1 且其值为0
			sum.bit=minBit+1;//最高位右移
		}
		else{
			sum.bit=minBit;//最高位不变
		}
		return sum;
	}
	/**
	 * 获取 BigDecimal中 S坐标到E坐标 的子串
	 */
	/**
	 * if S > E return
	 * num ← E - S + 1
	 * i ← NUM - 1
	 * for E to S
	 *     sum.r[i--] ← r[E]
	 * sum.bit ← i + 1
	 * while the first index of r's value = 0
	 *     sum.bit ← sum.bit + 1
	 * return sum
	 */
	BigDecimal sub(int S,int E){
		if(S>E)return BigDecimal();
		BigDecimal sum;
		int num=E-S+1,i=NUM-1;
		for(;E>=S;E--){//获取E到S的子串
		   	sum.r[i--]=r[E];
		}
        sum.bit=i+1;
		while(sum.bit!=NUM-1 && !sum.r[sum.bit]){//如果最高位为0
			sum.bit++;//最高位右移
		}
		return sum;
	}
	/**
 	 * 10^n
	 */
	static BigDecimal Ten(int n){
		BigDecimal sum;
		sum.r[NUM-1]=0;
		sum.r[NUM-n-1]=1;//第NUM-n-1位赋值为1
		sum.bit=NUM-n-1;//把该位设为最高位
		return sum;
	}
	/**
	 * 大整数乘法
	 * 使用该函数不宜将NUM设为1000以上,内存占用太多导致弹框
	 */
	BigDecimal static mul(BigDecimal a,BigDecimal b){
		int n=min(NUM-a.bit,NUM-b.bit);
		if(n<=1)return a*b;
		if(n<=0){
			printf("结果位数太大,超出数组长度,请修改NUM的大小。\n");
			exit(0);
		}
		BigDecimal a1=a.sub(a.bit,NUM-n/2-1),//获取a数字前半段
		           a0=a.sub(NUM-n/2,NUM-1),//获取a数字后半段
	               b1=b.sub(b.bit,NUM-n/2-1),//获取b数字前半段
		           b0=b.sub(NUM-n/2,NUM-1),//获取b数字后半段
		           c2=mul(a1,b1),//a数字前半段 乘以 b数字前半段
		           c0=mul(a0,b0),//a数字后半段 乘以 b数字后半段
				   c1=mul(a1+a0,b1+b0)-(c2+c0);
		return c2*Ten(n/2*2)+c1*Ten(n/2)+c0;
	}
};

class Power{
private:
    int a;
	int n;
	int sum;
	BigDecimal r;
	/**
	 * 数字为600位以下的时候使用移位相乘法,600位以上的时候使用大整数乘法
	 */
	BigDecimal Cal_MOVEANDCUT(int nn){
		if(nn>1){
			BigDecimal a=Cal_MOVEANDCUT(nn/2);
			BigDecimal b=Cal_MOVEANDCUT(nn-nn/2);
			if(NUM-a.bit>=600 || NUM-b.bit>=600){
				return BigDecimal::mul(a,b);
			}
			else{
				return a*b;
			}
		}
		return r;
	}
	/**
	 * 左移相加法,分治求a^n
	 */
	BigDecimal Cal_MOVE(int nn){
		if(nn>1){
			return Cal_MOVE(nn/2)*Cal_MOVE(nn-nn/2);
		}
		return r;
	}
	/**
	 * 折半求积法,分治求a^n
	 */
	BigDecimal Cal_CUT(int nn){
		if(nn>1){
			return BigDecimal::mul(Cal_CUT(nn/2),Cal_CUT(nn-nn/2));
		}
		return r;
	}
public:
	Power(int a,int n){
		this->a=a;
		this->n=n;
		r.assign(a);
		sum=0;
	}
	void set(int a,int n){
		this->a=a;
		this->n=n;
		r.assign(a);
	}
	/**
	 * 大整数乘法
	 */
	BigDecimal Cal_CUT(){
		try{
			if(n==0 && a!=0)return BigDecimal();
			if(n==0 && a==0)throw 1;
			if(n<0 || a<=0)throw 2;
			if(n==1)return r;
			if(a==1)return BigDecimal();
			return BigDecimal::mul(Cal_CUT(n/2),Cal_CUT(n-n/2));
		}
		catch(int message){
			switch(message){
			case 1:
				printf("0 ^ 0 is undefined!\n");break;
			case 2:
				printf("Minus's exponentation is undefined\n");break;
			}
			return BigDecimal();
		}
	}
	/**
	 * 左移相加法
	 */
	BigDecimal Cal_MOVE(){
		try{
			if(n==0 && a!=0)return BigDecimal();
			if(n==0 && a==0)throw 1;
			if(n<0 || a<=0)throw 2;
			if(n==1)return r;
			if(a==1)return BigDecimal();
			return Cal_MOVE(n/2)*Cal_MOVE(n-n/2);
		}
		catch(int message){
			switch(message){
			case 1:
				printf("0 ^ 0 is undefined!\n");break;
			case 2:
		    	printf("Minus's exponentation is undefined\n");break;
			}
			return BigDecimal();
		}
	}
	/*
	 * 左移相加法与折半求积法一同使用
	 */
	BigDecimal Cal_MOVEANDCUT(){
		try{
			if(n==0 && a!=0)return BigDecimal();
			if(n==0 && a==0)throw 1;
			if(n<0 || a<=0)throw 2;
			if(n==1)return r;
			if(a==1)return BigDecimal();
			return Cal_MOVEANDCUT(n/2)*Cal_MOVEANDCUT(n-n/2);
		}
		catch(int message){
			switch(message){
			case 1:
				printf("0 ^ 0 is undefined!\n");break;
			case 2:
				printf("Minus's exponentation is undefined\n");break;
			}
			return BigDecimal();
		}
	}
};


int main(){
	int a,n;
	printf("a = ");
	scanf("%d",&a);
	printf("n = ");
	scanf("%d",&n);
	Power p(a,n);
	BigDecimal b;
	b=p.Cal_MOVE();
	b.display();
	return 0;

}

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

【C++心路历程23】求幂的和 分治算法

【问题描述】   题目很简单:请你计算(a^1+a^2+…+a^n) mod 1234567 的结果,其中(0 < a,n < 2^31 )。【输入格式】 第一行T,表示数据组数,接下来的T行,每...

二分求幂(pow的logn算法)

二分求幂,最初是在剑指offer上看到,书中给出了递归的代码,后来在王道机试指南上再次看到,那上面给出了非递归的代码。 二分求幂的原理如图: 剑指offer上的递归代码如下: dou...

008-大整数乘法-分治法-《算法设计技巧与分析》M.H.A学习笔记

设u和v是两个n位二进制整数,传统乘法算法需要Θ(n2)的时间来计算u和v的乘积。 但我们还有一个Θ(n1.59)的改进算法。

【算法设计与分析基础】大整数乘法string版

大整数乘法的算法实现与分析,对于大于600位的两个整数相乘会比原始乘法要快得多。

算法06:大整数乘法分治算法——分治法Part2

(2)大整数乘法

分治法求大数乘法

摘要: 乘法主要有3种方法:1、模拟竖式计算法复杂度O( N 2次方);2、分治法,最优O(N 1.58次方);3、傅里叶变换法FFT。其中竖式计算法占物理空间小,思维简单;分治法复杂度有所...

分治法大整数乘法课件

  • 2011-05-12 08:01
  • 642KB
  • 下载

分治法大整数乘法课件

  • 2011-05-11 21:57
  • 642KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)