高精度算法之乘法运算

       事先声明本文章中编写的代码仅用于学习算法思想和编写基础形式使用,并未进行太多的代码优化,因此,若需要对代码进行优化以及异常处理的小伙伴们,可自行添加相关操作,谢谢!

一、引言

        前面,我们陆续叙述了关于高精度加法以及减法的算法实现,接下来,我打算在这里继续讲解乘法运算的代码实现。

        经过前面两个运算的实现,正常来说,在我们的脑海中应该已经形成了相对比较清楚的思路框架,也很容易发现,这些运算的实现中核心就在运算规律代入代码时的不同,其他步骤都极其类似。那么,现在我们就来聊聊高精度乘法的代码实现过程。(文末附有总代码实现

二、问题分析

1、需求重述

       因数据类型大小的限制,为实现高位数的乘法运算,现在需要编码实现一个能够计算高位数的乘法运算。

2、问题分析

        要实现高精度乘法,那必然也逃不过前面讲述加减法时涉及到的思路框架了,基础核心在于数组,算法核心在于运算规则的编写。因此,经过前面两次的思想熏陶,这里我在此总结一下编写高精度算法的核心步骤以及编写乘法所不同的地方以及注意事项。

     1 大致步骤

1)构建字符数组以及存放长度的变量

2)构建整形数组存放逆序的转化后的数以及后续结果数

3)核心运算逻辑编写

包括两项 1 确定结果数组容量

                2 运算逻辑实现以及特殊情况处理

4)确定实际结果的数组长度,保证输出结果的准确性

5)逆序打印输出结果

     2 实现逻辑的区别

        关于乘法,如果是多位数乘法,在大的方面同样也是采用我们自己计算的方式---竖式,从小的方面看,乘法的运算和加减法有一些区别,什么区别呢?我们先看一下我们列竖式计算乘法的大致过程图:

        通过上图可以了解到,我们首先需要对第一个乘数的每一位上的数遍历一遍来与第二个乘数的位上的一个数分别作乘法,然后让第二个乘数的每一位都执行相同的操作,然后作进位处理,这样可能清楚一点,这就是关于乘法与加减法的区别:更加麻烦,遍历更多了。

        这其实也能隐隐约约感受到这个算法实现的一些思路了,那么接下来我们就正式进入斯露德构建与代码的实现了。

三、思路完善与代码实现

现在,我们就来按照前面所写的步骤来完善思路,实现代码。

1)定义字符数组和存放数组的长度的变量
2)构建整形数组,将字符数组转化并逆序存入

         基于前面两个算法(如果还不清楚,建议点击这里跳转至加法实现去看看)的讲解,这块已经算是相对比较简单的了,这里就不加赘述,直接上代码:

//构建两个字符数组以及对应存放数组长度的变量,以及输入
string str1, str2;
int len1, len2;
getline(cin, str1); //输入,回车即停止
len1 = str1.size();  //记录数组长度
getline(cin, str2);
len2 = str2.size();
//构建三个整形数组分别存放两个数以及结果数
int n1[1002] = { 0 }, n2[1002] = { 0 }, ret[2002] = { 0 },i,j;
//并转化成整形数组并逆序存存放
for (i = 0; i < len1; i++)
{
	n1[i] = str1[len1 - 1 - i] - '0';  //字符转整形的办法 字符-‘0’,然后逆序存放
}
for (i = 0; i < len2; i++)
{
	n2[i] = str2[len2 - 1 - i] - '0';
}
3)核心逻辑实现

   1、确定结果数的数组长度

       确定结果输的数组长度,也就是想办法找出两数相乘以后的数可能得最高的位数即可,因为确定好看你的最高的位数,那么在进行存放过程中就不容易出现溢出的情况了。那么如何找呢?我们可以通过举例看出:如23*4 = 96,23*9 = 207 ,300*4=1200 ...等等,我们容易看出n位数×m位数所得的结果数最多是(m+n)位数,因此我们就可以大致得到存放结果数的数组大小了。

