算法 —— 高精度(模拟)

目录

加法高精度

两个正整数相加

两个正小数相加

两正数相加

减法高精度

两个正整数相减

两个正小数相减

两正数相减

加减法总结

 乘法高精度

两个正整数相乘

两个正小数相乘

乘法总结


加法高精度

题目来源洛谷: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);// 统一个数// 位数不够前面补0if (na > nb)for (int i = 1; i <= na - nb; i++)b = "0" + b;elsefor (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);// 统一个数// 位数不够后面补0if (na > nb)for (int i = 1; i <= na - nb; i++)b = b + "0";elsefor (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说明要整数部分+1return 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) // 看看结尾是不是全是0if (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);// 统一个数// 位数不够前面补0if (na > nb)for (int i = 1; i <= na - nb; i++)b = "0" + b;elsefor (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; //问前面一位要了一个1d = -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();elsebreak;}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);// 统一个数// 位数不够后面补0if (na > nb)for (int i = 1; i <= na - nb; i++)b = b + "0";elsefor (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; //问前面一位要了一个1d = -1;}tmp = tmp_max - tmp_min;ret.push_back(tmp + '0');} if (d == -1)// 整数部分也要减1ret.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) // 看看结尾是不是全是0if (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 > bfor (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++) // 从十位开始就要后面补0str.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()); //逆置尾插0for (int i = 0; i <= n - nret; i++)ret.push_back('0');ret.insert(n, 1, '.'); //这里的insert只要挪一个0就行  好好思考为什么reverse(ret.begin(), ret.end());}elseret.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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

老物件线上3D回忆展拓宽了艺术作品的展示空间和时间-深圳华锐视点

在数字技术的浪潮下&#xff0c;3D线上画展为艺术家们开启了一个全新的展示与销售平台。这一创新形式不仅拓宽了艺术作品的展示空间&#xff0c;还为广大观众带来了前所未有的观赏体验。 3D线上画展制作以其独特的互动性&#xff0c;让艺术不再是单一的视觉享受。在这里&#x…

220V降5V芯片输出电压电流封装选型WT

220V降5V芯片输出电压电流封装选型WT 220V降5V恒压推荐&#xff1a;非隔离芯片选型及其应用方案 在考虑220V转低压应用方案时&#xff0c;以下非隔离芯片型号及其封装形式提供了不同的电压电流输出能力&#xff1a; 1. WT5101A&#xff08;SOT23-3封装&#xff09;适用于将2…

勒索防御第一关 亚信安全AE防毒墙全面升级 勒索检出率提升150%

亚信安全信舷AE高性能防毒墙完成能力升级&#xff0c;全面完善勒索边界“全生命周期”防御体系&#xff0c;筑造边界勒索防御第一关&#xff01; 勒索之殇&#xff0c;银狐当先 当前勒索病毒卷携着AI技术&#xff0c;融合“数字化”的运营模式&#xff0c;形成了肆虐全球的网…

SpringBoot使用RedisTemplate、StringRedisTemplate操作Redis

前言 RedisTemplate 是 Spring Boot 访问 Redis 的核心组件&#xff0c;底层通过 RedisConnectionFactory 对多种 Redis 驱动进行集成&#xff0c;上层通过 XXXOperations 提供丰富的 API &#xff0c;并结合 Spring4 基于泛型的 bean 注入&#xff0c;极大的提供了便利&#x…

【自学安全防御】二、防火墙NAT智能选路综合实验

任务要求&#xff1a; &#xff08;衔接上一个实验所以从第七点开始&#xff0c;但与上一个实验关系不大&#xff09; 7&#xff0c;办公区设备可以通过电信链路和移动链路上网(多对多的NAT&#xff0c;并且需要保留一个公网IP不能用来转换) 8&#xff0c;分公司设备可以通过总…

网络安全防御【防火墙安全策略用户认证综合实验】

目录 一、实验拓扑图 二、实验要求 三、实验思路 四、实验步骤 1、打开ensp防火墙的web服务&#xff08;带内管理的工作模式&#xff09; 2、在FW1的web网页中网络相关配置 3、交换机LSW6&#xff08;总公司&#xff09;的相关配置&#xff1a; 4、路由器相关接口配置&a…

connect by prior 递归查询

connect by prior 以公司组织架构举例&#xff0c;共四个层级&#xff0c;总公司&#xff0c;分公司&#xff0c;中心支公司&#xff0c;支公司 总公司level_code为1 下一层级的parent_id为上一层级的id&#xff0c;建立关联关系 SELECT id, name, LEVEL FROM org_info a STA…

海事无人机解决方案

海事巡察 海事巡察现状 巡查效率低下&#xff0c;存在视野盲区&#xff0c;耗时长&#xff0c;人力成本高。 海事的职能 统一管理水上交通安全和防治船舶污染。 管理通航秩序、通航环境。负责水域的划定和监督管理&#xff0c;维护水 上交通秩序&#xff1b;核定船舶靠泊安…

