算法 —— 高精度(模拟)

目录

加法高精度

两个正整数相加

两个正小数相加

两正数相加

减法高精度

两个正整数相减

两个正小数相减

两正数相减

加减法总结

 乘法高精度

两个正整数相乘

两个正小数相乘

乘法总结


加法高精度

题目来源洛谷:P1601 A+B Problem(高精)

高精度:顾名思义,就是在很大的位数情况下进行运算,其基本思想就是用数组进行模拟加法进位,最后遍历数组输出。可以看到提示的a,b最大值达到10^18,而内置类型long long能接受数据的最大值在10^19,题目目的就是为了不让你内置类型来接收数据


两个正整数相加

我们可以使用字符串来模拟高精度运算,本题要求很少:只需要正整数相加即可,以 83 + 2047 为例模拟一下加法过程,我们可以看一下下图:

按照加法原则,个位与个位相加,十位与十位相加……注意相加的过程中要加上前一位的进位,注意最高位相加后要看进位d是否为0,代码如下:

// 整数部分 // 全是正数
string add_int(string a, string b)
{
	string ret; //存放最终结果
	int d = 0;// 记录进位
	int na = a.size(), nb = b.size();
	int n = max(na, nb);// 统一个数
	// 位数不够前面补0
	if (na > nb)
		for (int i = 1; i <= na - nb; i++)
			b = "0" + b;
	else
		for (int i = 1; i <= nb - na; i++)
			a = "0" + a;
	for (int i = n - 1; i >= 0; i--)
	{
		int tmp = 0, tmp_a = a[i] - '0', tmp_b = b[i] - '0';
		tmp = tmp_a + tmp_b + d; //加前一次的进位
		d = tmp / 10;
		tmp %= 10;
		ret.push_back(tmp + '0');
	}
	if (d != 0)
		ret.push_back(d + '0');
	reverse(ret.begin(), ret.end());
	return ret;
}

两个正小数相加

 根据此题,我又试着把大于0的小数相加设计了出来,这样使得原本加法更加完善,可以对大于等于0的所有数进行加法运算,但是需要注意一点的是,小数部分靠近小数点的为高位,意味着我们要在数字后面补0,以 0.99 和 0.4721 为例子,看以下图示:

根据上图可以用代码将其实现:

// 小数部分  // 全是正数
string add_float(string a, string b)
{
	string ret;
	int d = 0;// 记录进位
	int na = a.size(), nb = b.size();
	int n = max(na, nb);// 统一个数
	// 位数不够后面补0
	if (na > nb)
		for (int i = 1; i <= na - nb; i++)
			b = b + "0";
	else
		for (int i = 1; i <= nb - na; i++)
			a = a + "0";
	for (int i = n - 1; i >= 0; i--)
	{
		int tmp = 0, tmp_a = a[i] - '0', tmp_b = b[i] - '0';
		tmp = tmp_a + tmp_b + d; //加前一次的进位
		d = tmp / 10;
		tmp %= 10;
		ret.push_back(tmp + '0');
	}
	if (d != 0)// 说明要进位到整数
		ret.push_back('x'); 
	reverse(ret.begin(), ret.end());//前导x说明要整数部分+1
	return ret;
}

两正数相加

通过上述两个函数合并可以试着实现正数的加法,这里说一下我的思路:

  1. 找到两个字符串中的小数点
  2. 将字符串分为两个子串:左半部分为正整数子串,右半部分为正小数子串
  3. 两个部分分别调用上述函数
  4. 结果合并成一个字符串,该字符串为最终结果

 为了提高代码的可读性,本蒟蒻在每行代码后面添加了注释,大家可以试着看一下:

bz:本人考虑到小数部分相加刚好为1的情况,那么可以试着不要后面一大串全是0的子串,直接输出整数部分即可,这样更加贴合大家生活中的加法习惯。以下为代码:

