C高精度算法

目录

引言:

 一、高精度加法:

二、加法解释:

三、高精度减法

四、减法解释:

五、高精度乘法:

六、乘法解释:

七、高精度除法:

        1.高精度 / 低精度 类:

        2.高精度 / 高精度 类:

八、高精度算法中一个实用的,必备的小技能


引言:

        第一次写代码博客,欢迎大家......

        废话不多说,开始吧。

        我们知道,在c中,int类型数量级为1e9,long long类型的数量级是1e18,如果我们需要处理大型数据,如1e1000,1e5000,即使使用unsigned long long也无可奈何,我们就需要引入大数据处理算法,下面是以加减法的例子:

 一、高精度加法:

首先先上代码:

#include
using namespace std;
char s1[10000000], s2[10000000];
//*每一位数字存成字符,而不是用ull
int a[10000000], b[10000000], c[10000000];
int main(void)
{
	cin >> s1 >> s2;
	int la = strlen(s1);//*差不多是位数的意思
	int lb = strlen(s2);
	int lc = max(la,lb) + 1;
	int i = 0;
	for ( i = 0; i < la; i++)
	{
		a[la - i] = s1[i] - '0';
	}
	//*-‘0’是转换字符为数字的意思,这里la-i是把整个输入的大数据倒置,方便做加法运算,不然1234+827不倒置直接加,不能保证个位加个位,十位加十位,s1是4321,s2是728,这样从前到后,依次加法,就能保证对位加法
	for ( i = 0; i < lb; i++)
	{
		b[lb - i] = s2[i] - '0';
	}
	//*还需要注意的是,当i==0,lb-i是lb,当i==lb-1,lb-i就是1,也就是说a和b数组从【1】开始记录大数据,【0】值为0
	for ( i = 1; i <= lc; i++)
	{
		c[i] += a[i] + b[i];//*先无脑加
		c[i+1] += c[i] / 10;//*然后进位
		c[i] %= 10;//*如果没进位,那没事,进位了,就要取余10,防止炸掉,你当然不想个位数存在10这样的数字
	}
	while (c[lc] == 0 && lc > 0)
	{
		lc--;//*如果c数组最高位是0,而且lc>1//*(c是从【1】开始记录的,【0】不能算上)
	}
	if (lc == 0)
	{
		cout << 0 << endl;
		system("pause");
		return 0;
	}
	
	for ( i = lc; i >= 1; i--)
	{
		cout << c[i];//*输出就完事了
	}
	system("pause");
	return 0;
}

二、加法解释:

1.核心:用数组接受大数据

2.我们先用字符数组接受输入的大数据,字符数组的长度是大数据的位数,比如1234,就是四位,方括号就是4.用strlen就可以知道整个大数据的位数,得到的位数也是数组最大下标的值还要+1

3.建三个int类型数组,用来进行数字操作,ab数组是两个需要加和的大数据,c数组是做加法之后的大数据

4.在前面两个for中,我们是将字符数组倒置之后再赋值给int数组,为什么这么做,我们看,如果两个大数据的位数都是一样的,那么做和只需要个位加个位,十位加十位对应相加即可,但是如果出现1234+827这样的数字?这两个大数据在字符数组中保存时,1234分别是【0】【1】【2】【3】,827是【0】【1】【2】,那么如果做加法就要用8【0】+ 2【1】,2【1】+ 3 【2】,这样是在进行错位加法,不利于代码实现,如果我们将字符数组倒置,4321分别对应【0】【1】【2】【3】,728分别对应【0】【1】【2】,这样加法的时候,就能做到4【0】+ 7【0】了,具体的实现是s1数组的最大下标赋值给a数组0下标,依次类推,同理s2与b数组

 5.然后就是深入理解加法的实现,低位4+7,产生了进位1,那么高位就要接收这个1,用除号实现,同时,还要保证低位值不能超出10,用取余实现

C高精度算法_第1张图片

 6.做和之后的位数是原来两个数据位数最大值还要+1,这时候的位数lc就是做和之后的数组最后一个数字的下标,这点需要理解,这是由于字符数组倒置赋值给ab数组造成的,数据从【1】开始记录。

 7.值得注意的是,ab数组接收倒置后的字符数组,是从【1】开始的,【0】值应为0,所以在后面做和后的c数组也是从【0】开始的,做和之后的数组是倒置的,那么打印的时候就要倒序打印,打印到【1】结束,不打印【0】

 8.特殊情况是,当出现0+0时,lc就是2了,那么c数组【0】【1】【2】分别是0,0,0,如果按正常情况倒序打印,会打印出00,此时需要循环判断,如果最大下标lc是0,就利用lc--删除之,一直到lc为1或者出现非0数字时中止

