基础算法--高精度数据(1)

news2024/11/24 21:02:07

高精度数据处理一般内容简单,写代码难度较大,可能部分内容涉及基础数学、初等数论等知识。请小心食用。不过本节不会给大家太难的高精度处理,我们第一次接触,不能劝退大家对吧。

高精度算法是指,利用基础或高级的数学计算原理,来解决题目给出的数据量超出计算机正常存储范围的数据的一个方式。

开胃小菜-乘法

那么我们先来写个题练一下吧:信息学奥赛一本通(C++版)在线评测系统 (ssoier.cn)

一本通1307-高精度乘法:
题目描述:

输入两个高精度正整数M和N(M和N均小于100位)。求这两个高精度数的积。

输入:

输入两个高精度正整数M和N

输出:

求这两个高精度数的积

大眼一看这不就是输出a*b嘛,但是M和N最大都是100位的数啊,怎么存的下1e100啊。更何况,两个100位数相乘得到的10000位数呢。(1位数相乘最大得到9*9=81,两位数;2位数相乘最大得到99*99=9801,四位数......)

那我们不妨想一下,乘法到底是怎么算的,假设两数为a和b(a的位数>=b的位数),那么取b的个位,乘a的所有位数,以后每次都向左偏移一位,依次取完b的位数个数字,结果相加。(我表达的可能不是很好,但大家都能回想起来惩罚的运算规则了,这就足够了)。给大家个图看一下吧:

编码时,我们选择数组不就行了吗,模拟位数,100位,10000位的位数还是蛮充足的。

下面大家自己写一下代码,我也去写一下,别偷看哦,动手时的你脑子想的更多,远比单纯动脑思考有用。

第一步--逻辑清晰,层次分明

首先我们将主函数的架构搭好,我们的逻辑是怎么样的:

在主函数外,我们还有一些维护数位的数组,中间临时数组,结果数组。并合理分配空间。并设置全局变量参与运算的数据的位数,避免了传参的麻烦。设置结果的位数变量ans_size。

#include <bits/stdc++.h>
using namespace std;

int a[100 + 10];//乘数
int b[100 + 10];//被乘数
int t[100 + 10];//中间数
int ans[10000 + 10];//结果
int ans_size;//ans最终的位数
int len_a, len_b;//分别是a的位数和b的位数

int main() {
	init();//初始化a、b数组数据
	for (int i = 1; i <= len_b; i++) {
		ans_size = calculate(b[i], i);
        //依次从b中取一位-b[i],并传入第几位-i
        //每次更新最终答案位数
	}
	Output(ans_size);
    //从第ans_size位依次往前输出,知道第1位输出完毕,构成的一串字符就是计算的结果
	return 0;
}

 第二步--根据题意,输入数据

输入的数据位数太多,整型家族的数据都存不下,那我们不妨存在字符串中,字符串存100个字符还不是轻而易举。同时我们保证输入的第一个数据位数大于等于第二个(这是我们参与数学运算时,习惯将长的数a放在上面,短的数b在下面,依次取位b[i]),而且,为了保证数据的高低位,我们将字符串逆置,这样低位在前,高位在后,虽然不符合我们的书写逻辑,但符合我们的运算逻辑。同时不要忘了将数字字符转化为数字时需要减去‘0’(根据ASCII码值进行运算转换)。

void init() {
	string st1, st2;
	cin >> st1 >> st2;
	if (st2.size() > st1.size())swap(st1, st2);//保证数a位数多,保留数学运算习惯
	reverse(st1.begin(), st1.end());//类似于栈的出入
	reverse(st2.begin(), st2.end());
	len_a = st1.size(), len_b = st2.size();
	for (int i = 0; i < len_a; i++) {
		a[i + 1] = st1[i] - '0';
	}
	for (int i = 0; i < len_b; i++) {
		b[i + 1] = st2[i] - '0';
	}
}

第三步--输出简单,提前安排

输出时,我们只需要传入一个参数(结果的位数),不传也可以,直接使用全局变量。

然后为了保证我们的结果位数准确,我们可能会在正确位数前多置零一位,此时我们需要判断,这是几位数,如果是一位数,那是0就是0,不用管,如果大于一位数,那前面的零我就不要了,也就是数组有效位数减一(我们使用while更加保险,if一次也是完全可以的),然后依次输出即可。输出这没什么说的了吧。重要的核心代码还是计算。

