大數計算(進階) 支援大浮點數的任意精度加減乘除
阿新 • • 發佈:2019-01-30
上一篇實現了大數加法,乘除法都是簡單的複用加法,這樣做時間複雜度高,精度低。
進階:
1.乘法模擬豎式計算方法 核心思路是num1[i]*num2[j]的結果一定對應乘積中的[i+j]位,並且考慮對[i+j+1]位的進位。
這樣的時間複雜度為O(m*n) 而簡單的複用加法的時間複雜度為 (q/p)*(n+m)其中q,p為大數的實際值,n,m為大數的位數,這樣當p很小的時候,時間複雜度會很大。進一步優化的方法是使用FFT即快速傅立葉變換。下一篇中進行升級。
Num by(Num& a,Num&b) { Num ret; int pos1=a._float.length(); int pos2=b._float.length(); string s1,s2,s(1000,'0'); s1=a._int+a._float; s2=b._int+b._float; reverse(s1.begin(),s1.end()); reverse(s2.begin(),s2.end()); for(int i=0;i<s1.length();i++) for(int j=0;j<s2.length();j++) { int temp=(s1[i]-'0')*(s2[j]-'0'); s[i+j+1]=s[i+j+1]-'0'+(s[i+j]-'0'+temp)/10+'0'; s[i+j]=(s[i+j]-'0'+temp)%10+'0'; } reverse(s.begin(),s.end()); ret._int=s; int pos=s.length()-pos1-pos2+1; ret._int.resize(pos); ret._int[pos-1]='\0'; int len=pos1+pos2; string::reverse_iterator rts=s.rbegin(); while(rts!=s.rend()&&len>0) { ret._float.insert(0,1,*rts); rts++; len--; } if(a._sign==b._sign) ret._sign=POSITIVE; else ret._sign=NEGATIVE; ret._int=ret.InputNum(ret._int).second._int; return ret; }
2.除法模擬豎式計算。為滿足浮點計算,引入新的標誌:精度。每一個大數物件都擁有一個進度,在運算除法時,返回的結果精度由被除數的精度,除數的小數位數共同決定。
核心演算法為大數除以整數。處理兩個浮點大數相除的時候,將兩個大數的小數位對齊到整數位,記錄對齊位數。將被除數的整數位額外對齊 10e精度 倍。計算結束後,取結果的0~精度-1為整數位,精度~end為小數位 處理符號位後返回計算結果。
Num except(Num& a,Num& b) { int len1=a._float.length(); int len2=b._float.length(); int _precision=a._pos; string aa=a._int+a._float; string bb=b._int+b._float; string tmp; string ans; Num cop1,cop2,ret; int ok=0; for(int i=0;i<len1;i++) { bb+='0'; } for(int i=0;i<len2+_precision;i++) { aa+='0'; } cop2._int=bb; for(int i=0;i<aa.size();i++) { tmp+=aa[i]; cop1._int=tmp; if(cop1>=cop2) { ok=1; int ex=(_exc(tmp,bb)); ans+=(ex+'0'); //char arr[3]={0}; //itoa(ex,arr,10); Num ttmp; ttmp._int+=ex+'0'; tmp=reduce(cop1,by(cop2,ttmp))._int; } else/* if(ok==1)*/ { ans+='0'; } } /*****************************核心演算法****事實上要處理除數是字串,以及提高運算精度等問題********** // for(int i=0;i<s.size();i++) // { // cmp=(cmp*10+s[i]-'0'); // if(cmp>=x) // { // ok=1; // ans+=(cmp/x+'0'); // cmp%=x; // } // else{ // ans+='0'; // } // } ******************************************************************************************/ string::iterator it=ans.begin(); int intlen=ans.length()-_precision; while(it!=ans.end()&&intlen>0) { ret._int+=*it; it++; intlen--; } string::reverse_iterator rt1=ans.rbegin(); while(_precision>0&&rt1!=ans.rend()) { ret._float.insert(0,1,*rt1); rt1++; _precision--; } while(_precision>0) { ret._float.insert(0,1,'0'); _precision--; } ret._int=InputNum(ret._int).second._int; return ret; } int _exc(string& a,string& b) { int len1=a.length(); int len2=b.length(); int ret=0; Num bb; Num tmp1,tmp2; bb._int=b; tmp1._int=a; tmp2._int=b; if(tmp1<tmp2) return 0; while(tmp1>=tmp2) { ret++; tmp2=tmp2.add(tmp2,bb); } return ret; }
完整程式碼
這樣就得到了支援四則運算,支出修改除法精度的一個類 (lfloat)#pragma once #include <string> #include <cstring> #include <string.h> #define POS 10 //精度 using namespace std; enum SIGN { POSITIVE, NEGATIVE }; typedef class Num { protected: SIGN _sign; string _int; string _float; int _pos;//精度 public: Num() :_sign(POSITIVE), _pos(POS) {} Num(Num& a) { _sign=a._sign; _int=a._int; _float=a._float; _pos=a._pos; } int precision(int x) { _pos=x; return _pos; } bool IsZero(Num& a) { string::iterator it1=a._int.begin(); string::iterator it2=a._float.begin(); while(it1!=a._int.end()) { if(*it1!='0') return false; it1++; } while(it2!=a._float.end()) { if(*it2!='0') return false; it2++; } return true; } bool IsZero() { string::iterator it1=_int.begin(); string::iterator it2=_float.begin(); while(it1!=_int.end()) { if(*it1!='0') return false; it1++; } while(it2!=_float.end()) { if(*it2!='0') return false; it2++; } return true; } bool operator>(Num& a) { if(IsZero(*this)&&IsZero(a)) return false; if(_sign==NEGATIVE&&a._sign==POSITIVE) return false; else if(_sign==POSITIVE&&a._sign==NEGATIVE) return true; if(_sign==POSITIVE&&a._sign==POSITIVE) { if(_int.length()>a._int.length()) return true; else if(_int.length()<a._int.length()) return false; else if(_int.length()==a._int.length()) { if(strcmp(_int.c_str(),a._int.c_str())<0) return false; else if(strcmp(_int.c_str(),a._int.c_str())>0) return true; else if(strcmp(_int.c_str(),a._int.c_str())==0) { string::iterator it1=_float.begin(); string::iterator it2=a._float.begin(); while(it1!=_float.end()&&it2!=a._float.end()) { if(*it1<*it2) return false; it1++; it2++; } while(it2!=a._float.end()) { return false; } while(it1!=_float.end()) { return true; } } return false; } } if(_sign==NEGATIVE&&a._sign==NEGATIVE) { if(_int.length()>a._int.length()) return false; else if(_int.length()<a._int.length()) return true; else if(_int.length()==a._int.length()) { if(strcmp(_int.c_str(),a._int.c_str())<0) return true; else if(strcmp(_int.c_str(),a._int.c_str())>0) return false; else if(strcmp(_int.c_str(),a._int.c_str())==0) { string::iterator it1=_float.begin(); string::iterator it2=a._float.begin(); while(it1!=_float.end()&&it2!=a._float.end()) { if(*it1<*it2) return true; else if(*it1>*it2) return false; it1++; it2++; } while(it1!=_float.end()) { return false; } while(it2!=a._float.end()) return true; } return false; } } return true; } bool operator==(Num& a) { if(IsZero(*this)&&IsZero(a)) return true; if(_sign==NEGATIVE&&a._sign==POSITIVE) return false; else if(_sign==POSITIVE&&a._sign==NEGATIVE) return false; if(_int.length()!=a._int.length()||_float.length()!=a._float.length()) return false; else if(_int.length()==a._int.length()&&strcmp((_int.c_str()),a._int.c_str())!=0) return false; else if(_float.length()==a._float.length()&&strcmp((_float.c_str()),a._float.c_str())!=0) return false; return true; } bool operator<(Num& a) { return !(*this>a||*this==a); } bool operator>=(Num& a) { return *this>a||*this==a; } bool operator<=(Num& a) { return *this<a||*this==a; } Num& operator=(Num a) { _float=a._float; _int=a._int; _sign=a._sign; return *this; } Num _abs() { Num ret(*this); ret._sign=POSITIVE; return ret; } Num& operator++() { Num one; one._int='1'; return *this=add(*this,one); } Num operator++(int) { Num ret=*this; Num one; one._int='1'; *this=add(*this,one); return ret; } Num& operator--() { Num one; one._int='1'; one._sign=NEGATIVE; return *this=add(*this,one); } Num operator--(int) { Num ret=*this; Num one; one._sign=NEGATIVE; one._int='1'; *this=add(*this,one); return ret; } Num operator+(Num& a) { return add(*this,a); } Num operator-(Num& a) { return reduce(*this,a); } Num operator*(Num& a) { return by(*this,a); } Num operator/(Num& a) { if(IsZero(a)) { perror("/ 0 "); exit(1); } return except(*this,a); } Num add(Num& a,Num& b) { Num ret; int flag=0;//進位符 if(a.IsZero()) return b; if(b.IsZero()) return a; if(a._sign==b._sign)//同號相加 { ret._sign=a._sign; int len1=a._float.length(); int len2=b._float.length(); string::reverse_iterator rt1=a._float.rbegin(); string::reverse_iterator rt2=b._float.rbegin(); while(len1>len2)//小數部分 { len1--; ret._float.insert(0,1,*rt1); rt1++; } while(len2>len1) { len2--; ret._float.insert(0,1,*rt2); rt2++; } while(len1==len2&&rt1!=a._float.rend()&&rt2!=b._float.rend()) { int tmp=(*rt1-'0')+(*rt2-'0')+flag; flag=tmp/10; tmp=tmp%10; ret._float.insert(0,1,tmp+'0'); rt2++; rt1++; len1--; len2--; } rt1=a._int.rbegin(); rt2=b._int.rbegin(); while(rt1!=a._int.rend()&&rt2!=b._int.rend()) { int tmp=(*rt1-'0')+(*rt2-'0')+flag; flag=tmp/10; tmp=tmp%10; ret._int.insert(0,1,tmp+'0'); rt2++; rt1++; } while(rt1!=a._int.rend()) { int tmp=(*rt1-'0')+flag; flag=tmp/10; tmp=tmp%10; ret._int.insert(0,1,tmp+'0'); rt1++; } while(rt2!=b._int.rend()) { int tmp=(*rt2-'0')+flag; flag=tmp/10; tmp=tmp%10; ret._int.insert(0,1,tmp+'0'); rt2++; } if(flag) ret._int.insert(0,1,'1'); } if(a._sign!=b._sign) //異號相加 { Num aa=a,bb=b; if(a._abs()<b._abs()) { swap(aa,bb); } else if(a._abs()==b._abs()) return ret; //先計算小數位 string::reverse_iterator rt1=aa._float.rbegin();//a>b string::reverse_iterator rt2=bb._float.rbegin(); int len1=aa._float.length(); int len2=bb._float.length(); while(len1>len2) { ret._float.insert(0,1,*rt1); len1--; rt1++; } while(len1<len2) { int tmp=10-(flag+*rt2-'0'); flag=1; rt1++; len2--; } while(len1==len2&&rt1!=aa._float.rend()&&rt2!=bb._float.rend()) { int tmp=(*rt1-'0')-(flag+*rt2-'0'); if(tmp<0) { flag=1; tmp+=10; } else if(tmp>0) { flag=0; } ret._float.insert(0,1,tmp+'0'); rt1++; rt2++; len1--; len2--; } rt1=aa._int.rbegin();//計算整數部分 rt2=bb._int.rbegin(); while(rt1!=aa._int.rend()&&rt2!=bb._int.rend()) { int tmp=(*rt1-'0')-(flag+*rt2-'0'); if(tmp<0) { flag=1; tmp+=10; } else if(tmp>=0) { flag=0; } ret._int.insert(0,1,tmp+'0'); rt1++; rt2++; } while(rt1!=aa._int.rend()) { int tmp=*rt1-flag-'0'; if(tmp<0) { flag=1; tmp+=10; } else if(tmp>=0) { flag=0; } ret._int.insert(0,1,tmp+'0'); rt1++; } ret=InputNum(ret._int+'.'+ret._float).second; } if(a._abs()>b._abs()) //處理符號位 ret._sign=a._sign; else if(a._abs()<b._abs()) { ret._sign=b._sign; } return ret; } Num reduce(Num& a,Num& b) { Num tb(b); if(a._sign==b._sign&&a._sign==POSITIVE) { tb._sign=NEGATIVE; return add(a,tb); } if(a._sign==b._sign&&a._sign==NEGATIVE) { tb._sign=POSITIVE; return add(a,tb); } } Num by(Num& a,Num&b) { Num ret; int pos1=a._float.length(); int pos2=b._float.length(); string s1,s2,s(1000,'0'); s1=a._int+a._float; s2=b._int+b._float; reverse(s1.begin(),s1.end()); reverse(s2.begin(),s2.end()); for(int i=0;i<s1.length();i++) for(int j=0;j<s2.length();j++) { int temp=(s1[i]-'0')*(s2[j]-'0'); s[i+j+1]=s[i+j+1]-'0'+(s[i+j]-'0'+temp)/10+'0'; s[i+j]=(s[i+j]-'0'+temp)%10+'0'; } reverse(s.begin(),s.end()); ret._int=s; int pos=s.length()-pos1-pos2+1; ret._int.resize(pos); ret._int[pos-1]='\0'; int len=pos1+pos2; string::reverse_iterator rts=s.rbegin(); while(rts!=s.rend()&&len>0) { ret._float.insert(0,1,*rts); rts++; len--; } if(a._sign==b._sign) ret._sign=POSITIVE; else ret._sign=NEGATIVE; ret._int=ret.InputNum(ret._int).second._int; return ret; } Num except(Num& a,Num& b) { int len1=a._float.length(); int len2=b._float.length(); int _precision=a._pos; string aa=a._int+a._float; string bb=b._int+b._float; string tmp; string ans; Num cop1,cop2,ret; int ok=0; for(int i=0;i<len1;i++) { bb+='0'; } for(int i=0;i<len2+_precision;i++) { aa+='0'; } cop2._int=bb; for(int i=0;i<aa.size();i++) { tmp+=aa[i]; cop1._int=tmp; if(cop1>=cop2) { ok=1; int ex=(_exc(tmp,bb)); ans+=(ex+'0'); //char arr[3]={0}; //itoa(ex,arr,10); Num ttmp; ttmp._int+=ex+'0'; tmp=reduce(cop1,by(cop2,ttmp))._int; } else/* if(ok==1)*/ { ans+='0'; } } string::iterator it=ans.begin(); int intlen=ans.length()-_precision; while(it!=ans.end()&&intlen>0) { ret._int+=*it; it++; intlen--; } string::reverse_iterator rt1=ans.rbegin(); while(_precision>0&&rt1!=ans.rend()) { ret._float.insert(0,1,*rt1); rt1++; _precision--; } while(_precision>0) { ret._float.insert(0,1,'0'); _precision--; } ret._int=InputNum(ret._int).second._int; return ret; } int _exc(string& a,string& b) { int len1=a.length(); int len2=b.length(); int ret=0; Num bb; Num tmp1,tmp2; bb._int=b; tmp1._int=a; tmp2._int=b; if(tmp1<tmp2) return 0; while(tmp1>=tmp2) { ret++; tmp2=tmp2.add(tmp2,bb); } return ret; } pair<bool,Num> InputNum(string& a) { string tmp; int point=0; int head=1; int flag=1; Num ret; string::iterator it=a.begin(); if(a.empty()) { return make_pair(true,ret); } while(it!=a.end()) //123 +123 -123 .123 0.123 0000.000123 +-= { if(*it=='-'&&head==1&&flag==1) { ret._sign=NEGATIVE; flag=0; it++; continue; } else if(*it=='+'&&head==1&&flag==1) { it++; flag=0; continue; } else if((*it=='+'||*it=='_')&&flag==0) { return make_pair(false,ret); } if(*it=='0'&&point==0&&head==1) { it++; continue; } if(*it=='.'&&point==0) { point=1; head=0; it++; continue; } else if(*it=='.'&&point==1) { return make_pair(false,ret); } if(*it>'9'||*it<'0') { return make_pair(false,ret); } if(*it<='9'&&*it>='0') { head=0; if(point==0) { ret._int+=*it; it++; } else if(point==1) { ret._float+=*it; it++; } } } return make_pair(true,ret); } friend ostream& operator<<(ostream& cout,Num& a); }lfloat; ostream& operator<<(ostream& cout,Num& a) { if(a.IsZero(a)) { cout<<"0"; return cout; } if(a._int.empty()&&a._sign==NEGATIVE) cout<<"-0"<<'.'<<a._float; else if(a._int.empty()&&a._sign==POSITIVE) cout<<"0"<<'.'<<a._float; else if(a._sign==NEGATIVE) cout<<'-'<<a._int<<'.'<<a._float; else cout<<a._int<<'.'<<a._float; return cout; }
下一篇優化乘法,縮減冗餘程式碼,並實現運算表示式計算。