9.如果并不想把相加的结果放在新的数组c中,而是放在a数组中(或者是b数组中,看你的意愿),我们可以把代码简单修改少许,删除掉c数组与lc,得到更加实用的高精度加法:

#include
using namespace std;
char s1[10000], s2[10000];
int a[10000], b[10000];
int main(void)
{
	cin >> s1 >> s2;
	int la = strlen(s1);
	int lb = strlen(s2);
	int i = 0;
	for ( i = 0; i < la; i++)
	{
		a[la - i] = s1[i] - '0';
	}
	for ( i = 0; i < lb; i++)
	{
		b[lb - i] = s2[i] - '0';
	}
	la = max(la,lb) + 1;
	for ( i = 1; i <= la; i++)
	{
		a[i] += b[i];
		a[i + 1] += a[i] / 10;
		a[i] %= 10;
	}
	while (a[la] == 0 && la > 0)
	{
		la--;
	}
	if (la == 0)
	{
		cout << 0 << endl;
		system("pause");
		return 0;
	}
	system("pause");
	return 0;
}

三、高精度减法

加法是减法的基础:

废话不多说,先上代码:

#include
using namespace std;
char s1[10000], s2[10000];
int a[10000], b[10000], c[100000];
int main(void)
{
	cin >> s1 >> s2;
	int la = strlen(s1);
	int lb = strlen(s2);
	int lc = max(la,lb);
	int i = 0;
	for ( i = 0; i < la; i++)
	{
		a[la - i] = s1[i] - '0';
	}
	for ( i = 0; i < lb; i++)
	{
		b[lb - i] = s2[i] - '0';
	}
	for ( i = 1; i <= lc; i++)
	{
		if (a[i] < b[i])
		{
			a[i] += 10;
			a[i - 1]--;
		}
		c[i] = a[i] - b[i];
	}
	while (c[lc] == 0 && lc > 0)
	{
		lc--;
	}
	if (lc == 0)
	{
		cout << 0 << endl;
		system("pause");
		return 0;
	}
	for ( i = lc; i >= 1; i--)
	{
		cout << c[i];
	}
	system("pause");
	return 0;
}

很容易发现,其实代码大部分与大整数加法是一样的,只是减法的实现有所不同

四、减法解释:

1.减法结果最高位是被减数的位数和减数的位数之间较大的一个

2.如果a【i】比b【i】小,那么a【i】向a【i+1】借位,这里用一个if进行判断,然后照常做减法,值交给c数组即可

3.如果并不想把相减的结果放在新的数组c中,而是放在a数组中,我们可以把代码简单修改少许,删除掉c数组与lc,得到更加实用的高精度减法:

#include
using namespace std;
char s1[10000], s2[10000];
int a[10000], b[10000];
int main(void)
{
	cin >> s1 >> s2;
	int la = strlen(s1);
	int lb = strlen(s2);
	int i = 0;
	for ( i = 0; i < la; i++)
	{
		a[la - i] = s1[i] - '0';
	}
	for ( i = 0; i < lb; i++)
	{
		b[lb - i] = s2[i] - '0';
	}
	la = max(la,lb);
	for ( i = 1; i <= la; i++)//注意,减法是从低位开始减,不能用for倒序做减法
	{
		if (a[i] < b[i])
		{
			a[i] += 10;
			a[i + 1]--;
		}
		a[i] -= b[i];
	}
	while (a[la] == 0 && la > 0)
	{
		la--;
	}
	if (la == 0)
	{
		cout << 0 << endl;
	}
	system("pause");
	return 0;
}

五、高精度乘法:

        先上代码提前消化:

#include
using namespace std;
char s1[2005], s2[2005];
int a[2005], b[2005], c[2005];
int main(void)
{
    int la, lb, lc;
    cin >> s1 >> s2;
    la = strlen(s1);
    lb = strlen(s2);

    for (int i = 0; i < la; i++)
    {
        a[la - i] = s1[i] - '0';
    }
    for (int i = 0; i < lb; i++)
    {
        b[lb - i] = s2[i] - '0';
    }
    lc = la + lb;
    for (int i = 1; i <= la; i++)
    {
        for (int j = 1; j <= lb; j++)
        {
            c[i + j - 1] += a[i] * b[j];
            c[i + j] += c[i + j - 1] / 10;
            c[i + j - 1] %= 10;
        }
    }
    while (c[lc] == 0 && lc > 0)
    {
        lc--;
    }
    if(lc == 0)
    {
        cout << 0 << endl;
        system("pause");
        return 0;
    }
    for (int i = lc; i > 0; i--)
    {
        cout << c[i];
    }
    system("pause");
    return 0;
}

