高精度算法

高精度算法

本文内容较多,信息量偏大,请仔细阅读。

1.高精度算法简介

高精度算法(High Accuracy Algorithm)是处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

2.高精数读入

方法一:使用字符数组

\(gets\) 遇回车符则认为当前字符串结束(可以读入空格);

\(scanf\)\(cin\) 遇空格或回车符则认为当前字符串结束。

#include<iostream> 
#include<cstdio>  
#include<cstring>  
using namespace std;  
char s[200];
int main(){
   gets(s);//注,gets()在最新的c++里已经不能用了
   cout<<strlen(s)<<endl<<s;  
   return 0;
}

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
char s[200];
int main(){
   cin>>s;  
   cout<<strlen(s)<<endl<<s;  
   return 0;
}

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
char s[200];
int main(){
   scanf("%s",s);  
   cout<<strlen(s)<<endl<<s;  
   return 0;
}

方法二:使用字符串

\(cin\) 遇空格或回车符则认为当前字符串结束;

\(getline\) 遇回车符则认为当前字符串结束(可以读入空格)。

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
string s;
int main(){
   getline(cin,s);  
   cout<<s.length()<<endl<<s;  
   return 0;
}

#include<iostream>  
#include<cstdio>  
#include<cstring>  
using namespace std;  
string s;
int main(){
    cin>>s;  
    cout<<s.length()<<endl<<s;  
    return 0;
}

3.高精数存储

s1="123456789"

a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8]
9 8 7 6 5 4 3 2 1

s2="3456"

a[0] a[1] a[2] a[3]
6 5 4 3

一般是倒着存(a[0]是个位,最后的元素是最高位)。

1、需要先计算个位,逆序存放方便对齐。

2、方便进位。

代码:

la=s.size();//用la存放字符串s的位数 
for(i=0;i<la;i++)//将数串s转换为数组a,注意:倒序存储
	a[i]=s[la-1-i] -'0';

4.高精加

进位

a[6] a[5] a[4] a[3] a[2] a[1] a[0]
+ b[4] b[3] b[2] b[1] b[0]
c[6] c[5] c[4] c[3] c[2] c[1] c[0]

加法法则:个位对齐,逢十进一。

代码实现

方法一:设置一个进位变量 \(m\)

if(la>lb) lc=la;//lc取最长位
else lc=lb;
m=0;
for(int i=0;i<lc;i++){	
    c[i]=(m+a[i]+b[i])%10;  
    m=(m+a[i]+b[i])/10;
}
if(m>0){//最高位有进位时	
    c[lc]=m; 
    lc++;
}  

方法二:先计算,最后处理进位

for(int i=0;i<lc;i++)  
    c[i]=a[i]+b[i];
for(int i=0;i<lc;i++){ 
    c[i+1]=c[i+1]+c[i]/10;  
    c[i]=c[i]%10;
}
if(c[lc]>0) lc++;
a[3] a[2] a[1] a[0]
6 2 4 7
+ 3 7 9 5
9 9 13 12
1 0 0 4 2
c[4] c[3] c[2] c[1] c[0]

改进:去掉 \(c\) 数组

for(int i=0;i<la;i++)  
    a[i]=a[i]+b[i];
for(int i=0;i<a;i++){ 
    a[i+1]+=a[i]/10;
    a[i]%=10;
}
if(a[la]>0) la++;

代码

#include<iostream>
#include<string>
using namespace std;
int main(){
    int a[205]={0},b[205]={0};
    string a1,b1;
    cin>>a1>>b1;
    int la=a1.length(),lb=b1.length();
    for(int i=0;i<la;i++)
        a[i]=a1[la-1-i]-'0';
    for(int i=0;i<lb;i++)
        b[i]=b1[lb-1-i]-'0';
    if(lb>la) la=lb;
    for(int i=0;i<la;i++)
        a[i]+=b[i];
    for(int i=0;i<la;i++){
        a[i+1]+=a[i]/10;
        a[i]%=10;
    }
    if(a[la]>0) la++;
    while(la>0&&a[la-1]==0) la--;//删除前导零
    for(int i=la-1;i>=0;i--)
        cout<<a[i];
    return 0;
}

5.高精减(跟高精加区别不大,所以不细说了)

借位

for(int i=0;i<la;i++){ 
    if(a[i]<b[i]){	
        a[i+1]--;//跟一般的竖式思想一样,前一位减1
        a[i]+=10;//本位加十
    }
    a[i]-=b[i];
}

代码(不是最终版)

