1. 程式人生 > >高精度之高精度除法(高精除以高精)

高精度之高精度除法(高精除以高精)

好像NOIP並不會用到,但是作為強迫症的我還是堅持學了。高精度除以高精度我所知道的有兩個思路:

手動模擬法

還是手動模擬除法過程,但是注意在截取了被除數的正確片段之後應該試商,即列舉k從1到9看當k等於多少才合適,但是如果每次迴圈都試一邊的話時間複雜度必定會很高,優化的方法就是在開始時就把k乘以除數的值記錄下來,程式執行時只要二分比較確定K值就行了。

但是這種方法實現起來太過麻煩,比較大小時可能會用到高精度減法,剛開始初始化的時候又用到高精度乘法,所以被我自動忽略。下面著重介紹第二種方法。

高精度減法模擬法

第二種方法是運用高精度減法,擷取完被除數之後用餘數減除數,直到值為負數,這樣確定出了這意味的商應該是多少,然後再取被除數的下一位。為了免去一些麻煩,可以使被除數被擷取的區間長度比除數大1,最後得出來的商如果大於9就朝上一位進位。具體程式碼實現如下:
讀的過程中建議自己造一組資料自行模擬手動的減法過程

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<vector> 
#include<iterator>
#include<cstdio>
using namespace std;
string a,b,d="0",bb="0";
int dif,dns=0,r,all=0;
int lena,lenb,qq=0;//qq記錄小數點是否已經輸出 
vector
<int>
c; void change(string x,string y)//這裡的減法和我寫的獨立的減法程式一樣,不再贅述 { c.clear(); dif=abs(x.length()-y.length()); int len=max(x.length(),y.length()); for(int i=len-1;i>=0;i--) { int a=x[i]-'0'+dns,b=y[i-dif]-'0'; if(len-i > y.length()) { b=0; } int
p=a-b; dns=0; if (p < 0) { p+=10; dns=-1; } c.push_back(p); } } void handle() { int num=0; while(a.length() > b.length() || (b.length() == a.length() && a >= b)) {//只是將除法的過程改為了減法,而這一步就是確定商多少 num++;//每減一次商累加一 change(a,b); int con=1; a="0"; for(int i=c.size()-1;i>=0;i--)//把餘數重新轉化為string { if(c[i]==0&&con) { continue; } con=0; if(a == "0") a=char(c[i]+48);//0的阿斯科瑪是48 else a=a+char(c[i]+48); }//為了不再更改之前寫的高精度減法的基礎,這裡只能將餘數轉換成string的形式 //之前其實沒用char(c[i]+48)的形式但是出現了各種錯誤,最後才發現這樣可以 } cout<<num;//輸出得到的商的當前位,當然如果希望得到結果的話也可以將它存起來 if(a == "0" ) exit(0);//對於小於的情況,如果餘數等於0了一定是已經除盡 int con=1; while(a.length() < b.length() || (b.length() == a.length() && a < b)) {//餘數後面補零,使餘數大於除數 if(con--) a=a+char(48);//如果只補一次,自然結束了 else //但是如果補了兩個0的話,一定會得到一個商是0,這時應該輸出 { cout<<"0"; a=a+char(48); } } } int main() { cin>>a>>b; if(b=="0") { cout<<"-1"; return 0; } lena=a.length(); lenb=b.length(); if(a == b) { cout<<"1"; return 0; } if(lena < lenb || (lena == lenb && a < b))//如果被除數小於除數,先輸出“0.”,再將被除數加一 { cout<<"0."; a+="0"; while(a.length() < b.length() || (b.length() == a.length() && a < b))//有可能出現加一後被除數仍然小於除數,這時應該繼續加 { a+="0"; cout<<"0";//為了模擬手動除法,每次給被除數加末尾數字的時候都應該輸出0 } while(all <= 100)//all用來控制輸出的位數,這裡是輸出的小數點後的位數 { handle();//其實這裡的handle改自高精度減法的main函式 all++; } } else//被除數大於除數和小於除數的情況差別很大,所以分開寫 { r=lenb-1;//記錄a中已經讀到的位置 //還是模擬除法,每次得到商的一位後都應該從被除數上降下一位到餘數,而r就是記錄從那裡降 //之前大於的情況在因為被除數已經沒有位可以降,所以是直接降0,而這裡顯然不可以 for(int i=0;i<lenb;i++)//將所選區間轉化為string形式 { if(d == "0") d=a[i]; else d+=a[i]; } while(d.length() < b.length() || (d.length() == b.length() && d < b)) { d+=a[++r]; }//還是考慮到可能所選區間不夠大 while(all <= 100) { int num=0; while(d.length()>lenb||(d.length()==lenb && d>=b )) { num++; change(d,b);//減法過程 int con=1; d=bb; for(int i=c.size()-1;i>=0;i--) { if(c[i]==0&&con)continue; con=0; if(d == "0") d=char(c[i]+48); else d=d+char(c[i]+48); } // cout<<d<<endl; } cout<<num; if(r+1 == lena && d == "0") break; if(r+1 == lena ) { if(!qq) { cout<<"."; qq=1; } d=d+char(48); } else d=d+a[++r]; all++; } } return 0; }