高精度算法

        由于计算机在计算位数超过十几位的数时,不能采用现有类型,只能自己编程计算。

        高精度计算通用方法:

        高精度计算时一般用一个数组来存储一个数,数组的一个元素对应于数的一位(当然,在以后的学习中为了加快计算速度,也可用数组的一个元素表示数的多位数字,暂时不讲),表示时,由于数计算时可能要进位,因此为了方便,将数由低位到高位依次存在数组下标对应由低到高位置上,另外,我们申请数组大小时,一般考虑了最大的情况,在很多情况下,表示有富余,即高位有很多0,可能造成无效的运算和判断,因此,我们一般将数组的第0个下标对应位置来存储该数的位数。如数:3485(三千四百八十五),表达在数组a[10]上情况是:
下标  0    1    2    3     4    5    6    7    8    9  
内容  4    5    8    4     3    0    0    0    0    0
说明:位数   个位  十位  百位 千位
具体在计算加减乘除时方法就是小学时采用的列竖式方法
注:高精度计算时一般用正数,对于负数,通过处理符号位的修正。

        一.高精度数的存储

        1.如对数采用的字符串输入

 #include <iostream>
 #include <cstring>
 using namespace std;
 const int N=100;//最多100位
 int main()
 {
     int a[N+1],i;
     string s1;
     cin>>s1;//数s1
     memset(a,0,sizeof(a)); //数组清0
     a[0]=s1.length(); //位数
     for(i=1;i<=a[0];i++) a[i]=s1[a[0]-i]-'0';//将字符转为数字并倒序存储.
     return 0;
 }

        2.直接读入

#include <iostream>
using namespace std;
const int N=100;//最多100位
int main()
{
    int a[N+1],i,s,key;
    cin>>key;//数key
    memset(a,0,sizeof(a)); //数组清0
    i=0;//第0位
    while(key)  //当key大于0
    {
        a[++i]=key%10;//取第i位的数
        key=key/10;
    }
    a[0]=i; //共i位数
    return 0;
}

以下程序都只写函数,不写完整程序,所有高精度数存储都满足上述约定。

       二.高精度数比较

int compare(int a[],int b[])   //比较a和b的大小关系,若a>b则为1,a<b则为-1,a=b则为0
{
    int i;
    if (a[0]>b[0]) return 1;//a的位数大于b则a比b大
    if (a[0]<b[0]) return -1;//a的位数小于b则a比b小
    for(i=a[0];i>0;i--)  //从高位到低位比较
    {
        if (a[i]>b[i]) return 1;
        if (a[i]<b[i]) return -1;
    }
    return 0;//各位都相等则两数相等。
}

         三、高精度加法

int plus(int a[],int b[]) //计算a=a+b
{
    int i,k;
    k=a[0]>b[0]?a[0]:b[0]; //k是a和b中位数最大的一个的位数
    for(i=1;i<=k;i++)
    {
        a[i+1]+=(a[i]+b[i])/10;  //若有进位,则先进位
        a[i]=(a[i]+b[i])%10;
    }  //计算当前位数字,注意:这条语句与上一条不能交换。
    if(a[k+1]>0) a[0]=k+1;  //修正新的a的位数(a+b最多只能的一个进位)
    else a[0]=k;
    return 0;
}

         四、高精度减法

int gminus(int a[],int b[]);//计算a=a-b,返加符号位0:正数 1:负数
{ 
    int flag,i;
    flag=compare(a,b); //调用比较函数判断大小
    if (falg==0)//相等
    {
        memset(a,0,sizeof(a));return 0;
    }  //若a=b,则a=0,也可在return前加一句a[0]=1,表示是 1位数0
    if(flag==1) //大于  
    {  
        for(i=1;i<=a[0];i++)
        {  
            if(a[i]<b[i]){ 
                a[i+1]--;a[i]+=10;
            } //若不够减则向上借一位
            a[i]=a[i]-b[i];
        }
        while(a[a[0]]==0) a[0]--; //修正a的位数
        return 0;
    }
    if (flag==-1)//小于  则用a=b-a,返回-1
    { 
        for(i=1;i<=b[0];i++){  
            if(b[i]<a[i]){ b[i+1]--;b[i]+=10;} //若不够减则向上借一位
            a[i]=b[i]-a[i];
        }
        a[0]=b[0];
        while(a[a[0]]==0) a[0]--; //修正a的位数
        return -1;
    }
}

        五、高精度乘法1(高精度乘单精度数,单精度数是指通常的整型数)

int multi1(int a[],long  key) //a=a*key,key是单精度数  
{
    int i,k;
    if (key==0){memset(a,0,sizeof(a));a[0]=1;return 0;} //单独处理key=0
    for(i=1;i<=a[0];i++)a[i]=a[i]*key;//先每位乘起来
    for(i=1;i<=a[0];i++){a[i+1]+=a[i]/10;a[i]%=10;} //进位
    //注意上一语句退出时i=a[0]+1
    while(a[i]>0) {a[i+1]=a[i]/10;a[i]=a[i]%10;i++;a[0]++;}  
    //继续处理超过原a[0]位数的进位,修正a的位数
    return 0;
}

        六、 高精度除以低精度; 

        算法:按照从高位到低位的顺序,逐位相除。在除到第j位时,该位在接受了来自第j+1位的余数后与除数相除,如果最高位为零,则商的长度减一。源程序如下:

#include  <stdio.h>
#define   N  500
main()
{
    int  a[N] = {0}, c[N] = {0};
    char  a1[N];  
    printf("Input 除数:");
    printf("Input 被除数:");
    scanf("%s", a1);
    k = strlen(a1);
    for(i = 0; i < k; i++)  a[i] = a1[k - i - 1] - '0';
    d = 0;
    for(i = k - 1; i >= 0 ; i--)
    {
        d = d * 10 + a[i];
        c[i] = d / b;
        d = d % b;      
    }   
    while(c[k - 1] == 0 && k > 1)  k--;  
    printf("商=");
    printf("\n余数=%d", d);   
}     

 

        七、高精度乘以高精度(要求用尽可能少的存储单元); 

        算法:用数组保存两个高精度数,然后逐位相乘,注意考虑进位和总位数。源程序如下:

#include  <cstdio>
main()
{
	int  a[240] = {0}, b[240] = {0}, c[480] = {0};
	int  i, j, ka, kb, k;
	char  a1[240], b1[240];
	gets(a1);   
	ka = strlen(a1);
	gets(b1);   
	kb = strlen(b1);
	k = ka + kb;
	for(i = 0; i < ka; i++)  a[i] = a1[ka-i-1] - '0';
	for(i = 0; i < kb; i++)  b[i] = b1[kb-i-1] - '0';
	for(i = 0; i < ka; i++)
		for(j = 0; j < kb; j++)
		{
			c[i + j] = c[i + j] + a[i] * b[j];
			c[i + j +1] = c[i + j +1] + c[i + j]/10;
			c[i + j] = c[i + j] % 10;
		}
	if(!c[k])  k--;
	for(i = k-1; i >= 0; i--)  printf("%d", c[i]);        
}     

        六、高精度除于高精度

        基本的思想是反复做减法,看看从被除数里最多能减去多少个除数,商就是多少。一个一个减显然太慢,如何减得更快一些呢?

        以7546除以23为例来看一下:

       开始商为0。先减去23的100倍,就是2300,发现够减3次,余下646。于是商的值就增加300。

       然后用646减去230,发现够减2次,余下186,于是商的值增加20。

       最后用186减去23,够减8次,因此最终商就是328。

       所以本题的核心是要写一个大整数的减法函数,然后反复调用该函数进行减法操作。计算除数的10倍、100倍的时候,不用做乘法,直接在除数后面补0即可。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include <iostream>

#define MaxLen 200
using namespace std;
//函数SubStract功能:
//用长度为len1的大整数p1减去长度为len2的大整数p2
// 结果存在p1中,返回值代表结果的长度
//不够减 返回-1 正好够 返回0
int SubStract( int *p1, int *p2, int len1, int len2 )
{
    int i;
    if( len1 < len2 )
        return -1;
    if( len1 == len2 )
    {                        //判断p1 > p2
        for( i=len1-1; i>=0; i-- )
        {
            if( p1[i] > p2[i] )   //若大,则满足条件,可做减法
                break;
            else if( p1[i] < p2[i] ) //否则返回-1
                return -1;
        }
    }
    for( i=0; i<=len1-1; i++ )  //从低位开始做减法
    {
        p1[i] -= p2[i];
        if( p1[i] < 0 )          //若p1<0,则需要借位
        {
            p1[i] += 10;         //借1当10
            p1[i+1]--;           //高位减1
        }
    }
    for( i=len1-1; i>=0; i-- )       //查找结果的最高位
        if( p1[i] )                  //最高位第一个不为0
            return (i+1);       //得到位数并返回
    return 0;                  //两数相等的时候返回0
}
int main()
{
    int n, k, i, j;             //n:测试数据组数
    int len1, len2;             //大数位数
    int nTimes;                 //两大数相差位数
    int nTemp;                  //Subtract函数返回值
    int num_a[MaxLen];          //被除数
    int num_b[MaxLen];          //除数
    int num_c[MaxLen];          //商
    char str1[MaxLen + 1];      //读入的第一个大数
    char str2[MaxLen + 1];      //读入的第二个大数
 
    scanf("%d",&n);
    while ( n-->0 )
    {
        scanf("%s", str1);        //以字符串形式读入大数
        scanf("%s", str2);
        cout<<str1<<" "<<str2<<endl; 
 
        for ( i=0; i<MaxLen; i++ )   //初始化清零操作
        {
            num_a[i] = 0;
            num_b[i] = 0;
            num_c[i] = 0;
        }
 
        len1 = strlen(str1);  //获得大数的位数
        len2 = strlen(str2);
 
        for( j=0, i=len1-1; i>=0; j++, i-- )
            num_a[j] = str1[i] - '0';  //将字符串转换成对应的整数,颠倒存储
        for( j=0, i=len2-1; i>=0; j++, i-- )
            num_b[j] = str2[i] - '0';
 
        if( len1 < len2 )   //如果被除数小于除数,结果为0
        {
            printf("0\n");
            continue;   //利用continue直接跳出本次循环。 进入下一组测试
        }
        nTimes = len1 - len2;    //相差位数
        for ( i=len1-1; i>=0; i-- )    //将除数扩大,使得除数和被除数位数相等
        {
            if ( i>=nTimes )
                num_b[i] = num_b[i-nTimes];
            else                     //低位置0
                num_b[i] = 0;
        }
        len2 = len1;
        for( j=0; j<=nTimes; j++ )      //重复调用,同时记录减成功的次数,即为商
        {
            while((nTemp = SubStract(num_a,num_b + j,len1,len2 - j)) >= 0)
            {
                len1 = nTemp;      //结果长度
                num_c[nTimes-j]++;//每成功减一次,将商的相应位加1
            }
        }
 
        //输出结果
        for( i=MaxLen-1; num_c[i]==0 && i>=0; i-- );//跳过高位0
        if( i>=0 )
            for( ; i>=0; i-- )
                printf("%d", num_c[i]);
        else
            printf("0");
        printf("\n");
    }
    return 0;
}

 

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值