// 加法
string add(string a,string b)
{
	auto it_a = a.find('.'), it_b = b.find('.');
	string a_int = a, b_int = b, a_float, b_float; // 默认a b是整数
	if (it_a != -1)// 找到小数点了就要分成两部分
	{
		a_int = a.substr(0, it_a); // 把整数部分裁下来
		a_float = a.substr(it_a + 1, a.size() - it_a); // 把小数部分裁下来
	}
	if(it_b != -1)
	{
		b_int = b.substr(0, it_b);
		b_float = b.substr(it_b + 1, b.size() - it_b); // 同上
	}
	string ad_float = add_float(a_float, b_float);
	if (ad_float[0] == 'x')//看看有没有前导x,有就需要进位
	{
		ad_float = ad_float.substr(1, ad_float.size() - 1); //把前导x舍弃
		a_int = add_int(a_int, "1"); // 小数进位只会加1
	}
	int count = 0;
	for (auto e : ad_float) // 看看结尾是不是全是0
		if (e == '0')
			count++;
	if (count == ad_float.size()) // 说明全是0,ret可以不要了
		ad_float.clear(); //清空
	string ad_int = add_int(a_int, b_int);
	string ret = ad_int;
	if (ad_float.size() != 0) // 说明里面有小数
		ret = ret + "." + ad_float;
	return ret;
}

减法高精度

减法高精度与加法高精度略有不同,其区别在于进位,加法的进位是把进位值给高一级位,而减法进位需要向高一级位借一位(前数比后数小的前提下)


两个正整数相减

减法的难点在于:它不像加法那样可以一位一位顺位相加,在借位的情况下,可能出现越位减一的情况,例如100 - 9,要从百位借位,这样大大增加了难度。

注意以下两个特殊情况:

  1. 两个相同数相减为0
  2. 低位数不够,向高位借1

根据加法的代码,可以考虑在加法的基础上进行修改,并且通过两个正整数的相减可以实现一正一负的加法运算,提高了代码的复用性。看以下一下代码:

// 整数部分 // 全是正数 // 大 - 小
string sub_int(string a, string b)
{
	string ret; // 存放最终结果
	int d = 0;// 记录进位
	int na = a.size(), nb = b.size();
	int n = max(na, nb);// 统一个数
	// 位数不够前面补0
	if (na > nb)
		for (int i = 1; i <= na - nb; i++)
			b = "0" + b;
	else
		for (int i = 1; i <= nb - na; i++)
			a = "0" + a;
	string maxn = a, minn = b; // 重新搞两个字符串(提高可读性)
	for (int i = n - 1; i >= 0; i--)
	{
		int tmp = 0, tmp_max = maxn[i] - '0' + d, tmp_min = minn[i] - '0'; // 进位放初始化
		d = 0;// 注意用完 d 归零
		if (tmp_max < tmp_min) // 借位情况(大数该位不够减小数该位)
		{
			tmp_max += 10; //问前面一位要了一个1
			d = -1;
		}
		tmp = tmp_max - tmp_min;
		ret.push_back(tmp + '0');
	}
	for (int i = n - 1; i >= 1; i--) //一样的数相减全是0删掉(注意留最后一个0)
	{
		if (ret[i] == '0')
			ret.pop_back();
		else
			break;
	}
	reverse(ret.begin(), ret.end());
	return ret;
}

两个正小数相减

正小数相减和相加类似,最后返回的字符串如果要借个位的一位就存放一个x,两函数合并时判断是否有x(需要向个位借一位的情况)即可,代码如下:

// 小数部分 // 全是正数 // 大 - 小
string sub_float(string a, string b)
{
	string ret; // 存放最终结果
	int d = 0;// 记录进位
	int na = a.size(), nb = b.size();
	int n = max(na, nb);// 统一个数
	// 位数不够后面补0
	if (na > nb)
		for (int i = 1; i <= na - nb; i++)
			b = b + "0";
	else
		for (int i = 1; i <= nb - na; i++)
			a = a + "0";
	string maxn = a, minn = b; // 重新搞两个字符串(提高可读性)
	for (int i = n - 1; i >= 0; i--)
	{
		int tmp = 0, tmp_max = maxn[i] - '0' + d, tmp_min = minn[i] - '0'; // 进位放初始化
		d = 0;// 注意用完 d 归零
		if (tmp_max < tmp_min) // 借位情况(大数该位不够减小数该位)
		{
			tmp_max += 10; //问前面一位要了一个1
			d = -1;
		}
		tmp = tmp_max - tmp_min;
		ret.push_back(tmp + '0');
	} 
	if (d == -1)// 整数部分也要减1
		ret.push_back('x');
	reverse(ret.begin(), ret.end());
	return ret;
}