使用自制Qt工具配合mitmproxy进行网络调试

在软件开发和网络调试过程中&#xff0c;抓包工具是不可或缺的。传统的抓包工具如Fiddler或Charles Proxy通常需要设置系统代理&#xff0c;这会抓到其他应用程序的网络连接&#xff0c;需要设置繁琐的过滤&#xff0c;导致不必要的干扰。为了解决这个问题&#xff0c;我们可以…

linux中关于环境变量的常用的设置方法

一. linux中设置环境变量的方式 1.使用/etc/environment, 是一个全局的环境变量设置文件&#xff0c;它会影响到所有用户和所有进程。当你需要设置一个全局的环境变量时&#xff0c;应该使用这个文件。这个文件的格式是 KEYvalue&#xff0c;每行一个环境变量。 2. 使用/etc/…

【unity笔记】常见问题收集

一 . Unity Build GI data 卡住问题 解决: 参考官方文档&#xff0c;GI(Global Illumination) data 指的是全局照明信息。 在Unity的Edit->Preference中&#xff0c;可以编辑GI缓存路径和分配GI缓存大小。 调出Window->Rendering->Lighting窗口&#xff0c;取消勾选…

【Caffeine】⭐️SpringBoot 项目整合 Caffeine 实现本地缓存

目录 &#x1f378;前言 &#x1f37b;一、Caffeine &#x1f37a;二、项目实践 2.1 环境准备 2.2 项目搭建 2.3 接口测试 ​&#x1f49e;️三、章末 &#x1f378;前言 小伙伴们大家好&#xff0c;缓存是提升系统性能的一个不可或缺的工具&#xff0c;通过缓存可以避免大…

基于SpringBoot+VueJS+微信小程序技术的图书森林共享小程序设计与实现:7000字论文+源代码参考

博主介绍&#xff1a;硕士研究生&#xff0c;专注于信息化技术领域开发与管理&#xff0c;会使用java、标准c/c等开发语言&#xff0c;以及毕业项目实战✌ 从事基于java BS架构、CS架构、c/c 编程工作近16年&#xff0c;拥有近12年的管理工作经验&#xff0c;拥有较丰富的技术架…

Java面试八股之Redis哨兵机制

Redis哨兵机制 Redis Sentinel&#xff08;哨兵&#xff09;模式是一种高可用解决方案&#xff0c;用于监控和自动故障转移Redis主从集群。以下是对哨兵模式详细过程的描述&#xff1a; 1. 初始化与配置 部署哨兵节点&#xff1a;在不同的服务器上部署一个或多个Redis Sentin…

leetcode 周赛(406)全AC留念

纪念第一次 leetcode 周赛&#xff08;406&#xff09;全AC 1.(100352. 交换后字典序最小的字符串) 题目描述&#xff1a; 给你一个仅由数字组成的字符串 s&#xff0c;在最多交换一次 相邻 且具有相同 奇偶性 的数字后&#xff0c;返回可以得到的 字典序最小的字符串 。 如…

ubantu22.04安装OceanBase 数据库

1、管理员启动cmd,运行 sudo bash -c "$(curl -s https://obbusiness-private.oss-cn-shanghai.aliyuncs.com/download-center/opensource/service/installer.sh)" 2、提示如下代表安装完成 3、修改数据库配置文件的密码 sudo vim /etc/oceanbase.cnf 然后保存退…

初学SpringMVC之 JSON 篇

JSON&#xff08;JavaScript Object Notation&#xff0c;JS 对象标记&#xff09;是一种轻量级的数据交换格式 采用完全独立于编程语言的文本格式来存储和表示数据 JSON 键值对是用来保存 JavaScript 对象的一种方式 比如&#xff1a;{"name": "张三"}…

Redis实战—附近商铺、用户签到、UV统计

本博客为个人学习笔记&#xff0c;学习网站与详细见&#xff1a;黑马程序员Redis入门到实战 P88 - P95 目录 附近商铺 数据导入 功能实现 用户签到 签到功能 连续签到统计 UV统计 附近商铺 利用Redis中的GEO数据结构实现附近商铺功能&#xff0c;常见命令如下图所示。…

牛客TOP101:合并两个排序的链表

文章目录 1. 题目描述2. 解题思路3. 代码实现 1. 题目描述 2. 解题思路 与正常的合并两个有序数组思路一样&#xff0c;这里可以定义一个头节点&#xff08;虚拟节点&#xff09;&#xff0c;可以方便我们一开始进行连接。用两个指针标记两个链表的结点&#xff0c;进行循环比较…

5.4 软件工程-系统设计

系统设计 - 概述 设计软件系统总体结构 数据结构及数据库设计 编写概要设计文档、评审 详细设计的基本任务 真题