void Output(int n) {
	//输出第一位可能为0,最终未产生多余进位
	//if(n > 1 && 0 == ans[n]) n--;
	while (n > 1 && 0 == ans[n]) n--;

	for (int i = n; i > 0; i--) {
		cout << ans[i];
	}cout << endl;
}

第四步--核心代码,返璞归真 

首先将这是b的第几位暂时存下来,因为我们的每一位与乘数的结果临时储存在t中,我们需要从这第idx位开始往ans中放。

给你画个图你理解一下计算的过程:第一位9乘99999,我们的结果从第一位开始放在ans中,第二位9乘99999,我们的结果不放在个位了,从十位开始放。......依次类推,所以第几位b[i]乘a还是有必要存下来的。

存下来之后我们开始第一步:计算中间结果:(x为传进来的b的某一位)

本位结果t[i]=x*a[i]+上一位进位t[i-1]/10

上一位结果t[i-1]更新为t[i-1]%=10。(因为我们是十进制运算)

当x与a所有位上的数相乘完之后,我们不确定最高位存储的数据是不是一位数,(有可能是两位数),我们需要额外多维护一位,

第二步,将中间结果加到最终结果中:看上文,我们从第几位开始加?对的,第idx位(传的是b的第几位数,我就从第几位加)

while循环是为了将临时结果全部加到最终结果中

下面不加if判断也行,但是加上更容易我们理解。

这次不需要乘,只需要我们相加,然后满足十进制,跟上一步的思路差不多

最后也需要多维护一位。并返回最高位的数字。

int calculate(int x, int idx) {
	int ans_start_pos = idx;//答案从第几位开始加
	for (int i = 1; i <= len_a; i++) {//依次进位
		t[idx] = (x * a[i] + t[idx - 1] / 10);
		t[idx - 1] %= 10;
		idx++;
	}
	/*中间值最终进位*/
	t[idx] = t[idx - 1] / 10;
	t[idx - 1] %= 10;

	int cur_pos = ans_start_pos;//就是初始的idx
	while (cur_pos <= idx) {
		if (ans[cur_pos - 1] > 9) {
			ans[cur_pos] += ans[cur_pos - 1] / 10;
			ans[cur_pos - 1] %= 10;
		}
		ans[cur_pos] += t[cur_pos];
		cur_pos++;
	}
	/*结果最终进位*/
	ans[cur_pos] = ans[cur_pos - 1] / 10;
	ans[cur_pos - 1] %= 10;

	return cur_pos;
}

参考答案--乘法

总的代码如下:(有一些赘余的变量,但无伤大雅,考虑一下将中间结果数组去掉,只保留结果数组能不能运算?指定是能的,作为你学会本题思路后的额外思考)

#include <bits/stdc++.h>
using namespace std;

int a[100 + 10];//乘数
int b[100 + 10];//被乘数
int t[100 + 10];//中间数
int ans[10000 + 10];//结果
int ans_size;//ans最终的位数
int len_a, len_b;//分别是a的位数和b的位数
void init() {
	string st1, st2;
	cin >> st1 >> st2;
	if (st2.size() > st1.size())swap(st1, st2);//保证数a位数多,保留数学运算习惯
	reverse(st1.begin(), st1.end());//类似于栈的出入
	reverse(st2.begin(), st2.end());
	len_a = st1.size(), len_b = st2.size();
	for (int i = 0; i < len_a; i++) {
		a[i + 1] = st1[i] - '0';
	}
	for (int i = 0; i < len_b; i++) {
		b[i + 1] = st2[i] - '0';
	}
}
int calculate(int x,int idx) {
	int ans_start_pos = idx;//答案从第几位开始加
	for (int i = 1; i <= len_a; i++) {//依次进位
		t[idx] = (x * a[i] + t[idx - 1] / 10);
		t[idx - 1] %= 10;
		idx++;
	}
	/*中间值最终进位*/
	t[idx] = t[idx - 1] / 10;
	t[idx - 1] %= 10;

	int cur_pos = ans_start_pos;
	while (cur_pos <= idx) {
		if (ans[cur_pos - 1] > 9) {
			ans[cur_pos] += ans[cur_pos - 1] / 10;
			ans[cur_pos - 1] %= 10;
		}
		ans[cur_pos] += t[cur_pos];
		cur_pos++;
	}
	/*结果最终进位*/
	ans[cur_pos] = ans[cur_pos - 1] / 10;
	ans[cur_pos - 1] %= 10;

	return cur_pos;
}
void Output(int n) {
	//输出第一位可能为0,最终未产生多余进位
	//if(n > 1 && 0 == ans[n]) n--;
	while (n > 1 && 0 == ans[n]) n--;
	
	for (int i = n; i > 0; i--) {
		 cout << ans[i];
	}cout << endl;
}
int main() {
	init();//初始化a、b数据
	for (int i = 1; i <= len_b; i++) {
		ans_size=calculate(b[i],i);//依次从b中取一位b[i],第几位i
	}
	Output(ans_size);
	return 0;
}

 提交结果:AC,通过了。