#include<iostream>
#include<string>
using namespace std;
int main(){
    int a[205]={0},b[205]={0};
    string a1,b1;
    cin>>a1>>b1;
    int la=a1.length(),lb=b1.length();
    if(a1==b1){
        cout<<0;
        return 0;
    }
    for(int i=0;i<la;i++)
        a[i]=a1[la-1-i]-'0';
    for(int i=0;i<lb;i++)
        b[i]=b1[lb-1-i]-'0';
    for(int i=0;i<la;i++){
        if(a[i]<b[i]){
            a[i]+=10;
            a[i+1]-=1;
        }
        a[i]-=b[i];
    }
    while(la>0&&a[la-1]==0) la--;
    for(int i=la-1;i>=0;i--)
        cout<<a[i];
    return 0;
}

但我们还可能遇到结果是负数的情况

所以,我们要在代码前面判断哪个数大。

如果第二个数大,那么我们就先输出负号,再让第二个数去减第一个数。

判断代码如下:

if(la<lb||la==lb&&a1<b1){
    swap(a1,b1);
    swap(la,lb);
    cout<<"-";
}

在原始代码上加上判断代码即可

6.高精乘

高精乘单精

9 9 9
* 9 9
891 891 891
9 8 9 0 1

分析:

1.\(a\) 的每一位都单独与 \(b\) 相乘;

2.再由低到高位依次处理进位;

3.最后处理最高位。

代码(高精乘单精)

#include<iostream>
#include<string>
using namespace std;
int main(){
    int la,b,m,a[260]={0};  
    string a1;   
    cin>>a1>>b;  
    la=a1.size();
    for(int i=0;i<la;i++)  
        a[i]=a1[la-i-1]-'0';
    for(int i=0;i<la;i++)  
        a[i]=a[i]*b;
    for(int i=0;i<la;i++){  
        a[i+1]=a[i+1]+a[i]/10;
        a[i]=a[i]%10;
    }
    m=a[la]; //最高位 
    while(m>0){	//可能有多次进位
        a[la]=m%10; 
        m/=10;
        ++la;	
    }
    for(int i=la-1;i>=0;i--)
        cout<<a[i];
    return 0;
    
}

高精乘高精

分析:

假设乘数和被乘数的长度分别为 \(la\)\(lb\)

则最后的乘积长度最长为 \(la+lb\) ,最小是 \(la+lb-1\)

下标关系:

a[2] a[1] a[0]
b[1] b[0]
a[2]*b[0] a[1]*b[0] a[0]*b[0]
a[2]*b[1] a[1]*b[1] a[0]*b[1]
c[4] c[3] c[2] c[1] c[0]

\[c[i+j]+=a[i] * b[j] \]

代码(高精乘高精)

#include<iostream>
#include<string>
using namespace std;
int main(){
    int a[205]={0},b[205]={0},c[205]={0};
    string a1,b1;
    cin>>a1>>b1;
    int la=a1.size(),lb=b1.size(),lc=la+lb;
    for(int i=0;i<la;i++)
        a[i]=a1[la-i-1]-'0';
    for(int i=0;i<lb;i++)
        b[i]=b1[lb-i-1]-'0';
    for(int i=0;i<la;i++)
        for(int j=0;j<lb;j++)
            c[i+j]+=a[i]*b[j];
    for(int i=0;i<lc;i++){
        c[i+1]+=c[i]/10;
        c[i]%=10;
    }
    while(lc>0&&c[lc-1]==0) lc--;
    for(int i=lc-1;i>=0;i--)
        cout<<c[i];
    return 0;
}

7.高精除

高精除以单精(与手工除类似)

#include<iostream>
#include<string>
using namespace std;
int main(){
    int a[205]={0},b,c[205]={0};
    string a1;
    cin>>a1>>b;
    int la=a1.size(),lc=0,ys=0;
    for(int i=0;i<la;i++)
        a[i]=a1[i]-'0';
    for(int i=0;i<la;i++){
        c[i]=(ys*10+a[i])/b;
        ys=(ys*10+a[i])%b;
    }
    while(c[lc]==0&&lc<la-1) lc++;
    for(int i=lc;i<la;i++)
        cout<<c[i];
    return 0;
}

高精除以高精

大体思路:

高精度除法,这个和加减乘一样,我们都要从手算的角度入手。举 一个例子,比如 \(524134\) 除以 \(123\),结果是 \(4261\)

第一位 \(4\) 的来源是把 \(524\)\(123\) 对齐,然后进行循环减法,循环了 \(4\) 次,余\(32\)

\(32134\) 的前三位 \(321\)继续和 \(123\) 对齐,循环减法 \(2\) 次,余 \(75\)

\(7534\) 的前三位\(753\)\(123\) 对齐,循环减法 \(6\) 次,余\(15\)

\(154\)\(123\) 对齐,只能减 \(1\) 次,所以结果是 \(4261\)

终止程序。

完awa~

敲了360行,人快废了。

给个点赞吧,球球了~~~

posted @ 2022-07-14 22:43  Rainforests  阅读(162)  评论(0编辑  收藏  举报