zoukankan      html  css  js  c++  java
  • 高精度

    高精度运算,是指参与运算的数或结果的范围大大超出了标准数据类型(整型,实型)
    能表示的范围的运算。例如,求两个 200 位的整数的和。这时,就要用到高精度算法了。
    常用的数据类型:
    表 1 整型
    数据类型 定义标识符 占字节数 数值范围 数值范围
    整型 int 4(32 位)
    -2147483648 ~
    2147483647
    -231 ~
    231-1
    超长整型 long
    long
    8(64 位)
    -9223372036854775808~
    9223372036854775807
    -263 ~
    263-1
    表 2 实型
    数据类型 定义标识符 占字节数 数值范围 有效位数
    双精度实型 double 8(64 位) -1.7E+308~1.7E+308 15~16 位
    (1)高精度数的输入和保存。
    输入要符合数字的输入规律:连续输入,中间无空格。
    可以使用字符串读入,数组保存。
    char s1[300],s2[300];
    int a[300],b[300],c[300];
    scanf("%s",s1)或者 cin>>s;
    如:s1=‘32456345’
    保存: 3 2 4 5 6 3 4 5
    a[8] a[7] a[6] a[5] a[4] a[3] a[2] a[1]
    int la=strlen(s1); //s1 的长度
    for(int i=1;i<=la;i++) a[i]=s1[la-i]-48; //保存读入的高精度数,
    a[1]保存个位
    (2)高精度数的输出
    for (int i=lc;i>=1;i--) cout<<c[i];
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    83 / 105
    【例题分析】
    例 2.6.1 高精度加法运算
    输入正整数 a 和 b,输出 a+b 的值。0<a,b<=10250
    输入:
    第一行:a
    第二行:b
    输出:a+b 的和。
    样例输入:
    99
    999
    样例输出:
    1098

    分析:
    (1)加法运算
    …… a[7] a[6] a[5] a[4] a[3] a[2] a[1]
    …+… 0 0 b[5] b[4] b[3] b[2] b[1]
    …… c[7] c[6] c[5] c[4] c[3] c[2] c[1]
    运算的次数=max(la,lb)
    (2)程序实现:
    方法一:模拟手工计算,设置一个进位变量 m
    #include<iostream>
    #include<cstring>
    using namespace std;
    char s1[300],s2[300];
    int a[300],b[300],c[300];
    int main(){
    int la,lb,lc,m=0;
    cin>>s1>>s2;
    la=strlen(s1);
    lb=strlen(s2);
    for (int i=1;i<=la;i++)
    a[i]=s1[la-i]-48;
    for (int i=1;i<=lb;i++)
    b[i]=s2[lb-i]-48;
    if (la>lb) lc=la;
    else lc=lb;
    for (int i=1;i<=lc;i++) {
    c[i]=(m+a[i]+b[i])%10;
    m=(m+a[i]+b[i])/10;
    }
    if (m==1) {lc++;c[lc]=1;}
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    84 / 105
    for (int i=lc;i>=1;i--) cout<<c[i];
    cout<<endl;
    return 0;
    }
    方法二:先计算,最后处理进位
    #include<iostream>
    #include<cstring>
    using namespace std;
    char s1[300],s2[300];
    int a[300],b[300],c[300];
    int main(){
    int la,lb,lc;
    cin>>s1>>s2;
    la=strlen(s1);
    lb=strlen(s2);
    for (int i=1;i<=la;i++)
    a[i]=s1[la-i]-48;
    for (int i=1;i<=lb;i++)
    b[i]=s2[lb-i]-48;
    if (la>lb) lc=la;
    else lc=lb;
    for (int i=1;i<=lc;i++) c[i]=a[i]+b[i];
    for (int i=1;i<=lc;i++) {
    c[i+1]=c[i+1]+c[i]/10;
    c[i]=c[i]%10;
    }
    if (c[lc+1]==1) lc++;
    for (int i=lc;i>=1;i--) cout<<c[i];
    return 0;
    }
    思考:能不能把数组 c 去掉?
    方法二改进:去掉 c 数组
    for (int i=1;i<=la;i++) a[i]=a[i]+b[i];
    for (int i=1;i<=la;i++) {
    a[i+1]=a[i+1]+a[i]/10;
    a[i]=a[i]%10;
    }
    if (a[la+1]==1) la++;
    for (int i=la;i>=1;i--) cout<<a[i];
    return 0;
    例 2.6.1 拓展:大整数加法
    求两个不超过 200 位的非负整数的和。
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    85 / 105
    输入:
    有两行,每行是一个不超过 200 位的非负整数,可能有多余的前导 0。
    输出
    一行,即相加后的结果。结果里不能有多余的前导 0,即如果结果是 342,那么就不能
    输出为 0342。
    样例输入
    22222222222222222222
    33333333333333333333
    样例输出
    55555555555555555555
    题目来源:http://noi.openjudge.cn/ch0106/10/
    分析:高精度加法,输出前考虑前导 0 的问题。
    while (a[la]==0&&la>1)la--;
    例 2.6.2 高精度减法运算
    求两个大的正整数相减的差。
    输入:
    共 2 行,第 1 行是被减数 a,第 2 行是
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    86 / 105
    int a[300],b[300];
    int main(){
    int la,lb,m=0;
    cin>>s1>>s2;
    la=strlen(s1);
    lb=strlen(s2);
    if (strcmp(s1,s2)==0) {cout<<0; return 0;}
    if (la<lb||la==lb&&strcmp(s1,s2)<0) {
    cout<<'-';
    strcpy(s3,s1);
    strcpy(s1,s2);
    strcpy(s2,s3);
    }
    la=strlen(s1);
    lb=strlen(s2);
    for (int i=1;i<=la;i++)
    a[i]=s1[la-i]-48;
    for (int i=1;i<=lb;i++)
    b[i]=s2[lb-i]-48;
    for (int i=1;i<=la;i++) {
    if (a[i]<b[i]) {
    a[i+1]--;
    a[i]+=10;
    }
    a[i]-=b[i];
    }
    while (a[la]==0&&la>1)la--;
    for (int i=la;i>=1;i--) cout<<a[i];
    cout<<endl;
    return 0;
    }
    例 2.6.2 拓展:输入正整数 a 和 b,输出 a-b 的值。a,b<=10250
    输入:
    第一行:a
    第二行:b
    输出:a-b 的值。
    样例输入:
    33
    100
    样例输出:
    -67
    分析:
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    87 / 105
    比较 a 和 b 的大小:从而确定结果的正负号
    if (a==b) 结果 : 0
    if (a>b) 结果 + :a-b
    if (a<b) 结果 - :-(b-a)
    la=strlen(s1);
    lb=strlen(s2);
    if (strcmp(s1,s2)==0) {cout<<0; return 0;}
    if (la<lb||la==lb&&strcmp(s1,s2)<0) {如果 a<b,则交换,保证用大数
    -小数
    cout<<'-';
    strcpy(s3,s1);
    strcpy(s1,s2);
    strcpy(s2,s3);
    }
    la=strlen(s1);
    lb=strlen(s2);
    程序实现:
    #include<iostream>
    #include<cstring>
    using namespace std;
    char s1[300],s2[300],s3[300];
    int a[300],b[300];
    int main(){
    int la,lb,m=0;
    cin>>s1>>s2;
    la=strlen(s1);
    lb=strlen(s2);
    if (strcmp(s1,s2)==0) {cout<<0; return 0;}
    if (la<lb||la==lb&&strcmp(s1,s2)<0) {
    cout<<'-';
    strcpy(s3,s1);
    strcpy(s1,s2);
    strcpy(s2,s3);
    }
    la=strlen(s1);
    lb=strlen(s2);
    for (int i=1;i<=la;i++)
    a[i]=s1[la-i]-48;
    for (int i=1;i<=lb;i++)
    b[i]=s2[lb-i]-48;
    for (int i=1;i<=la;i++) {
    if (a[i]<b[i]) {
    a[i+1]--;
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    88 / 105
    a[i]+=10;
    }
    a[i]-=b[i];
    }
    while (a[la]==0&&la>1)la--;
    for (int i=la;i>=1;i--) cout<<a[i];
    cout<<endl;
    return 0;
    }
    例 2.6.3 高精度乘单精度
    求 a=a*b。(0<a<10250, 0<b<108)
    输入:
    第一行:a
    第二行:b
    输出:a*b 的值。
    样例输入:
    330
    100
    样例输出:
    33000
    分析:
    1)a 的每一位都单独与 b 相乘;
    2)再由低到高位依次处理 a 的进位;
    3)最后处理最高位。
    程序实现:
    #include<iostream>
    #include<cstring>
    using namespace std;
    char s1[300],s2[300];
    int a[300];
    int main(){
    int la,b,m;
    cin>>s1>>b;
    la=strlen(s1);
    for (int i=1;i<=la;i++) a[i]=s1[la-i]-48;
    for (int i=1;i<=la;i++) a[i]=a[i]*b;
    for (int i=1;i<=la;i++) {//处理进位
    a[i+1]=a[i+1]+a[i]/10;
    a[i]=a[i]%10;
    }
    m=a[la+1];
    while (m>0){a[++la]=m%10;m=m/10;}//处理最高位
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    89 / 105
    for (int i=la;i>=1;i--) cout<<a[i];
    return 0;
    }
    例 2.6.4 高精度乘高精度
    求 c=a*b(0<a<10250, 0<b<10250)
    输入:
    第一行:a
    第二行:b
    输出:a-b 的值。
    样例输入:
    22222
    33333
    样例输出:
    740725926
    分析:
    假设乘数和被乘数的长度分别为 la 和 lb,则最后的乘积长度最长为 la+lb,最短为
    la+lb-1;
    程序实现:边乘边处理进位
    #include<iostream>
    #include<cstring>
    using namespace std;
    int a[300],b[300],c[300];
    char s1[300],s2[300],s3[300];
    int main(){
    cin>>s1>>s2;
    int la,lb,len;
    la=strlen(s1);
    lb=strlen(s2);
    for(int i=1;i<=la;i++) a[i]=s1[la-i]-48;
    for(int i=1;i<=lb;i++) b[i]=s2[lb-i]-48;
    for(int i=1;i<=la;i++) //a[i]*b[j]的结果存放在 c[i+j-1]
    for(int j=1;j<=lb; j++){
    c[i+j-1]=c[i+j-1]+a[i]*b[j];
    c[i+j]=c[i+j]+c[i+j-1]/10;
    c[i+j-1]=c[i+j-1]%10;
    }
    len=la+lb;
    while (c[len]==0&&len>1) len--; //去掉多余的前导 0;
    for(int i=len;i>=1;i--) cout<<c[i];
    return 0;
    }
    【高精度应用】
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    90 / 105
    例 2.6.5 回文数(NOIP1999 简化版)
    若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文
    数。
    例如:给定一个 10 进制数 56,将 56 加 65(即把 56 从右向左读),得到 121 是一个
    回文数。
    又如:对于 10 进制数 87:
    STEP1:87+78 = 165 STEP2:165+561 = 726
    STEP3:726+627 = 1353 STEP4:1353+3531 = 4884
    在这里的一步是指进行了一次 N 进制的加法,上例最少用了 4 步得到回文数 4884。
    输入:
    第一行是整数 N(2<=N<=10),表示这个数的进制;
    第二行是 N 进制数 M,求最少经过几步可以得到回文数。
    输出:
    若最少在 30 步以内得到回文数,则输出步数;
    如果在 30 步以内(包含 30 步)不可能得到回文数,则输出“Impossible”(无引号)
    样例输入:
    9
    87
    样例输出:
    6
    分析:
    (1)本题数值虽然不大,但是为了处理方便可以采用字符串读入,数组保存。
    (2)N 进制的加法:逢 n 进 1
    程序实现:
    #include<iostream>
    #include<cstring>
    using namespace std;
    char s1[300],s2[300];
    int a[300],b[300];
    int main(){
    int n,len,cnt=0,t=0,ok;
    cin>>n>>s1;
    len=strlen(s1);
    for (int i=1;i<=len;i++)
    a[i]=s1[len-i]-48;
    while (t<=30){
    t++;
    for (int i=1;i<=len;i++) b[i]=a[i]+a[len-i+1];
    for (int i=1;i<=len;i++) {
    b[i+1]=b[i+1]+b[i]/n;
    b[i]=b[i]%n;
    }
    if (b[len+1]==1) len++;
    ok=0;
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    91 / 105
    for (int i=1;i<=len/2;i++)
    if (b[i]!=b[len-i+1]) {ok=1;break;}
    memcpy(a,b,300*sizeof(int));
    if (ok==0) break;
    }
    if (ok==0) cout<<t<<endl;
    else cout<<"Impossible"<<endl;
    return 0;
    }
    例 2.6.5 斐波那契数列
    斐波那契数列,指的是这样一个数列:0、1、1、2、3、5、8、13、21、……在数学
    上,斐波纳契数列以如下的方法定义:
    F0=0,F1=1,Fn=F(n-1)+F(n-2)(3<=n<=1000)
    输入正整数 n, 求该数列第 n 项的值。
    输入样例 1:20
    输出样例 1:4181
    输入样例 2:40
    输出样例 2: 63245986
    输入样例 3:50
    输出样例 3: 7778742049
    分析:高精度+高精度
    程序实现:
    #include<iostream>
    #include<cstring>
    using namespace std;
    int a[1000],b[1000],c[1000];
    int main(){
    int n;
    cin>>n;
    a[1]=1;a[0]=1;
    b[1]=1;b[0]=1;
    int len;
    for (int k=3;k<=n;k++) {
    len=b[0];
    for (int i=1;i<=len;i++) c[i]=a[i]+b[i];
    for (int i=1;i<=len;i++) {
    c[i+1]=c[i+1]+c[i]/10;
    c[i]=c[i]%10;
    }
    if (c[len+1]==1) len++;
    c[0]=len;
    memcpy(a,b,1000*sizeof(int));memcpy(b,c,1000*sizeof(int));
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    92 / 105
    }
    for(int i=c[0];i>=1;i--) cout<<c[i];
    return 0;
    }
    例 2.6.6 计算 2 的 N 次方
    任意给定一个正整数 N(N<=100),计算 2 的 n 次方的值。
    输入:一个正整数 N。
    输出:2 的 N 次方的值。
    样例输入:5
    样例输出:32
    题目来源:http://noi.openjudge.cn/ch0106/12/
    分析:
    2100 约等于 1030,运算结果超过了标准数据类型的范围,所以采用高精度运算(高精
    度*单精度)。
    程序实现:
    #include<iostream>
    #include<cstring>
    using namespace std;
    int a[300];
    int main(){
    int n,la=1;
    cin>>n;
    a[1]=1;
    for (int j=1;j<=n;j++) {
    for (int i=1;i<=la;i++) a[i]=a[i]*2;
    for (int i=1;i<=la;i++) {
    a[i+1]=a[i+1]+a[i]/10;
    a[i]=a[i]%10;
    }
    int m=a[la+1];
    while (m>0){a[++la]=m%10;m=m/10;}//处理最高位
    }
    for (int i=la;i>=1;i--) cout<<a[i];
    cout<<endl;
    return 0;
    }
    例 2.6.7 求 10000 以内 n 的阶乘
    输入:输入整数 n(0<=n<=10000)。
    输出:一行,即 n!的值。
    样例输入
    100
    样例输出
    93326215443944152681699238856266700490715968264381621468592963
    2016 年山东省信息学奥赛夏令营讲义(2016.7.12-7.20) 举办学校:日照一中
    93 / 105
    89521759999322991560894146397615651828625369792082722375825118521
    0916864000000000000000000000000
    来源 http://noi.openjudge.cn/ch0106/14/
    分析:高精度*单精度
    程序实现:
    #include<cstdio>
    #include<iostream>
    using namespace std;
    int a[100000];
    int main(){
    int n,la=1;
    cin>>n;
    a[1]=1;//初值
    for (int j=1;j<=n;j++){
    for (int i=1;i<=la;i++) a[i]=a[i]*j;
    for (int i=1;i<=la;i++) {//处理进位
    a[i+1]=a[i+1]+a[i]/10;
    a[i]=a[i]%10;
    }
    int m=a[la+1];
    while (m>0){a[++la]=m%10;m=m/10;}//处理最高位
    }
    for (int i=la;i>=1;i--) cout<<a[i];
    return 0;
    }

  • 相关阅读:
    Python笔记17(Django之路由系统)
    Python笔记16(Django介绍与安装)
    Python笔记16(Web框架本质)
    序列的区间操作
    并查集(入门)
    (补题 Uva 3027)Corporative Network
    (补题 cf 1167C)News Distribution
    (补题 CF 1013B 模拟)And
    (补题 CF 1234C)Pipes
    (补题 POJ 1679 次小生成树)The Unique MST
  • 原文地址:https://www.cnblogs.com/lipeiyi520/p/8678573.html
Copyright © 2011-2022 走看看