好不容易哦,这才一道简单的乘法,就这么难理解了?但你从中学到的不仅仅只有这一点。例如:你知道了用数组元素存储数位;你知道了朴实无华的运算规律/方法;你知道了完成一道题的分析步骤;你初步知道如何处理高精度数据了......只要你认真看完了,思考了,学会了,你收获的远不止这些。哪里不懂,尽管留言,我看到就回复你。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pygame—炸弹牌(可做课设)

游戏介绍 在5X5的数字宫格里翻牌&#xff0c;翻出所有的2和3即可获胜每一格只能是0、1、2、3&#xff0c;第六列和最第六行为 X | Y&#xff0c;X代表该列或该行的数字总和&#xff0c;Y代表该列或该行的0的个数控制难度&#xff0c;每行每列的数字总和不超过9该游戏需要一定运…

Vue3学习笔记之数据绑定篇(0823)

学习完Vue2 的C友们&#xff0c;今天继续追赶Vue3的大潮流吧&#xff01; 废话不多说&#xff0c;直接上代码 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport" content"…

MobaXterm接触session会话保存14个的限制

问题描述 在我们使用MobeXterm的过程中&#xff0c;发现session保存了14个之后&#xff0c;再无法继续保存了&#xff1b; 原因是免费版本的MobeXterm的最大个数被限制了&#xff0c;需要进行破解&#xff1b; MobaXterm-keygen解除session保存限制的python脚本 可以使用上面…

计算机的错误计算(七十一)

摘要 计算机的错误计算&#xff08;七十&#xff09;探讨了大数的正割函数的错误计算。本节讨论另外一类数值&#xff1a; 附近数 的正割函数的计算精度问题。 例1. 已知 计算 若用 在 Python下编程计算&#xff0c;则有 若在 Excel 中计算&#xff0c;则有&#xff1a…

Xmind 在线导图上线!多设备实时同步,节约本地空间

在现代职场上&#xff0c;高效的工作方法对于提升个人和团队的生产力至关重要。 Xmind 作为一款领先的思维导图软件&#xff0c;最近推出了其在线版本&#xff0c;旨在帮助我们解决在工作中常见的 「掉线状态」 问题&#xff0c;并提升工作效率。 在日常工作中&#xff0c;我们…

抖音如何去水印导出,3种高效工具让你轻松掌握

在抖音上&#xff0c;我们经常会遇到一些精彩视频想要保存下来&#xff0c;但视频上往往带有水印&#xff0c;影响了观看和分享的体验。下面&#xff0c;我将介绍三种去除抖音视频水印的方法&#xff0c;让你轻松保存无水印视频。 技巧一&#xff1a;奈斯水印助手(小程序) 这是…

基于大语言模型的物联网(artificial intelligence of thing)

与当下热门的AI类似&#xff0c;曾几何时&#xff0c;物联网&#xff08;Internet of thing&#xff09;实现“万物互联"给人类带来了无限的遐想。但是往往事与愿违&#xff0c;美好的愿景并没有如约而至。十几年来&#xff0c;物联网远没有实现”万物互联“的美好愿景。 …

Kafka·Producer

Producer发送原理 拦截器进行拦截 对key和value进行序列化 org.apache.kafka.clients.producer.KafkaProducer#doSend 分区选择 计算消息要发送到topic的哪个分区上 若指定了分区&#xff0c;则使用指定的值没有指定的话则使用分区器计算得到或者使用hash取余的方式 暂存…

Stm32通过SPI读写W25QXX

