大数运算--除法

#include <iostream>
#include <stdio.h>
#include <cstring>
#include<cassert>
#include <cmath>

using namespace std;
//大数运算最关键的是用进制理解,就是把一个数组元素表示的最大值作为一个进制;如此,最容易的大数运算也是最耗费空间的就是一个元素表示一位数字,即用十进制表示;还有一种折中的方案,是一个元素最大能表示9999,如此用的是10000进制,则下面的baseNum = 10000;本文的实现的是最省空间的方法,一个元素就是一个unsigned short,这样就表示用2^16进制
const int MaxShort = 100;//最多有MaxShort * 2个字节//如果要表示64位int,MaxShort = 4
const int baseNum = pow(2.0,16.0); 
const int Shift = 16;
typedef unsigned short Unshort;

typedef struct BigNum
{
	int m_len;//可以通过m_len的正负,判断大数的正负。本程序未实现
	Unshort m_data[MaxShort];
}BigNum;

void Reverse(char *src);
void GetAbsDecNum(BigNum &bg,char *tgt);

void PrintData(BigNum &a)
{
	for (int i=0;i<a.m_len;++i)
	{
		cout<<a.m_data[i]<<" ";
	}
	cout<<endl;
}
void Print(BigNum &a)
{
	char t[MaxShort];
	memset(t,0,sizeof(t));
	
	GetAbsDecNum(a,t);
	Reverse(t);
	cout<<t<<endl;
}
int AbsComp(BigNum &a,BigNum &b)//a>b,1;a = b,0;a<b,-1
{
	if(a.m_len>b.m_len)
		return 1;
	if (a.m_len<b.m_len)
		return -1;

	int size = a.m_len -1;
	for (int i=size;i>=0;--i)
	{
		if(a.m_data[i]>b.m_data[i])
			return 1;
		if(a.m_data[i] <b.m_data[i])
			return -1;

	}

	return 0;

	
}

BigNum CreateAbsBigNum(char *src)//低位放前,高位放后;
{
	BigNum bg;
	Unshort *data = bg.m_data;
	int b = 0;
	while (src[0]!='\0')
	{
		int i = 0;
		int reminder = 0;
		
		while (src[i]!='\0')
		{
			int divident = src[i] - '0' + reminder * 10;
			reminder = divident % baseNum;
			src[i] = divident / baseNum + '0';
			++i;
		}
		data[b++] = reminder;
		
		while (*src =='0')
			++src;
	}
	bg.m_len = b;
	return bg;
	
}

BigNum AbsMinus(BigNum &bg1,BigNum &bg2)//bg1 >bg2
{
	int left = bg1.m_len;
	int right = bg2.m_len;
	

	int carry = 0;
	BigNum bg;
	
	for (int i=0;i<right;++i)
	{
		int tmp = bg1.m_data[i] - bg2.m_data[i] - carry;
		if(tmp<0)
		{
			carry = 1;
			bg.m_data[i] = baseNum + tmp;
		}
		else
		{
			bg.m_data[i] = tmp;
			carry = 0;
		}
	}

	for (int i= right;i<left;++i)
	{
		int tmp = bg1.m_data[i] - carry;
		if (tmp <0)
		{
			carry = 1;
			bg.m_data[i] = baseNum +tmp;
		}
		else
		{
			bg.m_data[i] = tmp;
			carry = 0;
		}
	}
	bg.m_len = left;
	while (bg.m_len>=0 && bg.m_data[bg.m_len -1]==0)
	{
		--bg.m_len;
	}

	return bg;

}

int AbsMinus(BigNum &bg1,int n,BigNum &bg2)//必须保证bg1的前n位的数要大于等于bg2//返回减少的位数
{
	if(n<bg2.m_len)
	{
		cerr<<"minus error\n";
		exit(1);
	}

	int size = (bg1.m_len >n) ?n:bg1.m_len;
	int beg = bg1.m_len -size;
	int len2 = bg2.m_len;
	int len1 = bg1.m_len;
	
	int carry = 0;
	for (int i=0;i<len2;++i)
	{
		int tmp = bg1.m_data[i+beg] - bg2.m_data[i] - carry;
		if (tmp < 0)
		{
			carry = 1;
			tmp +=baseNum;

		}
		else
			carry = 0;
		bg1.m_data[i+beg] = tmp;
	}

	for (int i = len2 + beg;i<len1;++i)
	{
		int tmp = bg1.m_data[i] -carry;
		if(tmp<0)
		{
			carry = 1;
			tmp +=baseNum;
		}
		else
			carry = 0;

		bg1.m_data[i] = tmp;
	}
	
	while (bg1.m_len>=0 && bg1.m_data[bg1.m_len - 1] ==0)
		--bg1.m_len;

	return len1 - bg1.m_len;

}