两正数相减

与加法相同,裁剪小数和整数两部分字符串依次调用各自的函数即可,代码如下:

// 减法  // 默认 大 - 小
string sub(string a, string b)
{
	auto it_a = a.find('.'), it_b = b.find('.');
	string a_int = a, b_int = b, a_float, b_float; // 默认a b是整数
	if (it_a != -1)// 找到小数点了就要分成两部分
	{
		a_int = a.substr(0, it_a); // 把整数部分裁下来
		a_float = a.substr(it_a + 1, a.size() - it_a); // 把小数部分裁下来
	}
	if (it_b != -1)
	{
		b_int = b.substr(0, it_b);
		b_float = b.substr(it_b + 1, b.size() - it_b); // 同上
	}
	string su_float = sub_float(a_float, b_float);
	if (su_float[0] == 'x')//看看有没有前导x,有就需要借位
	{
		su_float = su_float.substr(1, su_float.size() - 1); //把前导x舍弃
		a_int = sub_int(a_int, "1"); // 小数进位只会加1
	}
	int count = 0;
	for (auto e : su_float) // 看看结尾是不是全是0
		if (e == '0')
			count++;
	if (count == su_float.size()) // 说明全是0,ret可以不要了
		su_float.clear(); //清空
	string su_int = sub_int(a_int, b_int);
	string ret = su_int;
	if (su_float.size() != 0) // 说明里面有小数
		ret = ret + "." + su_float;
	return ret;
}

加减法总结

 通过减法可以利用该函数实现加法中的正数加负数,当然在面对小减大的情况时,需要通过一个比较函数来判断两数的绝对值大小处理完之和直接在最终结果的字符串前头插一个” - " 即可,加减法无非以下几种情况:

                                                         加减法所有可能情况
式子形式大小比较结果大小
正数a + 正数b两者都大于0大于0
正数a + 负数b| a | >= | b |大于等于0
正数a + 负数b| a | < | b |小于0
负数a + 负数b两者都小于0小于0

正数a - 正数b

| a | >= | b |大于等于0
正数a - 负数ba >= 0    b < 0大于0
负数a - 正数ba < 0    b >= 0小于0
负数a - 负数b| a | >= | b |小于0

负数a - 负数b

| a | < | b |大于0

 以下代码为绝对值化处理和比较函数的内容,大家可以根据本蒟蒻上述代码尝试其他情况的代码编写。

// 绝对值化
if (a[0] == '-')
a = a.substr(1, a.size() - 1);
if (b[0] == '-')
b = b.substr(1, b.size() - 1);

// 比较函数
bool my_cmp(string a, string b) // 补0 且 绝对值化后进行比较
{
	// 默认 a > b
	for (int i = 0; i < a.size(); i++)
		if (a[i] < b[i])
			return false;
	return true;
}

 乘法高精度

本题来自洛谷题库:P1303 A*B Problem,相比加减法的高精度,乘法的高精度相对麻烦一些,但是归根结底还是运用字符串来实现位数的变化,其主要区别在于进位,加减法的进位只会是1,但是乘法的进位可以达到8(9 * 9 = 81),本文不探讨除法的高精度,个人认为除法的高精度不常见,接下来我们试着解决上题。

注意特殊情况:0和任何数相乘都为0!!!


两个正整数相乘

以 679 x 58 为例,看以下图片如何模拟该乘法过程:

 可以看到乘法内部实际为多个加法操作的求和,我们可以在上面的加法操作上进行修改:

// 整数部分 // 全是正数
string mul_int(string a, string b)
{
	if (a == "0" || b == "0")//处理特例
		return "0";
	string ret = "0"; // 存放最终结果
	int d = 0;// 记录进位
	string longer = a, shorter = b; // 假设法
	if (a.size() < b.size())
		swap(longer, shorter);
	int n = shorter.size(); // 乘的次数
	reverse(longer.begin(), longer.end()); reverse(shorter.begin(), shorter.end()); //逆置后位数顺序
	for (int i = 0; i < n; i++)
	{
		string str; // 临时存放数据
		for (int j = 0; j < longer.size(); j++)
		{
			int tmp = 0, tmp_longer = longer[j] - '0', tmp_shorter = shorter[i] - '0';
			tmp = tmp_longer * tmp_shorter + d; //加上进位
			d = tmp / 10;
			tmp %= 10;
			str.push_back(tmp + '0');
		}
		if (d != 0) //比原数多一位
			str.push_back(d + '0');
		reverse(str.begin(), str.end());
		for (int z = 1; z <= i; z++) // 从十位开始就要后面补0
			str.push_back('0');
		ret = add_int(ret, str);// 调用上述两整数相加运算
		d = 0; // 用完复位
	}
	return ret;
}

两个正小数相乘

小数相乘可以利用上面的正整数相乘,回想一下小学时期如何解决小数相乘问题的?

本蒟蒻总结了以下几个步骤:

  1. 计算两数小数点后几位之和
  2. 小数前面的0全部去掉
  3. 整数相乘
  4. 添加小数点

光看文字可能有些抽象,我们来看图片:

 根据上面图片是不是思路更加清晰了呢?下面我们来实现代码操作:

// 小数部分 // 全是正数
string mul(string a, string b)
{
	if (a == "0" || b == "0")//处理特例
		return "0";
	auto it_a = a.find('.'), it_b = b.find('.');
	string a_int = a, b_int = b, a_float, b_float; // 默认a b是整数
	if (it_a != -1)// 找到小数点了就要分成两部分
	{
		a_int = a.substr(0, it_a); // 把整数部分裁下来
		a_float = a.substr(it_a + 1, a.size() - it_a); // 把小数部分裁下来
	}
	if (it_b != -1)
	{
		b_int = b.substr(0, it_b);
		b_float = b.substr(it_b + 1, b.size() - it_b); // 同上
	}
	int n = a_float.size() + b_float.size();
	if (a_int == "0")
		a_int.clear();
	if (b_int == "0")
		b_int.clear();
	a = a_int + a_float, b = b_int + b_float; // 拼起来
	string ret = mul_int(a, b); int nret = ret.size();
	if (n >= nret)// 不够还要补0
	{
		//为什么不用insert? 如果要头插大量0,insert时间复杂度太高
		reverse(ret.begin(), ret.end()); //逆置尾插0
		for (int i = 0; i <= n - nret; i++)
			ret.push_back('0');
		ret.insert(n, 1, '.'); //这里的insert只要挪一个0就行  好好思考为什么
		reverse(ret.begin(), ret.end());
	}
	else
		ret.insert(nret - n, 1, '.');
	return ret;
}

上述代码我将含有小数和整数的数字合并在一起,该函数可以处理各种正数相乘。


乘法总结

乘法一共有以下几种情况,需要注意以下两点:

  1. 两个小数相乘不会向整数位进位
  2. 0乘任何数都为0

乘法需要考虑以下所有情况: 

                                                     乘法所有可能情况
式子形式大小比较结果大小
0 x 任何数0
正数a x 正数b两者都大于0大于0
正数a x 负数ba > 0   b < 0小于0
负数a x 负数ba < 0   b < 0大于0
整数a x 小数b两者都大于0(相当于除法)大于0
小数a x 小数b两者都大于0大于0且小于1

最后附上以上的测试代码,供大家调试:

int main()
{
	string sa, sb, ans; cin >> sa >> sb;
	//ans = add_int(sa, sb);
	//ans = add_float(sa, sb);
	//ans = add(sa, sb);
	//ans = sub_int(sa, sb);
	//ans = sub_float(sa, sb);
	//ans = sub(sa, sb);
	//ans = mul_int(sa, sb);
	ans = mul(sa, sb);
	cout << ans << endl;
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/801566.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

JVM知识点

一、java内存区域与内存异常 1、运行时数据区域 1&#xff09;程序计数器 程序计数器&#xff08;Program Counter Register&#xff09;是一块较小的内存空间&#xff0c;它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里 [1] &#xff0c;字节码…

降本增效CRKD:通过跨模态知识蒸馏增强相机与毫米波雷达目标检测精度

Abstract 在自动驾驶的3D目标检测领域&#xff0c;激光雷达-摄像头&#xff08;LC&#xff09;融合是表现最好的传感器配置。然而&#xff0c;激光雷达的成本相对较高&#xff0c;这阻碍了该技术在消费者汽车中的普及。相反&#xff0c;摄像头和雷达已经普遍部署在现有车辆上&…

Springboot整合MyBatis实现数据库查询(二)

目录 第一章、准备1.1&#xff09;准备数据库表1.2&#xff09;创建springboot项目&#xff0c;添加依赖1.3&#xff09;使用mybatis逆向工程 第二章、代码开发2.1&#xff09;建包并编写代码2.2&#xff09;application配置文件2.3&#xff09;设置编译位置 第三章、测试访问3…

用HTML和CSS实现提示工具(tooltip)及HTML元素的定位

所谓提示工具&#xff0c;是指将鼠标移动到某个HTML元素&#xff08;工具&#xff09;时会显示一些提示内容&#xff08;提示文本&#xff09;&#xff0c;而鼠标移出工具元素的范围时提示文本就消失了。考虑到提示文本元素应当在鼠标进入工具元素时显示&#xff0c;鼠标离开工…

JDK之使用keytool安装cer证书

可针对https请求缺失证书解决报错&#xff1a; PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 解决办法&#xff1a; 先通过浏览器下载证书&#xff0c;再使用JDK自带…

互联网末法时代的一些思考

这篇文章也是临时起意&#xff0c;很长一段时间没写个人思考类的文章&#xff0c;主要原因也是时间完全不够用。随着年龄的增长&#xff0c;看待问题的视角也逐渐发生变化&#xff0c;例如从关注现象到关注动机&#xff0c;从关注结果到关注起因&#xff0c;2021年的时代我曾经…

时间序列问题解题(基于经验模型,使用机器学习模型)(Datawhale AI 夏令营)

示例题目&#xff1a;2024 iFLYTEK A.I.开发者大赛-讯飞开放平台 (xfyun.cn) 一&#xff0c;时间序列问题概述 1、时间序列问题定义 时间序列问题是一类重要的统计和数据分析问题&#xff0c;它涉及对按时间顺序排列的数据点进行分析、建模和预测。时间序列数据是由一系列随时…

【Apache Doris】周FAQ集锦:第 14 期

【Apache Doris】周FAQ集锦&#xff1a;第 14 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户…

支持CF高帧率的免费虚拟机系统

分享一个支持CF高帧率的免费虚拟机系统&#xff0c;这个是某UP主分享的&#xff0c;帧率也是能到两百帧吧&#xff0c;内存这些我开的是6h6g的&#xff0c;具体还是得看你们自己的电脑配置&#xff01;文件较大&#xff0c;请先保存再下载&#xff0c;因为我也不知道哪天取消分…

Julia 初学者指南(一) | 安装、配置及编译器

唠唠闲话 Julia 是一种高性能的动态编程语言&#xff0c;特别适用于数值分析和计算科学领域。它拥有一个强大的类型系统和灵活的多重分派机制&#xff0c;这使得代码易于编写同时还能保持接近 C 语言的运行速度。此外&#xff0c;Julia 也能无缝调用 C 和 Fortran 库&#xff0…

有关电力电子技术的一些相关仿真和分析:⑤交-直-交全桥逆变+全波整流结构电路(MATLAB/Siumlink仿真)

全桥逆变+全波整流结构 参数:Vin=500V, Vo=200V, T=2:1:1, RL=10Ω, fs=100kHz, L=1mH, C=100uF (1)给定输入电压,输出电压和主电路参数,仿真研究电路工作原理,分析工作时序; (2)调节负载电阻,实现电流连续和断续,并仿真验证; (3)调节占空比,分析占空比与电…

公司想无偿裁员,同事赖着不走

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 这招好像也不错! 事情是这样的&#xff1a;某公司准备把成本高的员工都裁掉&#xff0c;主要包含研发部和程序员&#xff0c;总共18个人&#xff0c;准备裁掉10人&#xff0c;因为他们工资开的太高了&#xff0c;…

ROS-机械臂——从零构建机器人模型

URDF建模 URDF URDF&#xff0c;全称为 Unified Robot Description Format&#xff08;统一机器人描述格式&#xff09;&#xff0c;是一种用于描述机器人几何结构和运动学属性的标准文件格式。URDF 文件通常用于机器人模拟、路径规划、控制算法开发和可视化等领域&#xff0c…

信号和槽机制的轻量级实现,sigslot 库介绍及使用

Qt中的信号与槽机制很好用&#xff0c;然而只在Qt环境中。在现代 C 编程中&#xff0c;对象间的通信是一个核心问题。为了解决这个问题&#xff0c;许多库提供了信号和槽&#xff08;Signals and Slots&#xff09;机制。今天推荐分享一个轻量级的实现&#xff1a;sigslot 库。…

AWS CDN新增用户ip 地区 城市 响应头

1.需要自定义cdn缓存策略 这里的策略也是先复制之前的cdn策略哈 最后复制完了 全部新增这两条标头key CloudFront-Viewer-Country CloudFront-Viewer-City 2.然后新增cdn函数&#xff0c;应用你写的这个函数 function handler(event) {var request event.request;var respon…

全国农产品地理标志登记汇总表(截至2022年2月25日)

数据来源&#xff1a;自主整理 数据范围&#xff1a;省级层面 数据数量&#xff1a;3510条数据指标&#xff1a; 本数据展示了截至2022年2月25日的全国农产品地理标志登记汇总表&#xff0c;具体指标展示如下表&#xff1a; 序号 年份 产品名称 所在地域 证书持有人…

【每日刷题】Day81

【每日刷题】Day81 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 日期累加_牛客题霸_牛客网 (nowcoder.com) 2. 打印日期_牛客题霸_牛客网 (nowcoder.com) 3. 2956.…

分享两个性价比极高的SSR方案

最近总监提出我们公司运营的一个网站运营数据有点差&#xff0c;亟待提升该网站的SEO&#xff08;搜索引擎优化&#xff09;体验。不然自然流量着实有点少&#xff0c;全靠氪金买百度付费流量&#xff0c;成本太高&#xff0c;显然不太现实。但是当时技术选型的时候并未考虑到S…

【Linux】权限的管理和Linux上的一些工具

文章目录 权限管理chgrpchownumaskfile指令sudo指令 目录权限粘滞位Linux中的工具1.软件包管理器yum2.rzsz Linux开发工具vim 总结 权限管理 chgrp 功能&#xff1a;修改文件或目录的所属组 格式&#xff1a;chgrp [参数] 用户组名 文件名 常用选项&#xff1a;-R 递归修改文…

解析 Mira :基于 Web3,让先进的 AI 技术易于访问和使用

“Mira 平台正在以 Web3 的方式解决当前 AI 开发面临的复杂性问题&#xff0c;同时保护 AI 贡献者的权益&#xff0c;让他们可以自主拥有并货币化自己的模型、数据和应用&#xff0c;以使先进的 AI 技术更加易于访问和使用。” AI 代表着一种先进的生产力&#xff0c;它通过深…