Printf的重定向 因为printf是c中的库函数&#xff0c;要使用printf输出到串口&#xff0c;需要重定向&#xff0c;将printf定向到HAL_UART_Transmit。 新建一个retarget.c文件。 #include "stdio.h" #include "stm32f1xx_hal.h" #include "usart.h&…

创意无限,尽在掌握:热门视频剪辑软件一览

我们记录生活、分享故事、传播信息用视频的频率越来越高了。而这些视频往往都是通过剪辑之后才能展示出当前的效果。那这次我们就来探索剪辑视频的时候都会用到什么工具吧。 1.福昕视频剪辑 连接直达>>https://www.pdf365.cn/foxit-clip/ 这是一款专为追求高效与创意…

Pytorch 张量运算函数(补充)

mean() mean()函数是进行张量均值计算的函数,常用参数可以设置参数dim来进行对应维度的均值计算 以下是使用一个二维张量进行演示的例子 import numpy as np import torch device torch.device(mps if torch.backends.mps.is_available() else cpu) print(device ) data1 …

【数据管理】数据治理

目录 1、相关概念 2、数据治理和管理职责语境关系图 3、业务驱动因素 4、目标和原则 5、 数据治理和数据管理的关系 6、数据治理组织 7、数据管理职能 8、数据制度 9、数据资产估值 1、相关概念 1&#xff09;战略(Stategy)&#xff1a;定义、交流和驱动数据战略和数…

[数据集][目标检测]电力场景输电线异物检测数据集VOC+YOLO格式2060张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2060 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注…

电脑丢失dll文件一键修复之dll确实损坏影响电脑运行

在使用电脑过程中&#xff0c;DLL文件丢失或损坏是一个常见的问题&#xff0c;它可能导致程序无法正常运行&#xff0c;甚至影响整个系统的稳定性。本文将详细介绍如何一键修复丢失的DLL文件&#xff0c;探讨常见的DLL丢失报错原因&#xff0c;并提供详细的修复步骤和预防措施。…

sklearn回归树

说明&#xff1a;内容来自菜菜的sklearn机器学习和ai生成 回归树 调用对象的参数 class sklearn.tree.DecisionTreeRegressor (criterion’mse’, splitter’best’, max_depthNone, min_samples_split2, min_samples_leaf1, min_weight_fraction_leaf0.0, max_featuresNone…

大数据基础:数仓架构演变

文章目录 数仓架构演变 一、传统离线大数据架构 二、​​​​​​Lambda架构 三、Kappa架构 四、​​​​​​​​​​​​​​混合架构 五、湖仓一体架构 六、流批一体架构 数仓架构演变 20世纪70年代&#xff0c;MIT(麻省理工)的研究员致力于研究一种优化的技术架构&…

Linux shell编程学习笔记75:sed命令——沧海横流任我行(下)

0 前言 在 Linux shell编程学习笔记73&#xff1a;sed命令——沧海横流任我行&#xff08;上&#xff09;-CSDN博客文章浏览阅读684次&#xff0c;点赞32次&#xff0c;收藏24次。在大数据时代&#xff0c;我们要面对大量数据&#xff0c;有时需要对数据进行替换、删除、新增、…

OpenCV几何图像变换(9)仿射变换函数warpAffine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 函数是应用一个仿射变换到图像上。 warpAffine 函数使用指定的矩阵对源图像进行仿射变换&#xff1a; dst ( x , y ) src ( M 11 x M 12 y M…

Elasticsearch:使用 ELSER 进行语义搜索 - sparse_vector

Elastic Learned Sparse EncodeR&#xff08;或 ELSER&#xff09;是由 Elastic 训练的 NLP 模型&#xff0c;可让你使用稀疏向量表示执行语义搜索。语义搜索不是根据搜索词进行文字匹配&#xff0c;而是根据搜索查询的意图和上下文含义检索结果。 本教程中的说明向你展示了如…

[医疗 AI ] 3D TransUNet:通过 Vision Transformer 推进医学图像分割

[医疗 AI ] 3D TransUNet&#xff1a;通过 Vision Transformer 推进医学图像分割’ 论文地址 - https://arxiv.org/pdf/2310.07781 0. 摘要 医学图像分割在推进医疗保健系统的疾病诊断和治疗计划中起着至关重要的作用。U 形架构&#xff0c;俗称 U-Net&#xff0c;已被证明在…