代码实现如下:

	//确定大致结果数组长度
	int maxlen = len1 + len2;

   2、核心,乘法逻辑(分离进位处理,后续进行)

      接下来就是真正的重点了!!!前面,我们大致介绍了竖式计算乘法的方法,那么现在我们要让=用代码实现人的操作,应该如何操作呢?那么我们不妨来找一下通用的规律,然后就进行实现。这里我们利用画图解释一下:

       由图很明显可以看出规律:结果各个位上的数对应的下标=两数的各个位上的数对应下标的和,即 ret[i+j] = n1[i] + n2[j];

       接着,我们这些数的相加必然需要利用循环来实现,那么如何实现呢?前面说过,他是一个位上的数与另一个数上的各个位数上的数分别相乘,然后每一个位上的数执行的时候都会错位实现我们的加法,很明显,我们可以利用一个嵌套循环即可实现,大家可以思考一下看是不是这样:外层循环就是前者要与另一个数各个位分别相乘的数组遍历,内层循环就是被乘的数组遍历。然后目前我们发现了相加的规律因此计算机上直接按照规律相加即可。

       综上所述:在先不考虑进位的情况下,要实现乘法的计算实际上就是 各个相乘+按照规律遍历相加

代码实现如下:

//乘法运算编写,实现总结规律
for (i = 0; i < len1; i++)
{
	for (j = 0; j < len2; j++)
	{
		ret[i + j] += n1[i] * n2[j]; //相乘然后规律相加存放至数组
	}
}

  3、进位处理 

        进位,与前面加法进位是类似的,简单总结一下就是:

        判断是否大于10,若大于10则发生进位,先把原数除10得出进位数然后赋值给下一个数,然后把原数取余变成进位后的数即可。

代码实现如下:

	//处理进位
	for (i = 0; i < maxlen; i++)
	{
		if (ret[i] > 10)  //>10即会发生进位
		{
			ret[i + 1] += ret[i] / 10; //把进位数用找出来赋给下一位
			ret[i] %= 10; //剔出进位后,原数用取余实现更改
		}
	}
4) 确定实际结果位数,确定结果数组长度

       这个步骤也是老生常谈的了,与前面实现加减法一模一样,即 判断结果数组末尾是否为0,若为0则数组长度-1,清掉无用空间,否则就执行下一步。可以利用while循环实现

具体代码实现如下:

	//确定最终长度,清掉没用的空间
	while(ret[maxlen - 1] == 0&& maxlen > 1)
//maxlen>1是为了防止乘数含0时结果数组长度被意外-1而导致无法打印出来
		maxlen--;
5)最后,逆序打印输出结果即可

        我们说我们在进行运算时为方便编码,都是逆序以后进行的逻辑编写,因此最后输出时,还需要注意逆序再输出才是正确的结果。

具体代码实现如下:

	//逆序打印输出结果
	for (i = 0; i < maxlen; i++)
	{
		cout << ret[maxlen - 1 - i];
	}

届时,我们所有的代码讲述就完毕了!下面有总代码实现供大家观看! 

四、总代码实现

#include<iostream>
#include<string>
//高精度乘法
using namespace std;

int main()
{
	//构建两个字符数组以及对应存放数组长度的变量,以及输入
	string str1, str2;
	int len1, len2;
	getline(cin, str1);
	len1 = str1.size();
	getline(cin, str2);
	len2 = str2.size();
	//构建三个整形数组分别存放两个数以及结果数
	int n1[1002] = { 0 }, n2[1002] = { 0 }, ret[2002] = { 0 },i,j;
	//并转化成整形数组并逆序存存放
	for (i = 0; i < len1; i++)
	{
		n1[i] = str1[len1 - 1 - i] - '0';
	}
	for (i = 0; i < len2; i++)
	{
		n2[i] = str2[len2 - 1 - i] - '0';
	}
	//确定大致结果数组长度
	int maxlen = len1 + len2;
	//cout << maxlen;
	//乘法运算编写,实现总结规律
	for (i = 0; i < len1; i++)
	{
		for (j = 0; j < len2; j++)
		{
			ret[i + j] += n1[i] * n2[j];
		}
	}
	//处理进位
	for (i = 0; i < maxlen; i++)
	{
		if (ret[i] > 10)
		{
			ret[i + 1] += ret[i] / 10;
			ret[i] %= 10;
		}
	}
	//确定最终长度,清掉没用的空间
	while(ret[maxlen - 1] == 0&& maxlen > 1)
		maxlen--;
	//逆序打印输出结果
	for (i = 0; i < maxlen; i++)
	{
		cout << ret[maxlen - 1 - i];
	}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值