運籌學與最優化理論基礎——高精度加減乘除(C++實現)
概要
這是本學期運籌學和最優化理論課的第一次作業。導師要求是實現含分數的高精度加減乘除運算,不能含有浮點數,這樣會造成計算誤差。為了實現分數的高精度加減乘除運算,我們首先必須實現整數的高精度加減乘除運算,之後將分數運算轉化成分子和分母相關的高精度計算。所有程式碼上傳自己的github,如有需要請移步:高精度計算
整數高精度加法
整數高精度加法運算演算法如下:
1 若str1為0,直接返回str2,若str2為0,直接返回str1。
2 否則初始化標誌位sign=1,來記錄兩個數中是否存在負數,初始化兩個數str1和str2,這兩個數用字串表示。
3 若str1和str2都為負數時,sign=-1,表示結果應該為負數。之後去掉str1和str2的最前面的負號得到兩個正數,之後計算兩個正數加法的結果。
4 若str1為正,str2為負,將str2的最前面的負號去掉,轉化為str1與-str2之間減法,即呼叫整數高精度減法函式。
5 若str1為負,str2為正,將str1的最前面的負號去掉,轉化為str2與-str1之間減法,即呼叫整數高精度減法函式。
6 若str1和str2都為正數,首先比較str1和str2字串長度大小,較短的字串在前面新增字串長度之差的個數的0,以用來對齊。之後初始化int1=int2=0,結果字串str為空,int1用來記錄每一位的對應加法結果,int2記錄進位。將str1和str2從後往前遍歷,int1=(int(str1[i])-‘0’+int(str2[i])-‘0’+int2)%10,即int1保留對應位加法的個位結果,int2=(int(str1[i])-‘0’+int(str2[i])-‘0’+int2)/10,即int2保留對應位加法十位結果,str=char(int1+‘0’)+str。結束遍歷之後,若int不為零,說明仍然有進位,那麼直接將int2追加到結果字串的最前面。
7 最後判斷sign是否為-1,若為-1,且str不為0,那麼必須在結果字串str前加上負號。
部分程式碼如下:
//整數加法 string ADD_INT(string str1,string str2) { if(str1.compare("0") == 0){ return str2; } if(str2.compare("0") == 0){ return str1; } //高精度加法 int sign=1; //sign 為符號位 string str; if(str1[0]=='-'){ //str1為負數 if (str2[0]=='-'){ //str2為負數 sign=-1; //-1代表兩個負數相加 // 去掉兩個符號後相加 str=ADD_INT(str1.erase(0,1),str2.erase(0,1)); }else{//str2為正數 //去掉str1的符號,轉化為減法 str=SUB_INT(str2,str1.erase(0,1)); } }else{ // str1為正數 if(str2[0]=='-'){ //str2為負數 // 去掉str2的負數,轉化為減法 str=SUB_INT(str1,str2.erase(0,1)); }else{ //str2為正數 //把兩個整數對齊,短整數前面加0補齊 string::size_type L1,L2; int i; L1=str1.size(); L2=str2.size(); if(L1<L2){ // str1的長度比str2小,str1在前補充0 for(i=1;i<=L2-L1;i++){ str1="0"+str1; } }else{ // str2的長度比str2小,str2在前面補充0 for(i=1;i<=L1-L2;i++){ str2="0"+str2; } } int int1=0,int2=0; //int2 記錄進位,int1記錄結果 for(i=str1.size()-1;i>=0;i--){ int1=(int(str1[i])-'0'+int(str2[i])-'0'+int2)%10; int2=(int(str1[i])-'0'+int(str2[i])-'0'+int2)/10; str=char(int1+'0')+str; } if(int2!=0){ //計算結束後還有進位,直接在前面補充 str=char(int2+'0')+str; } } } //運算後處理符號位 if((sign==-1)&&(str[0]!='0')){ str="-"+str; } return str; }
整數高精度減法
整數高精度減法運算演算法如下:
1 若str1為0,那麼若str2為0直接返回0,若身直接返回str2,若str2不為0,返回str2的相反數。若str2為0,直接返回str1。
2 否則初始化標誌位sign=1,來記錄兩個數中是否存在負數,初始化兩個數str1和str2,str1為被減數,str2為減數。這兩個數用字串表示。
3 若str2為負數,去掉str2的最前面的負號,將str1與str2之間的減法轉化為str1與str2的相反數之間的加法得到結果str。
4 若str1為負,str2為正,將轉化為str2與str1相反數之間加法,得到其結果str,之後將str加上符號就是我們想要的結果。
5 若str1和str2都為正,首先判斷str1和str2兩個字串的大小,若相等則直接返回字串“0”,若str1小於str2,那麼sign=-1,並將str1與str2互換tempint
=str1.size()-str2.size(),tempint記錄連個字串長度之差。之後值較小的字串(str2)進行反向遍歷,str1[i+tempint]<str2[i]時,j=1利用j記錄偏移位,之後開始迴圈,若str1[i+tempint-j]==‘0’,說明第i位之前j位的數字為0,需要進行借位,借位之後需要將該位置成“9”,即str1[i+tempint-j]=‘9’,否則str1[i+tempint-j]=char(int(str1[i+tempint-j])-1),並在此中斷迴圈。之後在結果字串前面加上char(str1[i+tempint]-str2[i]+’:’),即str=char(str1[i+tempint]-str2[i]+’:’)+str。若str1[i+tempint]>=str2[i],那麼對應相減即可,即str=char(str1[i+tempint]-str2[i]+‘0’)+str。遍歷結束後,若有多餘位,則直接追加到str前面。
6 最後將結果str的最前面可能存在在0給去掉,之後判斷sign是否為-1,若為將str前加上負號。
部分程式碼:
//整數減法
string SUB_INT(string str1,string str2)
{
//高精度減法
int sign=1; //sign為符號位
string str;
int i,j;
if(str2[0]=='-'){ //str2為負數
// 去掉str2的符號,轉化為加法
str=ADD_INT(str1,str2.erase(0,1));
}else{ // str2為正數
if(str1[0] == '-'){ //str1為負數,轉化為加法
str = ADD_INT(str1.erase(0,1),str2);
str = "-"+str;
}else{ //str1為正數
// 判斷str1和str2大小
int res=compare(str1,str2);
//cout<<"==="<<res<<endl;
if(res==0){ //str1和str2相等
return "0";
}
if(res<0){ //str1小於str2
sign=-1; //標誌位置為-1
// str1和str2互換
string temp =str1;
str1=str2;
str2=temp;
}
string::size_type tempint;
tempint=str1.size()-str2.size();
// 按值較小者的進行遍歷,tempint=0說明長度相等,大於0則str1長度大於str2
// 從最後一位開始遍歷
for(i=str2.size()-1;i>=0;i--){
// str1的對應為小於str2的對應位
if(str1[i+tempint]<str2[i]){
j=1; //偏移位
//開始迴圈,開始尋找借位
while(1){
//前面的位為0,借1置成9
if(str1[i+tempint-j]=='0'){
str1[i+tempint-j]='9';
j++;
}else{//前面的位為0,借1,當前位減1
str1[i+tempint-j]=char(int(str1[i+tempint-j])-1);
break;
}
}
str=char(str1[i+tempint]-str2[i]+':')+str;
}else{
str=char(str1[i+tempint]-str2[i]+'0')+str;
}
}
//把多餘為進行向前補充
for(i=tempint-1;i>=0;i--){
str=str1[i]+str;
}
}
}
//去除結果中多餘的前導0
str.erase(0,str.find_first_not_of('0'));
if(str.empty()){
str="0";
}
if((sign==-1) && (str[0]!='0')){
str ="-"+str;
}
return str;
}
整數高精度乘法
整數高精度乘法運算演算法如下:
1 初始化標誌位sign=1,來記錄兩個數中是否存在負數,初始化兩個數str1和str2。這兩個數用字串表示。
2 若str1為負數,去掉str1的最前面的負號,sign = -1。即有一個是負數,乘積為負數。
3 若str2為負數,去掉str2的最前面的負號,sign=-1。即兩個負數乘積為正數。
4 * str保初始化為空,儲存兩個數的乘積結果。根據str2進行方向遍歷,tempstr儲存每次迴圈的計算記過,int1=0,int2=0,i=L2-1,int3=int(str2[i])-‘0’,判斷int3是否為0,若不為0,那麼在tempstr前端補0,個數為(L2-1-i)。之後對str1進行反向遍歷,int1=(int3*(int(str1[j])-‘0’)+int2)%10,int2=(int3*(int(str1[j])-‘0’)+int2)/10,即int1記錄個位數,int記錄十位數。tempstr=char(int1+‘0’)+tempstr,即將個位數追加到tempstr前面。對str1遍歷結束後,若int2不為0,那麼必須將int2追加到tempstr前面。之後將str和tempst進行相加。*
5 str2遍歷結束後,去除結果中最前面連續的0。之後若str為空,那麼str=“0”,若sign=-1,且str不為0,那麼str前加上負號。
部分程式碼如下:
//整數乘法
string MUL_INT(string str1,string str2)
{
//str1和str2至少存在1個0,直接返回0
if(str1.compare("0") == 0 || str2.compare("0") == 0){
return "0";
}
//str1為1直接返回str2
if(str1.compare("1") == 0){
return str2;
}
//str2為1直接返回str1
if(str2.compare("1") == 0){
return str1;
}
//str1為-1
if(str1.compare("-1") == 0){
//str2為負數
if(str2.compare("0") < 0){
return str2.erase(0,1);
}
//str2為正數
return "-"+str2;
}
//str2為1
if(str2.compare("-1") == 0){
//str1為負數
if(str1.compare("0") < 0){
return str1.erase(0,1);
}
//str1為正數
return "-"+str1;
}
//高精度乘法
int sign=1; //sign 為符號位
string str;
// str1為負數
if(str1[0]=='-'){
sign*=-1; // 標誌位乘-1
str1 =str1.erase(0,1); // 去掉負號
}
// str2為負數
if(str2[0]=='-'){
sign*=-1; // 標誌位乘-1
str2 =str2.erase(0,1); // 去掉負號
}
int i,j;
string::size_type L1,L2;
L1=str1.size();
L2=str2.size();
for(i=L2-1;i>=0;i--){ //模擬手工乘法豎式
string tempstr;
int int1=0,int2=0,int3=int(str2[i])-'0';
if(int3!=0){
// 下面的迴圈實現結果後端補0操作
for(j=1;j<=(int)(L2-1-i);j++){
tempstr="0"+tempstr;
}
for(j=L1-1;j>=0;j--){
// int1記錄個位數,int記錄十位數
int1=(int3*(int(str1[j])-'0')+int2)%10;
int2=(int3*(int(str1[j])-'0')+int2)/10;
tempstr=char(int1+'0')+tempstr;
}
if(int2!=0){
tempstr=char(int2+'0')+tempstr;
}
}
// 當前結果和tempstr向將
str=ADD_INT(str,tempstr);
}
//去除結果中的前導0
str.erase(0,str.find_first_not_of('0'));
if(str.empty()){
str="0";
}
if((sign==-1) && (str[0]!='0')){
str="-"+str;
}
return str;
}
整數高精度除法
整數高精度除法運算演算法如下:
1 quotient,residue分別記錄商和餘數。若str2為0時,那麼quotient,residue置為“ERROR”,若str1為“0”時,quotient,residue置為1,若str2為“1”,quotient=str1,residue=“0”, str2為“-1”時,str1若位負數,quotient=str1的相反數,若為正數,quotient = “-”+str1,不管str1為正數還是負數,residue = “0”。
2 否則,初始化標誌位sign1=1,sign2=1,初始化兩個數str1和str2。這兩個數用字串表示。
3 若str1為負數,那麼直接返回去掉str1的最前面的負號,sign1 = -1,sign2 = -1,即一正一負的的商和餘數都為負數
4 若str2為負數,去掉str2的最前面的負號,sign1 = -1,sign2 = -1。即兩個負數的商和餘數為正數。
5 比較str1和str2的大小,若str1小於str2,那麼quotient=“0”,residue =str1
6 若str1等於str2,quotient=“1”,residue =“0”。
7 若str1大於str2,L1=str1.size(),L2=str2.size(),tempstr儲存當前結果,從L2-1遍歷到L1,tempstr=tempstr+str1[i],之後去掉前導0,若tempstr為空,那麼tempstr = “0”。反之,從ch=9一直遍歷到0進行試商,找到之後停止試商quotient=quotient+ch,tempstr =SUB_INT(tempstr,MUL_INT(str2,str));。迴圈結束後,residue=tempstr;
8 之後去除商的前導0。判斷sign1,sign2是否為-1,若為,那麼將商和餘數加上負號。返回商和餘數
str保初始化為空,儲存兩個數的乘積結果。根據str2進行方向遍歷,tempstr儲存每次迴圈的計算記過,int1=0,int2=0,i=L2-1,int3=int(str2[i])-‘0’,判斷int3是否為0,若不為0,那麼在tempstr前端補0,個數為(L2-1-i)。之後對str1進行反向遍歷,int1=(int3(int(str1[j])-‘0’)+int2)%10,int2=(int3(int(str1[j])-‘0’)+int2)/10,即int1記錄個位數,int記錄十位數。tempstr=char(int1+‘0’)+tempstr,即將個位數追加到tempstr前面。對str1遍歷結束後,若int2不為0,那麼必須將int2追加到tempstr前面。之後將str和tempst進行相加。
部分程式碼如下:
//整數除法
string DIVIDE_INT(string str1,string str2,int flag)
{
//高精度除法。flag==1時,返回商; flag==0時,返回餘數
string quotient,residue; //定義商和餘數
int sign1=1,sign2=1;
//判斷除數是否為0
if(str2 == "0"){
quotient= "ERROR!";
residue = "ERROR!";
if(flag==1){
return quotient;
}else{
return residue;
}
}
//判斷被除數是否為0
if(str1=="0"){
quotient="0";
residue ="0";
}
//被除數為1,直接返回str1
if(str2 == "1"){
quotient = str1;
residue = "0";
}
//被除數為-1
if(str2 == "-1"){
if(str1.compare("0") < 0){
quotient = str1.erase(0,1);
}else{
quotient = "-"+str1;
}
residue = "0";
}
// str1為負數
if(str1[0]=='-'){
str1 = str1.erase(0,1); //去除前導0
sign1 *= -1;
sign2 = -1;
}
// str2為負數
if(str2[0]=='-'){
str2 = str2.erase(0,1); //去除前導0
sign1 *= -1;
}
//比較str1和str2的大小
int res=compare(str1,str2);
// str1小於str2
if(res<0){
// 商為0,餘數為str1
quotient="0";
residue =str1;
}else if(res == 0){
// str1與str2相等,商為1,餘數為0
quotient="1";
residue ="0";
}else{
// str1大於str2
string::size_type L1,L2;
L1=str1.size();
L2=str2.size();
string tempstr;
tempstr.append(str1,0,L2-1);
for(int i=L2-1;i<L1;i++){ //模擬手工除法豎式
tempstr=tempstr+str1[i];
//可能出現不夠除的情況,那麼必須把前導0去掉
tempstr.erase(0,tempstr.find_first_not_of('0'));
if(tempstr.empty()){
tempstr="0";
}
for(char ch='9';ch>='0';ch--){ //試商
string str;
str=str+ch;
if(compare(MUL_INT(str2,str),tempstr)<=0){
quotient=quotient+ch;
tempstr =SUB_INT(tempstr,MUL_INT(str2,str));
break;
}
}
}
residue=tempstr;
}
//去除結果中的前導0
quotient.erase(0,quotient.find_first_not_of('0'));
if(quotient.empty()){
quotient="0";
}
if((sign1==-1)&&(quotient[0]!='0')){
quotient="-"+quotient;
}
if((sign2==-1)&&(residue [0]!='0')){
residue ="-"+residue ;
}
if(flag==1){
return quotient;
}else{
return residue;
}
}
//整數除法,返回商
string DIV_INT(string str1,string str2
{
//高精度除法,返回商
return DIVIDE_INT(str1,str2,1);
}
//整肅除法,返回餘數
string MOD_INT(string str1,string str2)
{
//高精度除法,返回餘數
return DIVIDE_INT(str1,str2,0);
}
兩個整數的最大公約數
兩個整數的最大公約數演算法流程:
1 初始化sign=1,標誌其中兩個數是否有負數,兩個數用str1和str2表示,字串儲存
2 若str1為負數,sign*=-1,即一正一負的最大公約數為負數
3 若str2為負數,sign*=-1,即兩個負數的最大公約數為正數
4 若str1和str2至少有一個為1,判斷sign是否為-1,若是返回-1,否則返回1
4 反之利用輾轉相除法求得最大公約數,之後判斷sign是否為-1,若是最大公約數前加負號。之後返回最大公約數
部分程式碼:
//計算兩個數最大公約數
string gcd(string str1,string str2){
int sign = 1;
if(str1[0] == '-'){ // str1為負數,去除前導負號
sign *= -1; //標誌位取相反數
str1 = str1.erase(0,1);
}
if(str2[0] == '-'){ // str2為負數,去除前導負號
sign *= -1; //標誌位取相反數
str2 = str2.erase(0,1);
}
if(str1.compare("1") == 0 || str2.compare("1") == 0){ //str1或str2為1時
if(sign == -1){ //標誌位位-1,str2加負號
return "-1";
}
return "1";
}else{ //str1和str2都不為1時,利用輾轉相除法求最大公約數
string gcd;
while(str2.compare("0") != 0){
gcd = this->MOD_INT(str1,str2);
str1 = str2;
str2 = gcd;
}
gcd = str1;
if(sign == -1){
gcd = "-"+gcd;
}
return gcd;
}
}
分數高精度加法
分數高精度加法運算演算法如下:
1 若num1的分母不為0,num2的分子為0,則返回num1。
2 若num2的分母不為0,num1的分子為0,則返回num2。
3 否則,若num1和num2的分母相等,num的分母為num1的分母,num的分子為num1和num2的分子之和。然後尋求分子和分母的最大公約數,然後將分子分母除以這個最大公約數。
4 反之,num1和num2的分母不相等,那麼執行下列操作:
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu);
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu);
fenzi = this->ADD_INT(fenzi1,fenzi2);
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu);
gcd = this->gcd(fenmu,fenzi);
fenmu = this->DIV_INT(fenmu,gcd);
fenzi = this->DIV_INT(fenzi,gcd);
部分程式碼:
//分數加法
Number ADD(Number num1,Number num2){
string fenmu,fenzi,gcd;
//cout<<num1.result()<<"+"<<num2.result()<<"="<<endl;
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
return num2;
}
if(num2.fenmu.compare("0") != 0 && num2.fenzi.compare("0") == 0){
return num1;
}
if(num1.fenmu.compare(num2.fenmu) == 0){//兩個分母相等
fenmu = num1.fenmu; //保留分母
fenzi = this->ADD_INT(num1.fenzi,num2.fenzi); //計算分子
gcd = this->gcd(fenmu,fenzi); //計算最大公約數
//cout<<fenzi<<endl;
//cout<<gcd<<endl;
fenmu = this->DIV_INT(fenmu,gcd); //約分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //約分後的分子
}else{//分母不相等
//計算通分後的分子1
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu);
//計算通分後的分子2
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu);
fenzi = this->ADD_INT(fenzi1,fenzi2); //計算分子
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu); //計算分母
gcd = this->gcd(fenmu,fenzi); //計算分子和分母的最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //計算通分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //計算通分後的分子
}
Number result("1");
//cout<<"fenzi:"<<fenzi<<endl;
//cout<<"fenmu:"<<fenmu<<endl;
//cout<<gcd<<endl;
result.set(fenmu,fenzi);
return result;
}
分數高精度減法
分數高精度減法運算演算法如下:
1 若num1的分母不為0,num1的分子為0,則返回num2的相反數。
2 若num2的分母不為0,num2的分子為0,則返回num1。
3 否則,若num1和num2的分母相等,num的分母為num1的分母,num的分子為num1和num2的分子之和。然後尋求分子和分母的最大公約數,然後將分子分母除以這個最大公約數。
4 反之,num1和num2的分母不相等,那麼執行下列操作:
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu);
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu);
fenzi = this->SUB_INT(fenzi1,fenzi2);
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu);
gcd = this->gcd(fenmu,fenzi);
fenmu = this->DIV_INT(fenmu,gcd);
fenzi = this->DIV_INT(fenzi,gcd);
部分程式碼:
//分數減法
Number SUB(Number num1,Number num2){
//cout<<num1.result()<<"+"<<num2.result()<<"="<<endl;
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
string fenzi = "-"+num2.fenzi;
string fenmu = num2.fenmu;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
if(num2.fenmu.compare("0") != 0 && num2.fenzi.compare("0") == 0){
return num1;
}
string fenmu,fenzi,gcd;
if(num1.fenmu.compare(num2.fenmu) == 0){//兩個分母相等
fenmu = num1.fenmu; //保留分母
fenzi = this->SUB_INT(num1.fenzi,num2.fenzi); //計算分子
gcd = this->gcd(fenmu,fenzi); //計算最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //約分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //約分後的分子
}else{//分母不相等
//計算通分後的分子1
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu);
//計算通分後的分子2
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu);
fenzi = this->SUB_INT(fenzi1,fenzi2); //計算分子
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu);//計算分母
gcd = this->gcd(fenmu,fenzi); //計算分子和分母的最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //計算通分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //計算通分後的分子
}
//cout<<num1.result()<<"-"<<num2.result()<<"="<<endl;
//cout<<fenmu<<endl;
//cout<<fenzi<<endl;
//cout<<gcd<<endl;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
分數高精度乘法
分數高精度減法運算演算法如下:
1 num1和num2的分子中出現至少一個0,是直接返回0;
2 若num1為1時 返回num2,若num2為1時返回num1
3 若num1為-時 返回num2相反數,若num2為-1時返回num1相反數
4 若num1和num2的分母中出現都為1時,fenmu = 1;若num1分母為1,num2的分母不為1時,fenmu = num2.fenmu,若num2分母為1,num1的分母不為1時,fenmu = num1.fenmu
5 若num1和num2的分子中出現都為1時,fenzi = 1;若num1分子為1,num2的分子不為1時,fenzi= num2.fenzi,若num2分子為1,num1的分子不為1時,fenzi = num1.fenzi
6 分子分母都不為1時需要進行約分,計算分子分母的最大公約數,進行約分。
部分程式碼:
//分數乘法,保證分母沒有0出現情況
Number MUL(Number num1,Number num2){
string fenmu,fenzi;
if(num1.fenzi.compare("0") == 0|| num2.fenzi.compare("0") == 0){ //分子出現0時,直接返回0
//fenmu = "1";
//fenzi = "0";
Number result("0");
return result;
}
if(num1.fenzi.compare("1") == 0|| num1.fenmu.compare("1") == 0){ //num1為1
//fenmu = "1";
//fenzi = "0";
return num2;
}
if(num2.fenzi.compare("1") == 0|| num2.fenmu.compare("1") == 0){ //num2為1
//fenmu = "1";
//fenzi = "0";
return num1;
}
if(num1.fenzi.compare("-1") == 0|| num1.fenmu.compare("1") == 0){ //num1為-1
string fenzi = "-"+num2.fenzi;
string fenmu = num2.fenmu;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
if(num2.fenzi.compare("1") == 0|| num2.fenmu.compare("1") == 0){ //num2為1
string fenzi = "-"+num1.fenzi;
string fenmu = num1.fenmu;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
//cout<<num1.fenmu<<" "<<num2.fenmu<<endl;
//計算分母
if(num1.fenmu.compare("1") == 0 && num2.fenmu.compare("1") == 0){
//兩個分母全為1
fenmu = "1";
}else if(num1.fenmu.compare("1") == 0 && num2.fenmu.compare("1") != 0){
//num1分母為1
fenmu = num2.fenmu;
}else if(num1.fenmu.compare("1") != 0 && num2.fenmu.compare("1") == 0){
//num2分母為1
fenmu = num1.fenmu;
}else{ //兩個分母都不為1
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu);
}
//計算分子
//cout<<num1.fenzi<<" "<<num2.fenzi<<endl;
if(num1.fenzi.compare("1") == 0 && num2.fenzi.compare("1") == 0){
//兩個分子全為1
fenzi = "1";
}else if(num1.fenzi.compare("1") == 0 && num2.fenzi.compare("1") != 0){
//num1分子為1
fenzi = num2.fenzi;
}else if(num1.fenzi.compare("1") != 0 && num2.fenzi.compare("1") == 0){
//num2分子為1
fenzi = num1.fenzi;
}else{ //兩個分子都不為1
fenzi = this->MUL_INT(num1.fenzi,num2.fenzi);
}
// 分子分母都不為1時需要進行約分
if(fenmu.compare("1") != 0 && fenzi.compare("1") != 0){
//cout<<fenmu<<" "<<fenzi<<endl;
string gcd = this->gcd(fenmu,fenzi);
fenmu = this->DIV_INT(fenmu,gcd);
fenzi = this->DIV_INT(fenzi,gcd);
//cout<<gcd<<endl;
//cout<<fenmu<<" "<<fenzi<<endl;
}
Number result("1");
result.set(fenmu,fenzi);
return result;
}
分數高精度除法
分數高精度減法運算演算法如下:
1 num1為0時,直接返回0;
2 num2為1時,直接返回num1
3 否則轉化為num1與num2的導數的乘積運算。
部分程式碼:
//分數除法,分母不出現0
Number DIV(Number num1,Number num2){
// num1為0時
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
Number result("0/1");
return result;
}
//num2為1時
if(num2.fenmu.compare("1") == 0 && num2.fenzi.compare("1") == 0){
return num1;
}
string fenmu = num2.fenzi;
string fenzi = num2.fenmu;
//除法轉化為乘法
Number tmp("1");
tmp.set(fenmu,fenzi);
Number result("1");
result = result.MUL(num1,tmp);
return result;
}
C++ 程式碼
#include <iostream>
#include <string>
using namespace std;
class Number{
private:
string fenmu; //分母
string fenzi; //分子
public:
Number(string str){
if(str.find('/') == string::npos){
this->fenmu = "1";
this->fenzi = str;
}else{
int index = str.find('/');
string fenzi = str.substr(0,index);
string fenmu = str.substr(index+1,str.length()-index-1);
this->fenmu = fenmu;
this->fenzi = fenzi;
}
}
void set(string fenmu,string fenzi){
if(fenmu[0] == '-' && fenzi[0] == '-'){ // 分子分母同為負
fenmu = fenmu.erase(0,1);
fenzi = fenzi.erase(0,1);
}else if(fenmu[0] == '-' && fenzi[0] != '-'){ //分母為負 分子為正
fenmu = fenmu.erase(0,1);
fenzi = "-"+fenzi;
}
this->fenmu = fenmu;
this->fenzi = fenzi;
}
int compare(string str1,string str2){
//相等返回0,大於返回1,小於返回-1
if(str1.size()>str2.size()){
return 1; //長度長的整數大於長度小的整數
}else if(str1.size()<str2.size()){
return -1;
}else{
return str1.compare(str2); //若長度相等,則頭到尾按位比較
}
}
//計算兩個數最大公約數
string gcd(string str1,string str2){
int sign = 1;
if(str1[0] == '-'){ // str1為負數,去除前導負號
sign *= -1; //標誌位取相反數
str1 = str1.erase(0,1);
}
if(str2[0] == '-'){ // str2為負數,去除前導負號
sign *= -1; //標誌位取相反數
str2 = str2.erase(0,1);
}
if(str1.compare("1") == 0 || str2.compare("1") == 0){ //str1或str2為1時
if(sign == -1){ //標誌位位-1,str2加負號
return "-1";
}
return "1";
}else{ //str1和str2都不為1時,利用輾轉相除法求最大公約數
string gcd;
while(str2.compare("0") != 0){
gcd = this->MOD_INT(str1,str2);
str1 = str2;
str2 = gcd;
}
gcd = str1;
if(sign == -1){
gcd = "-"+gcd;
}
return gcd;
}
}
//計算兩個數最小公倍數
string gcm(string str1,string str2){
if(str1.compare("1") == 0 || str2.compare("1") == 0){
return "1";
}
if(str1.compare("-1") == 0 || str2.compare("-1") == 0){
return "-1";
}
string tmp1 = str1;
string tmp2 = str2;
string gcd = this->gcd(tmp1,tmp2); //計算最大公約數
string gcm = this->MUL_INT(str1,str2);
gcm = this->DIV_INT(gcm,gcd); //計算最小公倍數
return gcm;
}
//string SUB_INT(string str1,string str2);
//分數加法
Number ADD(Number num1,Number num2){
string fenmu,fenzi,gcd;
//cout<<num1.result()<<"+"<<num2.result()<<"="<<endl;
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
return num2;
}
if(num2.fenmu.compare("0") != 0 && num2.fenzi.compare("0") == 0){
return num1;
}
if(num1.fenmu.compare(num2.fenmu) == 0){//兩個分母相等
fenmu = num1.fenmu; //保留分母
fenzi = this->ADD_INT(num1.fenzi,num2.fenzi); //計算分子
gcd = this->gcd(fenmu,fenzi); //計算最大公約數
//cout<<fenzi<<endl;
//cout<<gcd<<endl;
fenmu = this->DIV_INT(fenmu,gcd); //約分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //約分後的分子
}else{//分母不相等
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu); //計算通分後的分子1
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu); //計算通分後的分子2
fenzi = this->ADD_INT(fenzi1,fenzi2); //計算分子
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu); //計算分母
gcd = this->gcd(fenmu,fenzi); //計算分子和分母的最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //計算通分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //計算通分後的分子
}
Number result("1");
//cout<<"fenzi:"<<fenzi<<endl;
//cout<<"fenmu:"<<fenmu<<endl;
//cout<<gcd<<endl;
result.set(fenmu,fenzi);
return result;
}
//分數減法
Number SUB(Number num1,Number num2){
//cout<<num1.result()<<"+"<<num2.result()<<"="<<endl;
// num1為0時,返回num2的相反數
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
string fenzi = "-"+num2.fenzi;
string fenmu = num2.fenmu;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
//num2為0時,返回num1
if(num2.fenmu.compare("0") != 0 && num2.fenzi.compare("0") == 0){
return num1;
}
string fenmu,fenzi,gcd;
if(num1.fenmu.compare(num2.fenmu) == 0){//兩個分母相等
fenmu = num1.fenmu; //保留分母
fenzi = this->SUB_INT(num1.fenzi,num2.fenzi); //計算分子
gcd = this->gcd(fenmu,fenzi); //計算最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //約分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //約分後的分子
}else{//分母不相等
string fenzi1 = this->MUL_INT(num1.fenzi,num2.fenmu); //計算通分後的分子1
string fenzi2 = this->MUL_INT(num2.fenzi,num1.fenmu); //計算通分後的分子2
fenzi = this->SUB_INT(fenzi1,fenzi2); //計算分子
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu); //計算分母
gcd = this->gcd(fenmu,fenzi); //計算分子和分母的最大公約數
fenmu = this->DIV_INT(fenmu,gcd); //計算通分後的分母
fenzi = this->DIV_INT(fenzi,gcd); //計算通分後的分子
}
//cout<<num1.result()<<"-"<<num2.result()<<"="<<endl;
//cout<<fenmu<<endl;
//cout<<fenzi<<endl;
//cout<<gcd<<endl;
Number result("1");
result.set(fenmu,fenzi);
return result;
}
//分數乘法,保證分母沒有0出現情況
Number MUL(Number num1,Number num2){
string fenmu,fenzi;
if(num1.fenzi.compare("0") == 0|| num2.fenzi.compare("0") == 0){ //分子出現0時,直接返回0
//fenmu = "1";
//fenzi = "0";
Number result("0");
return result;
}
//cout<<num1.fenmu<<" "<<num2.fenmu<<endl;
//計算分母
if(num1.fenmu.compare("1") == 0 && num2.fenmu.compare("1") == 0){ //兩個分母全為1
fenmu = "1";
}else if(num1.fenmu.compare("1") == 0 && num2.fenmu.compare("1") != 0){ //num1分母為1
fenmu = num2.fenmu;
}else if(num1.fenmu.compare("1") != 0 && num2.fenmu.compare("1") == 0){ //num2分母為1
fenmu = num1.fenmu;
}else{ //兩個分母都不為1
fenmu = this->MUL_INT(num1.fenmu,num2.fenmu);
}
//計算分子
//cout<<num1.fenzi<<" "<<num2.fenzi<<endl;
if(num1.fenzi.compare("1") == 0 && num2.fenzi.compare("1") == 0){ //兩個分子全為1
fenzi = "1";
}else if(num1.fenzi.compare("1") == 0 && num2.fenzi.compare("1") != 0){ //num1分子為1
fenzi = num2.fenzi;
}else if(num1.fenzi.compare("1") != 0 && num2.fenzi.compare("1") == 0){ //num2分子為1
fenzi = num1.fenzi;
}else{ //兩個分子都不為1
fenzi = this->MUL_INT(num1.fenzi,num2.fenzi);
}
// 分子分母都不為1時需要進行約分
if(fenmu.compare("1") != 0 && fenzi.compare("1") != 0){
//cout<<fenmu<<" "<<fenzi<<endl;
string gcd = this->gcd(fenmu,fenzi);
fenmu = this->DIV_INT(fenmu,gcd);
fenzi = this->DIV_INT(fenzi,gcd);
//cout<<gcd<<endl;
//cout<<fenmu<<" "<<fenzi<<endl;
}
Number result("1");
result.set(fenmu,fenzi);
return result;
}
//分數除法,分母不出現0
Number DIV(Number num1,Number num2){
// num1為0時
if(num1.fenmu.compare("0") != 0 && num1.fenzi.compare("0") == 0){
Number result("0/1");
return result;
}
//num2為1時
if(num2.fenmu.compare("1") == 0 && num2.fenzi.compare("1") == 0){
return num1;
}
string fenmu = num2.fenzi;
string fenzi = num2.fenmu;
//除法轉化為乘法
Number tmp("1");
tmp.set(fenmu,fenzi);
Number result("1");
result = result.MUL(num1,tmp);
return result;
}
//整數加法
string ADD_INT(string str1,string str2){
//str1為0,返回str2
if(str1.compare("0") == 0){
return str2;
}
//str2為0,返回str1
if(str2.compare("0") == 0){
return str1;
}
//高精度加法
int sign=1; //sign 為符號位
string str;
if(str1[0]=='-'){ //str1為負數
if (str2[0]=='-'){ //str2為負數
sign=-1; //-1代表兩個負數相加
// 去掉兩個符號後相加
str=ADD_INT(str1.erase(0,1),str2.erase(0,1));
}else{//str2為正數
//去掉str1的符號,轉化為減法
str=SUB_INT(str2,str1.erase(0,1));
}
}else{ // str1為正數
if(str2[0]=='-'){ //str2為負數
// 去掉str2的負數,轉化為減法
str=SUB_INT(str1,str2.erase(0,1));
}else{ //str2為正數
//把兩個整數對齊,短整數前面加0補齊
string::size_type L1,L2;
int i;
L1=str1.size();
L2=str2.size();
if(L1<L2){ // str1的長度比str2小,str1在前補充0
for(i=1;i<=L2-L1;i++){
str1="0"+str1;
}
}else{ // str2的長度比str2小,str2在前面補充0
for(i=1;i<=L1-L2;i++){
str2="0"+str2;
}
}
int int1=0,int2=0; //int2 記錄進位,int1記錄結果
for(i=str1.size()-1;i>=0;i--){
int1=(int(str1[i])-'0'+int(str2[i])-'0'+int2)%10;
int2=(int(str1[i])-'0'+int(str2[i])-'0'+int2)/10;
str=char(int1+'0')+str;
}
if(int2!=0){ //計算結束後還有進位,直接在前面補充
str=char(int2+'0')+str;
}
}
}
//運算後處理符號位
if((sign==-1)&&(str[0]!='0')){
str="-"+str;
}
return str;
}
//整數減法
string SUB_INT(string str1,string str2){
//str1為0,返回str2
if(str1.compare("0") == 0){
//str2為正數
if(str2.compare("0") == 1){
return "-"+str2;
}else if(str2.compare("0") == 0){
return "0";
}else{
return str2.erase(0,1);
}
}
//str2為0,返回str1
if(str2.compare("0") == 0){
return str1;
}
//高精度減法
int sign=1; //sign為符號位
string str;
int i,j;
if(str2[0]=='-'){ //str2為負數
// 去掉str2的符號,轉化為加法
str=ADD_INT(str1,str2.erase(0,1));
}else{ // str2為正數
if(str1[0] == '-'){ //str1為負數,轉化為加法
str = ADD_INT(str1.erase(0,1),str2);
str = "-"+str;
}else{ //str1為正數
// 判斷str1和str2大小
int res=compare(str1,str2);
//cout<<"==="<<res<<endl;
if(res==0){ //str1和str2相等
return "0";
}
if(res<0){ //str1小於str2
sign=-1; //標誌位置為-1
// str1和str2互換
string temp =str1;
str1=str2;
str2=temp;
}
string::size_type tempint;
tempint=str1.size()-str2.size();
// 按值較小者的進行遍歷,tempint=0說明長度相等,大於0則str1長度大於str2
// 從最後一位開始遍歷
for(i=str2.size()-1;i>=0;i--){
// str1的對應為小於str2的對應位
if(str1[i+tempint]<str2[i]){
j=1; //偏移位
//開始迴圈,開始尋找借位
while(1){
//前面的位為0,借1置成9
if(str1[i+tempint-j]=='0'){
str1[i+tempint-j]='9';
j++;
}else{//前面的位為0,借1,當前位減1
str1[i+tempint-j]=char(int(str1[i+tempint-j])-1);
break;
}
}
str=char(str1[i+tempint]-str2[i]+':')+str;
}else{
str=char(str1[i+tempint]-str2[i]+'0')+str;
}
}
//把多餘為進行向前補充
for(i=tempint-1;i>=0;i--){
str=str1[i]+str;
}
}
}
//去除結果中多餘的前導0
str.erase(0,str.find_first_not_of('0'));
if(str.empty()){
str="0";
}
if((sign==-1) && (str[0]!='0')){
str ="-"+str;
}
return str;
}
//整數乘法
string MUL_INT(string str1,string str2){
//str1和str2至少存在1個0,直接返回0
if(str1.compare("0") == 0 || str2.compare("0") == 0){
return "0";
}
//str1為1直接返回str2
if(str1.compare("1") == 0){
return str2;
}
//str2為1直接返回str1
if(str2.compare("1") == 0){
return str1;
}
//str1為-1
if(str1.compare("-1") == 0){
//str2為負數
if(str2.compare("0") < 0){
return str2.erase(0,1);
}
//str2為正數
return "-"+str2;
}
//str2為1
if(str2.compare("-1") == 0){
//str1為負數
if(str1.compare("0") < 0){
return str1.erase(0,1);
}
//str1為正數
return "-"+str1;
}
//高精度乘法
int sign=1; //sign 為符號位
string str;
// str1為負數
if(str1[0]=='-'){
sign*=-1; // 標誌位乘-1
str1 =str1.erase(0,1); // 去掉負號
}
// str2為負數
if(str2[0]=='-'){
sign*=-1; // 標誌位乘-1
str2 =str2.erase(0,1); // 去掉負號
}
int i,j;
string::size_type L1,L2;
L1=str1.size();
L2=str2.size();
for(i=L2-1;i>=0;i--){ //模擬手工乘法豎式
string tempstr;
int int1=0,int2=0,int3=int(str2[i])-'0';
if(int3!=0){
// 下面的迴圈實現結果後端補0操作
for(j=1;j<=(int)(L2-1-i);j++){
tempstr="0"+tempstr;
}
for(j=L1-1;j>=0;j--){
// int1記錄個位數,int記錄十位數
int1=(int3*(int(str1[j])-'0')+int2)%10;
int2=(int3*(int(str1[j])-'0')+int2)/10;
tempstr=char(int1+'0')+tempstr;
}
if(int2!=0){
tempstr=char(int2+'0')+tempstr;
}
}
// 當前結果和tempstr向將
str=ADD_INT(str,tempstr);
}
//去除結果中的前導0
str.erase(0,str.find_first_not_of('0'));
if(str.empty()){
str="0";
}
if((sign==-1) && (str[0]!='0')){
str="-"+str;
}
return str;
}
//整數除法
string DIVIDE_INT(string str1,string str2,int flag){
//高精度除法。flag==1時,返回商; flag==0時,返回餘數
string quotient,residue; //定義商和餘數
int sign1=1,sign2=1;
if(str2 == "0"){ //判斷除數是否為0
quotient= "ERROR!";
residue = "ERROR!";
if(flag==1){
return quotient;
}else{
return residue;
}
}
//判斷被除數是否為0
if(str1=="0"){
quotient="0";
residue ="0";
}
//被除數為1,直接返回str1
if(str2 == "1"){
quotient = str1;
residue = "0";
}
//被除數為-1
if(str2 == "-1"){
if(str1.compare("0") < 0){
quotient = str1.erase(0,1);
}else{
quotient = "-"+str1;
}
residue = "0";
}
// str1為負數
if(str1[0]=='-'){
str1 = str1.erase(0,1); //去除前導0
sign1 *= -1;
sign2 = -1;
}
// str2為負數
if(str2[0]=='-'){
str2 = str2.erase(0,1); //去除前導0
sign1 *= -1;
}
//比較str1和str2的大小
int res=compare(str1,str2);
// str1小於str2
if(res<0){
// 商為0,餘數為str1
quotient="0";
residue =str1;
}else if(res == 0){
// str1與str2相等,商為1,餘數為0
quotient="1";
residue ="0";
}else{
// str1大於str2
string::size_type L1,L2;
L1=str1.size();
L2=str2.size();
string tempstr;
tempstr.append(str1,0,L2-1);
for(int i=L2-1;i<L1;i++){ //模擬手工除法豎式
tempstr=tempstr+str1[i];
//可能出現不夠除的情況,那麼必須把前導0去掉
tempstr.erase(0,tempstr.find_first_not_of('0'));
if(tempstr.empty()){
tempstr="0";
}
for(char ch='9';ch>='0';ch--){ //試商
string str;
str=str+ch;
if(compare(MUL_INT(str2,str),tempstr)<=0){
quotient=quotient+ch;
tempstr =SUB_INT(tempstr,MUL_INT(str2,str));
break;
}
}
}
residue=tempstr;
}
//去除結果中的前導0
quotient.erase(0,quotient.find_first_not_of('0'));
if(quotient.empty()){
quotient="0";
}
if((sign1==-1)&&(quotient[0]!='0')){
quotient="-"+quotient;
}
if((sign2==-1)&&(residue [0]!='0')){
residue ="-"+residue ;
}
if(flag==1){
return quotient;
}else{
return residue;
}
}
//整數除法,返回商
string DIV_INT(string str1,string str2){
//高精度除法,返回商
return DIVIDE_INT(str1,str2,1);
}
//整肅除法,返回餘數
string MOD_INT(string str1,string str2){
//高精度除法,返回餘數
return DIVIDE_INT(str1,str2,0);
}
string result(){
if(this->fenzi.compare("0") == 0){
return "0";
}
string res = this->fenzi;
if(this->fenmu.compare("1") != 0){
res = res+"/"+this->fenmu;
}
return res;
}
};
int main()
{
char ch;
string s1,s2;
while(cin>>s1>>ch>>s2){
Number num1(s1);
Number num2(s2);
Number res("1");
switch(ch){
case '+':res=res.ADD(num1,num2);break;
case '-':res=res.SUB(num1,num2);break;
case '*':res=res.MUL(num1,num2);break;
case '/':res=res.DIV(num1,num2);break;
default : break;
}
cout<<res.result()<<endl;
}
return 0;
}