六、乘法解释:

        我们来看一个乘法的例子:
C高精度算法_第2张图片

         我们先看右边:1234*32,我们都是先从32的2开始乘以1234的每一位,于是得到了第一行2468,再从32的3开始乘以1234的每一位,最后两行位位对齐相加,得到结果,当然这是小学数学方法,却是我们乘法的核心。

        我们抽象这个操作,得到左边,我们发现c数组满足如此规律:如果a数组下标记为i,b数组下标记为j,c数组下标就是 i + j - 1,则有c【i + j - 1】+= a【i】* b【j】。还要考虑每一个c【i + j - 1】经过累加后产生的进位,因此c【i + j - 1】%= 10 以及 c【i + j】+= c【i + j - 1】/ 10就很有必要。

        乘法本质上是加法,因此数组的转置也是需要的,a,b数组分别是s1,s2数组转置的结果,可以更好地计算。

        A*B,结果的位数,最多是两者位数的和,这通过上面的例子图示就可以看出来,代码体现就是lc = la + lb。

        同样前导0需要删除

七、高精度除法:

        通常,较大数除以较小数称为高精度除以低精度,所以,较大数除以较大数就是高精度除以高精度,下面是这两种算法的代码,有注释供食用:

        1.高精度 / 低精度 类:

#include
using namespace std;
char s1[5005];
//    这个是输入的被除数,也就是高精度数
long long b = 0,c[5005], x = 0,a[5005],la = 0, lc = 0;
//    b是除数,我们没有给他开char数组是因为本身低精度数值不大,x是余数,
//    a数组是对输入的高精度处理后的数据,放在a数组里,la是被除数的位数,
//    lc后面会用到,是一个下标,c数组是除法结果
int main(void)
{
    cin >> s1 >> b;
//    输入高精度和低精度
    la = strlen(s1);
//    计算高精度的位数la,用strlen实现很方便
    int i = 1;

    for (; i <= la; i++)
    {
        a[i] = s1[i -1] -'0';
    }
   
//    为什么不需要转置s1数组?因为除法的原理在于从高位开始,逐位试商,比如说4321 / 21,
//    先对43 / 21操作,再对后面的数操作,没有了加减法那样必须一一对应的计算要求,转置就没有了必要

    for (i = 1; i <= la; i++)
    {
        c[i] = (x*10+a[i]) / b;    //1*
        x = (x*10+a[i]) % b;    //2*
    }
//    1*代码实现除法原理,2*代码实现求出余数原理

    lc = 1;
    while (c[lc]== 0 && lc < la)
    {
        lc++;
    }
//    删除前导0操作,lc就是一个下标,通过从高位往低位寻找0,来确定输出时的起始位置,
//    比如,4位的结果,可能没有前导0,如4321,也可能有1个前导0,如0321,但最多有4-1个前导0,
//    即0001,这时候,lc通过往右移动,来避免了0的输出

    for ( i = lc; i <= la; i++)
    {
        cout << c[i];
    }
    
//    从lc开始,输出结果
    system("pause");
    return 0;
}

        2.高精度 / 高精度 类:

#include
using namespace std;
int a[100000], b[100000], c[100000], tmp[100000];
void print(int a[]);
void minu(int a[], int b[]);
void init(int *x);
void numcpy(int b[], int tmp[], int n);
int compare(int a[], int b[]);
int main(void)
{
    init(a);    //TODO 输入被除数a,并倒置处理
    if (a[0] == 1 && a[1] == 0)
    {
        cout << 0 << endl;
        system("pause");
        return 0;
    }
    
    init(b);    //TODO 同上
    c[0] = a[0] - b[0] + 1; //*这里用【0】处的位置放高精度数的位数
    for (int i = c[0]; i >= 1; i--) //TODO 循环次数=商位数
    {
        memset(tmp,0,sizeof(tmp));
        // TODO 商的每一位对应的tmp都是不一样的,每次都要清零
        numcpy(b,tmp,i-1);
        //TODO 通过规律我们知道,除数b要移动i-1位,存到tmp里面
        while (compare(a,tmp) >= 0) //TODO 经过减法,被除数a已经变成了余数a,如果余数>=被除数tmp,那么继续做减,直到余数 0)
    {
        c[0]--; //TODO 删除前导0
    }
    print(c);   //TODO 打印商
    cout << endl;

    system("pause");
    return 0;
}
void init(int *x)
{
    char s[100000];
    cin >> s;
    x[0] = strlen(s);
    for (int i = 0; i < x[0]; i++)
    {
        x[x[0]- i] = s[i] - '0';
    }
    
}
void print(int a[])
{
    for (int i = a[0]; i >= 1; i--)
    {
        cout << a[i];
    }
}
void minu(int a[], int b[])
{
    for (int i = 1; i <= a[0]; i++)//注意一定要从低位开始减
    {
        if (a[i] < b[i])
        {
            a[i+1]--;
            a[i] += 10;
        }
        a[i] -= b[i];
    }
    while (a[a[0]] == 0 && a[0] > 0)
    {
        a[0]--;
    }   
}
void numcpy(int b[], int tmp[], int n)  //*n是0添加的个数,或者说是b右移的位数,此时b已经倒置
{
    for (int j = 1; j <= b[0]; j++) //TODO 循环次数 = 位数
    {
        tmp[j+n] = b[j];  //TODO 通过规律我们可以写出这个式子,它将原来的b右移n位,存到tmp里面
        //! tmp[j] = 0; 为什么这句不能加上去?
        //*因为这个numcpy函数是将b复制到tmp的指定位置上的,当复制到后面时,tmp【j】会把前面的都变成0
    }
    tmp[0] = b[0] + n;  //TODO  计算新位数
}
int compare(int a[], int b[])   //*这是一个比较高精度数据的算法,它先比较位数,位数相等则再从高位到低位逐个比较
{
    if (a[0] > b[0])
    {
        return 1;   //TODO  余数a比tmp大,则返回1,意义是继续减法
    }
    if (a[0] < b[0])
    {
        return -1;  //TODO  余数a比tmp小,则返回-1,意义是结束减法,结束本位的计算,进入商的下一位计算
    }
    for (int i = a[0]; i >= 1; i--)  //TODO  来到这里是两者位数相同情况,那就一一从高位到低位比较数字
    {
        if (a[i] > b[i])    
        {
            return 1;   //TODO  i位是高位,比较高位的数字即可
        }
        if (a[i] < b[i])
        {
            return -1;
        }
    }
    return 0;   //TODO  来到这里,说明两个高精度数相等,返回0,意义是继续减法
}

八、高精度算法中一个实用的,必备的小技能:

        将数存入数组中是一个实用的小技巧,下面的一个简单的小程序说明了,如何将数存入字符数组和整形数组中,以及一些需要注意的问题:

#include
using namespace std;
int arr1[100000000];
char arr2[100000000];
int main(void)
{
	int a = 123456789;
	int larr1 = 0, larr2 = 0;
	while (a > 0)
	{
		int tmp = a % 10;
		arr1[larr1++] = tmp;
		arr2[larr2++] = tmp;
		a /= 10;
	}
	for (int i = larr1 - 1; i >= 0; i--)
	{
		cout << arr1[i];
	}
	cout << endl;
	for (int i = larr2 - 1; i >= 0; i--)
	{
		cout << (int)arr2[i];
	}
	cout << endl;
	system("pause");
	return 0;
}

        中间的while()循环内容是核心,用tmp将整数的每一位数字直接赋值给整形数组和字符数组中,赋值给整型数组没什么问题,但是赋值给字符数组常常有这样的情况:

        arr2【larr2++】 = tmp - ‘0’

        这是什么?tmp是一个整数,‘0’是一个字符,我们知道ASCLL码也是以整数存储的,‘0’对应整数48,那么tmp - ‘0’结果就是一个负数,这是不被我们允许的。

        除此之外,需要注意的是这个arr2数组是一个下标从0开始的数组,当我们将此小技巧与高精度算法结合时,对其进行strlen求位数时,strlen遇到数字0会停止,因此当arr2是数字10时,strlen结果为0,我们正确的位数应该是上面代码的larr2,这是需要注意的一点。

        然后就是这个字符数组arr2存放的数字已经是倒置的了,当与高精度算法结合使用时,不需要再倒置。

        还有一个需要注意的是,我们输出字符数组时,要将其强制转换为int类型,不然输出会有问题,即进行下面的代码处理:
        

for (int i = larr2 - 1; i >= 0; i--)
	{
		cout << (int)arr2[i];
	}

第一次发文不易,喜欢的话多多点赞支持,希望我的经验能够帮到你们。

你可能感兴趣的:(算法,算法,c语言)