bool AbsComp(BigNum &a,int n,BigNum &b)//大数的前n位数与b比较大小;a>=b返回true
{
	int size = (a.m_len>n)? n:a.m_len;
	if(size > b.m_len)
		return true;
	if(size <b.m_len )
		return false;
	
	int end = a.m_len - size;
	for (int i=a.m_len -1;i>=end;--i)
	{
		if(a.m_data[i]>b.m_data[i-end])
			return true;
		if(a.m_data[i] < b.m_data[i-end])
			return false;

	}

	return true;
}

BigNum AbsDiv(BigNum &bg1,BigNum &bg2)//返回商,bg1变为余数;被除数的位数为n,除数的位数为m,则商的位数最多为n-m+1
{//bg1 >=bg2
	BigNum c;
	memset(c.m_data,0,sizeof(c.m_data));
	
	int len2= bg2.m_len;
	int cLen = bg1.m_len -len2 ;
	int i=0;
	while(AbsComp(bg1,bg2)>=0)
	{
		//cout<<"first while:"<<++i<<endl;
		int len1=bg1.m_len;
		int indx = len1 - len2;
		int n = len2;
		if(!AbsComp(bg1,len2,bg2))
		{
			--indx;
			++n;
		}
		//cout<<"n = "<<n<<endl;
		Unshort count = 0;
		while (AbsComp(bg1,n,bg2))
		{
			//cout<<"second while : n = "<<n<<endl;
			++count;
			//cout<<count<<endl;
			/*BigNum preBg1 = bg1;
			BigNum preBg2 = bg2;
			cout<<"pre \n";
			Print(preBg1);
			Print(preBg2);*/

			int tmp = AbsMinus(bg1,n,bg2);
			/*cout<<"after \n";
			preBg1 = bg1;
			preBg2 = bg2;
			Print(preBg1);
			Print(preBg2);*/

			n -= tmp;
		}
		//cout<<count<<endl;
		c.m_data[indx] = count;

	}
	while(cLen>=0 && c.m_data[cLen] ==0)
		--cLen;
	c.m_len = cLen +1;

	return c;
}

BigNum AbsAdd(BigNum &bg1,BigNum &bg2)
{
	int left = bg1.m_len;
	int right = bg2.m_len;

	int size = left;
	int maxLen = right;
	BigNum *bgmax = &bg2;
	if(size>right)
	{
		size = right;
		maxLen = left;
		bgmax = &bg1;
	}

	int carry = 0;
	BigNum bg;
	for (int i=0;i<size;++i)
	{
		int tmp = bg1.m_data[i] + bg2.m_data[i] + carry;
		//carry = tmp /baseNum;
		carry = tmp>>Shift;
		//bg.m_data[i] = tmp &(1<<Shift - 1);
		bg.m_data[i] = tmp & 0xffff;
	}
	
	for (int i = size;i<maxLen;++i)
	{
		int tmp = bgmax->m_data[i] + carry;
		carry = tmp>>Shift;
		//carry = tmp /baseNum;
		//bg.m_data[i] = tmp &(1<<Shift - 1);
		bg.m_data[i] = tmp & 0xffff;
	}

	if (carry)
	{
		bg.m_data[maxLen] = 1;
		++maxLen;
	}
	bg.m_len = maxLen;
	return bg;
}

BigNum AbsMul(BigNum &bg1,BigNum &bg2)
{
	BigNum bg;
	memset(bg.m_data,0,sizeof(bg.m_data));
	int i,j;
	for (i=0;i<bg1.m_len;++i)
	{
		size_t carry = 0;
		for (j=0;j <bg2.m_len;++j)
		{
			size_t tmp =  bg.m_data[i+j] + bg1.m_data[i] * bg2.m_data[j] + carry;
			bg.m_data[i+j] = tmp %baseNum;
			carry = tmp /baseNum;
		}
		if (carry >0)
		{
			bg.m_data[i+j] += carry; 
		}
	}
	if(bg.m_data[bg1.m_len+bg2.m_len -1] > 0)
		bg.m_len = bg1.m_len+bg2.m_len;
	else
		bg.m_len = bg1.m_len+bg2.m_len -1;

	return bg;



	
}

void GetAbsDecNum(BigNum &bg,char *tgt)
{
	int size = bg.m_len;
	Unshort *data = bg.m_data;
	int b=0;
	
	while (size>0)
	{
		int i = size - 1;
		int reminder = 0;

		while (i>=0)
		{
			int divident = data[i] + reminder * baseNum;
			reminder = divident % 10;
			data[i] = divident /10;
			--i;
		}
		tgt[b++] = reminder + '0';

		while(size>0 && data[size -1]==0)
			--size;
	}
	tgt[b] = '\0';
}

