高精度之高精度除法(高精除以高精)
阿新 • • 發佈:2019-01-02
好像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;
}