#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; }
大数除法应该是最难实现的了,假设数组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;
}