void Reverse(char *src)
{
	int size = strlen(src);
	int beg = size/2 -1;
	for (;beg>=0;--beg)
	{
		int sym = size -beg -1;
		char tmp = src[beg];
		src[beg] = src[sym];
		src[sym] = tmp;
	}
}

int main()
{
	char t[100];
	memset(t,0,sizeof(t));
	char s[] = "6430006453235007";//6430006453235007
	BigNum bg = CreateAbsBigNum(s);
	//PrintData(bg);	
	//BigNum bgg = bg;
	//Print(bg);
	//GetAbsDecNum(bg,t);
	//Reverse(t);
	//cout<<t<<endl;

	memset(t,0,sizeof(t)); 
	char s1[]="1234567932211123423411431421246";//1234567932211123423411431421246
	//112126430006453235007
	BigNum bg1 = CreateAbsBigNum(s1);
	//PrintData(bg1);
	//BigNum bg11 = bg1;
	//Print(bg11);
	//GetAbsDecNum(bg1,t);
	//Reverse(t);
	//cout<<t<<endl;
	BigNum bg2 = AbsDiv(bg1,bg);

	
	memset(t,0,sizeof(t));
	GetAbsDecNum(bg2,t);
	Reverse(t);
	cout<<t<<endl;
}

主要参考: http://hi.baidu.com/erennetwork/blog/item/7fcfe31164dac8134a90a7c2.html

大数除法应该是最难实现的了,假设数组a除以b,实现它有两种方式:

1:用a-=b,循环,直到a<b,记下来执行多少次,则为计算结果;

2:模拟现实,我们手算时是怎么算的,程序就怎么写,

假设a={2 4 2 3 1},b={2 3},结果result={0 0 0 0 0}:

先取a[0]a[1]即24,减去b一次,得a={0 1 2 3 1},result={0 1 0 0 0};

再取a[1]a[2]即12,发现它小于b,则多取一位,取a[1]a[2]a[3]即123,减b五次,得a={0 0 0 8 1},result={0 1 0 5 0};

再取a[3]a[4]即81,减b三次,得a={0 0 0 1 2},result={0 1 0 5 3}。

如果按照方法一计算,效率是无法容忍的。

假如用11111除以1,方法一要计算11111次,而方法二则只要5次。

下面是方法二的代码,经过了简单的测试(没做数据校验,假设数据都是正常的):

bool compare(int a[],int n,int b[],int m)//当a>=b时,返回true
{
    int i=0;
    int j=0;
    int temp_n=n;
    int temp_m=m;
    while(a[i]==0)
    {
        i++;
        temp_n--;
    }
    while(b[j]==0)
    {
        j++;
        temp_m--;
    }
    if(temp_n>temp_m)
        return true;
    else if(temp_n<temp_m)
        return false;
    else
    {
        for(i,j;i<n,j<m;i++,j++)
        {
            if(a[i]>b[j])return true;
            if(a[i]<b[j])return false;
        }
        return true;
    }
}
void div(int a[],int n,int b[],int m)
{
    int *result;
    int *temp;
    int start=0;
    int end=start+m;
    int i;
    int flg=0;
    int min=0;
    result=new int[n];
    for(i=0;i<n;i++)
        result[i]=0;
    temp=new int[m+1];
    for(i=0;i<m+1;i++)
        temp[i]=0;
    while(compare(a,n,b,m))
    {
        while(a[start]==0)
        {
            start++;
            end++;
        }
        for(i=start;i<end;i++)
        {
            temp[i-start+1]=a[i];
        }
        if(!compare(temp,m+1,b,m))
        {
            end++;
            for(i=start;i<end;i++)
                temp[i-start]=a[i];
        }
        while(compare(temp,m+1,b,m))
        {
            flg=0;
            for(i=m;i>=1;i--)
            {
                min=temp[i]-b[i-1]-flg;
                if(min<0)
                {
                    min+=10;
                    flg=1;
                }

                else

                    flg=0;

                temp[i]=min;
            }
            temp[i]-=flg;
            result[end-1]++;
        }
        if(end-start==m)
        {
            for(i=start;i<end;i++)
            {
                a[i]=temp[i+1-start];
                temp[i+1-start]=0;
            }
        }
        else
        {
            for(i=start;i<end;i++)
            {
                a[i]=temp[i-start];
                temp[i-start]=0;
            }
        }
        start++;
        end=start+m;
    }
    for(i=0;i<n;i++)
        cout<<result[i];
    cout<<endl;
    delete result;
    delete temp;
}




  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值