1. 程式人生 > >大整數運算C++1

大整數運算C++1

關於 聲明 inpu 以及 sig opera code ring 相加

//下面的代碼勉強算是bignum_beta1版本!
//實現了大整數的加減乘除四則運算,以及求兩個整數的最大公約數,以及求乘法逆,miller_rabin素性檢驗,平方_乘法算法
//不足之處,位數還很難擴展至幾千位,以及運算速度有一點慢,既然是beta1,說明bug還是挺多的
//程序缺少測試數據來測試,所以有的結果不敢保證其正確性
//由於使用c++復寫了很多運算符,加入這個文件之後,大數bignum可以看做是一個如同如同int一樣的基本類型
//可以像int一樣加減乘除和輸入輸出

#include<iostream>
#include<string>
#include<ctime>//
用於產生隨機數 using namespace std; const int base=1000;//base用來表示數組中每個數的進制,逢base向前一位進1 const int MAX_LEN=300;//數組的最大長度 class bigNum{ public: int num[MAX_LEN]; int len; int flag;//增設一個標誌,表示正負,這樣大數包就可以擴展置負數 friend istream& operator>>(istream& input,bigNum &obj); friend ostream& operator
<<(ostream& output,bigNum& obj); bigNum &operator=(const bigNum &s);//對於"="號的重載 //類的賦值運算符"="只能重載為成員函數,而不能把它重載為友元函數 bigNum();//構造函數 void eucli_setnum(int x);//設置數值 }; void bigNum::eucli_setnum(int x)//設置這個函數主要應對擴展的歐幾裏德算法 { num[0]=x; if(x!=0) len=1; else len=0
; } bigNum::bigNum()//構造函數 { memset(num,0,sizeof(num));//清零 len=0; flag=1;//默認的數為正數 } //關於下面的運算符重載函數,有一點需要特別記住,那就是len一定要記得更新,不然會出錯! //以下的幾個函數都是邏輯運算符的重載函數 bool operator==(bigNum &a,bigNum &b)//"=="號的重載 { for(int i=MAX_LEN-1;i>=0;i--) if(a.num[i]!=b.num[i]) return false; return true; } bool operator!=(bigNum &a,bigNum &b)//"!="號的重載 { for(int i=0;i<MAX_LEN;i++) if(a.num[i]!=b.num[i]) return true;//只要有一個不相等,就返回true return false; } /* bool operator!=(bigNum &a,int &b)//"!="號的重載 { if(a.num[0]!=b) return false;//只要有一個不相等,就返回true for(int i=1;i<MAX_LEN-1;i++) if(a.num[i]!=0) return false; return true; }*/ bool operator>(bigNum &a,bigNum &b)//">"號的重載 { for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索 if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小 if(a.num[i]>b.num[i]) return true; else return false; return false;//兩個數相同也返回false } bool operator<(bigNum &a,bigNum &b)//"<"號的重載 { for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索 { if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小 if(a.num[i]<b.num[i]) return true; else return false; } return false; } bool operator<=(bigNum &a,bigNum &b) { for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索 if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小 if(a.num[i]<b.num[i]) return true; else return false; return true;//最後相等返回true } bool operator>=(bigNum &a,bigNum &b) { for(int i=MAX_LEN-1;i>=0;i--)//從最高位向下搜索 if(a.num[i]!=b.num[i])//如果有兩個數不相等,必定有一大一小 if(a.num[i]>b.num[i]) return true; else return false; return true;//最後相等返回true } bigNum &bigNum::operator=(const bigNum &s)//"="號的重載 { if(this==&s) return *this;//防止s=s for(int i=0;i<MAX_LEN;i++) num[i]=s.num[i]; len=s.len; flag=s.flag; } //以下幾個函數是四則運算符的重載函數 bigNum operator-(bigNum a,bigNum b);//聲明,防止編譯出錯 bigNum operator+(bigNum a,bigNum b)//加法的重載 { bigNum sum;//存儲結果 int i; if(a.flag<0 && b.flag>0)//a為負,b為正,則a+b=b-|a| { a.flag=1;//這裏對a進行了修改(將a變為正數),以便於進行減法運算,這也是重寫不用引用的reason sum=b-a; if(b>a) sum.flag=1;//結果為正 else sum.flag=-1;//結果為負 return sum; } if(a.flag>0 && b.flag<0)//a為正,b為負,則b+a=a-|b| { b.flag=1; sum=a-b; if(a>b) sum.flag=1;//結果為正 else sum.flag=-1;//結果為負 return sum; } //余下的情況是a,b兩者符號相同,即a+b=(|a|+|b|)*flag,flag與a,b符號一致 for(i=0;i<MAX_LEN;i++) { sum.num[i]+=a.num[i]+b.num[i]; if(sum.num[i]>base)//超出base,則要進位 { sum.num[i]-=base; sum.num[i+1]++; } if(sum.num[i]!=0) sum.len=i+1;//len要同步更新 } sum.flag=a.flag;//如果a,b不是一正一負,那麽a,b必定同號 return sum; } bigNum operator-(bigNum a,bigNum b)//減法的重載 { bigNum sum;//存儲結果 if(a.flag<0 && b.flag>0)//a為負,b為正,則a-b=-(|a|+|b|) { a.flag=1; sum=b+a; sum.flag=-1;//兩個負數相加,結果一定為負數 return sum; } if(a.flag>0 && b.flag<0 && a>b)//a為正,b為負,則a-b=|a|+|b| { b.flag=1; sum=b+a; sum.flag=1;//兩個正數相加,結果一定為正數 return sum; } //下面a,b的符號值一致 if(a<b)//a<b,則|a|-|b|<0,轉化為-(|b|-|a|) { sum=b-a; sum.flag=-b.flag; return sum; } //下面表示的就是|a|>|b|,且a,b同號 for(int i=0;i<MAX_LEN;i++) { a.num[i]-=b.num[i]; if(a.num[i]<0)//不夠減時向前借位 { a.num[i]+=base; a.num[i+1]--; } if(a.num[i]!=0) a.len=i+1;//len要同步更新 } return a; } bigNum operator*(bigNum &a,bigNum &b)//對於乘法的重載 {//乘法的flag已經設置完畢 bigNum sum; int i,j; for(i=0;i<b.len;i++)//用第二個數b乘以第一個數a { for(j=0;j<a.len;j++) sum.num[i+j]+=b.num[i]*a.num[j];//先乘起來,後面統一進位 } for(i=0;i<MAX_LEN;i++)//循環統一處理進位問題 { if(sum.num[i]>=base) { sum.num[i+1]+=sum.num[i]/base; sum.num[i]%=base; } if(sum.num[i]!=0) sum.len=i+1;//len要同步更新 } //現在設置數的正負 if(a.flag+b.flag==0) sum.flag=-1; else sum.flag=a.flag; return sum; } int substract(int *p1,int *p2,int n1,int n2) { int i; //被除數不能小於除數 if(n1<n2) return -1;//p2數的長度不能大於p1數的長度 if(n1==n2)//兩數長度一致情況下(所占用數組長度),p2數要小於p1數 { for(i=n1-1;i>=0;i--) { if(p1[i]>p2[i]) break; else if(p1[i]<p2[i]) return -1; } } for(i=0;i<n1;i++) {//減去一個p2值 p1[i]-=p2[i]; if(p1[i]<0) { p1[i]+=base; p1[i+1]--; } } for(i=n1-1;i>=0;i--) if(p1[i]) return i+1;//返回所占用的數組長度 return 0; } bigNum operator/(bigNum a,bigNum b)//除法的重載 {//除法的flag設置完畢 bigNum sum; int i,j; if(a<b)//a<b時返回0 return sum; int nTimes=a.len-b.len; if(nTimes>0) { for(i=a.len-1;i>=nTimes;i--) b.num[i]=b.num[i-nTimes];//朝高位移動 for(;i>=0;i--) b.num[i]=0;//低位補0 b.len=a.len; } for(j=0;j<=nTimes;j++) { int nTmp; //一直減到不夠減為止 while((nTmp=substract(a.num,b.num+j,a.len,b.len-j))>=0) { a.len=nTmp; sum.num[nTimes-j]++;//每減成功一次,則將商的對應為加1 } if(sum.len==0 && sum.num[nTimes-j]!=0) sum.len=nTimes-j+1;//同步更新len } //現在設置數的正負 if(a.flag+b.flag==0) sum.flag=-1; else sum.flag=a.flag; return sum; } bigNum operator%(bigNum &a,bigNum &b)//取模運算的重載 { return a-b*(a/b); } istream& operator>>(istream& input,bigNum& obj)//重載輸入函數 {//輸入flag已經設置完畢 string str; input>>str; int l=str.size();//l為字符串長度 int i,k,j; for(j=0,i=base;i!=1;) if(i>0) { j++; i=i/10; }//j用來表示base的位數 int p=l/j,q=l%j;//輸入的數按照每個可以存放j個的標準,恰好放進,一共占用p個位置 if(q) obj.len=p+1;//當然,不一定恰好放進,就需要p+1個位置來放 else obj.len=p; if(str[0]==-)//輸入為負數 obj.flag=-1; else obj.flag=1;//設置符號位,正數則flag為1,否則為-1 for(i=0;i<q;i++)//用來存放不能整除的高位部分 { if(str[i]==-) i++;//如果是負數的話,第一位不用處理 obj.num[p]=obj.num[p]*10+str[i]-0; } p--; for(;p>=0;p--)//下面的字符,以j為一組,字符個數恰好能夠被j整除,一組組存入num數組裏 { for(k=1;k<=j;k++) { obj.num[p]=obj.num[p]*10+str[i]-0; i++; } } return input; } ostream& operator<<(ostream& output,bigNum& obj) {//輸出flag就已經設置好了 int i; for(i=MAX_LEN-1; (i>=0)&&(obj.num[i]==0);i--); if(i>=0) { if(obj.flag==-1) output<<-; for(;i>=0;i--) output<<obj.num[i]; } else output<<0;//整個數組都是0 return output; } bigNum extended_euclidean(bigNum n,bigNum m,bigNum &x,bigNum &y)//擴展的歐幾裏德算法的另一種形式 { bigNum x1, x2, x3=n; x1.eucli_setnum(1); x2.eucli_setnum(0); bigNum y1, y2, y3=m; y1.eucli_setnum(0); y2.eucli_setnum(1); bigNum zero; while(x3%y3!=zero) { bigNum d=x3/y3; bigNum t1,t2,t3; t1=x1-d*y1; t2=x2-d*y2; t3=x3-d*y3; x1=y1; x2=y2; x3=y3; y1=t1; y2=t2; y3=t3; } x=y1; y=y2; return y3; } bigNum gcd(bigNum &n,bigNum &m)//求兩個大數的最大公約數 { bigNum x,y; return extended_euclidean(n,m,x,y); } //求乘法逆其實也沒有特別好的算法,主要還是依靠歐幾裏德算法 bigNum mutirinverse(bigNum &n,bigNum &m)//求乘法逆 { bigNum x,y; extended_euclidean(m,n%m,x,y); return x; } //平方——乘法算法 bigNum Square_and_Mutiply(bigNum a,bigNum m,bigNum n) { bigNum sum,zero,two; two.eucli_setnum(2); sum.eucli_setnum(1); int length=1; int bin[300]; //先將m轉化為二進制 do { sum=m%two; bin[length++]=sum.num[0]; m=m/two; }while(m!=zero); sum.eucli_setnum(1); while(length>=0) { sum=(sum*sum)%n; if(bin[length]==1) { sum=(sum*a)%n; } length--; } return sum; } //最後一個函數,用於素數判定的Miller-Rabin算法 bool wintess(bigNum a,bigNum n) { bigNum m,x,y,one,two,zero; one.eucli_setnum(1);two.eucli_setnum(2); bigNum i,j; m=n-one; while(m%two==zero) { m=m/two; j=j+one; } x=Square_and_Mutiply(a,m,n); for(i.eucli_setnum(1);i<=j;i=i+one) { y=Square_and_Mutiply(x,two,n); if((y==one)&&(x!=one)&&(x!=n-one)) return true; x=y; } if(y!=one) return true; return false; } bool Miller_Robin(int times,bigNum &n) //n為大於3的奇數,輸出n是否通過素性檢驗 { bigNum a,one,two,random; one.eucli_setnum(1);two.eucli_setnum(2); if(n==one) return false; if(n==two) return true; srand((unsigned)time(0)); for(int i=1;i<=times;i++) { random.eucli_setnum(rand()); a=random%(n-two)+two; if(wintess(a,n)) return false; } return false; } int main() { bigNum a,b; while(1) { cin>>a; cin>>b; cout<<a*b<<endl; } system("pause"); return 0; }

byte傳輸的最小單位

1bit =8 byte;

密碼學算法最重要的就是大整數的運算和字符的裝換

大整數運算C++1