关闭

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

标签: 算法大整数乘法分治法求幂
442人阅读 评论(0) 收藏 举报
分类:
#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;

}

0
0

猜你在找
【直播】机器学习&数据挖掘7周实训--韦玮
【套餐】系统集成项目管理工程师顺利通关--徐朋
【直播】3小时掌握Docker最佳实战-徐西宁
【套餐】机器学习系列套餐(算法+实战)--唐宇迪
【直播】计算机视觉原理及实战--屈教授
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之矩阵--黄博士
【套餐】微信订阅号+服务号Java版 v2.0--翟东平
【直播】机器学习之凸优化--马博士
【套餐】Javascript 设计模式实战--曾亮
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:62144次
    • 积分:1406
    • 等级:
    • 排名:千里之外
    • 原创:80篇
    • 转载:6篇
    • 译文:1篇
    • 评论:15条
    最新评论