第三章:高精度算法(加、减、乘、除)

news2024/11/23 22:08:04

高精度算法

  • 高精度的整体思路:
  • 一、加法
    • 1、思路:
    • 2、模板:
      • (1)C++版:
      • (2)C语言版:
  • 二、减法
    • 1、思路:
    • 2、模板:
      • C++
      • C
  • 三、乘法
    • 1、思路:
    • 2、模板:
      • C++
  • 四、除法
    • 1、思路:
    • 2、模板:
      • C++
  • 五、总结:

高精度的整体思路:

为什么一个数据会存在最大数?我们知道,一个数字会转化成二进制存储在内存中。而每个二进制位都会消耗一个比特位。我们以int 为例,其大小是4个字节,32个比特位。因此,其所表示的最大数即32个比特位全是1的时候。

因此,我们只要打破内存的限制,就能实现超级大的数字之间的加减乘除运算。那么如何打破呢?此时我们就需要用到我们学过的数组,我们的数组可以在相应的内存区域限制内不断地开辟,从而满足我们的需要。

因此高精度的本质就是利用数组模拟各种运算法则,从而得到结果。

一、加法

1、思路:

在这里插入图片描述
我们以上述图示为例子,我们创建两个数组,模拟两个数字的位数,从个位开始运算。

我们创建一个中间变量t来存储每一位两个数字加起来的结果。但是我们需要注意的是,结果中的每一位不仅来自A,B两个数字中对应位数的相加,还包括前一位的进位。

个位的加减是没有前一位的进位的,因此我们初始化 t 为0。然后运算结果如上图所示。我们将t%10后,就是该位所应保留的数字,然后将t在/=10,此时t就保留了进到下一位中的进位。

因此,我们这里要写成:t += A[i] + B[i]。一定是+=!!。否则就会丢掉进位。不懂得话,可以详细看上面图片中的手写例子。

但是我们还需要注意的一点就是,当我们算到最后一位的时候,最后一位计算结束的时候,我们的t有可能依然有进位。由于A,B已经没有下一位了。所以如果不特殊处理以下的话,这一位就丢掉了。因此我们用if语句判断一下,如果存有进位,则再开一位存储1。

另外的一些细节,我们看完模板再解释。

2、模板:

(1)C++版:

#include<iostream>
#include<vector>
#include<string>
using namespace std;
vector<int>add(vector<int>&A,vector<int>&B)
{
	int t=0;
	vector<int>C;
	for(int i=0;i<A.size()||i<B.size();i++)
	{
		if(i<A.size())t+=A[i];
		if(i<B.size())t+=B[i];
		C.push_back(t%10);
		t/=10;
	}
	if(t!=0)C.push_back(1);
	return C;
}
int main()
{
	string a,b;
	vector<int>A,B;
	cin>>a>>b;
	//倒序存储数组
	for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
	for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
	//调用加法函数
	vector<int>C=add(A,B);
	//倒序输出结果
	for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
	return 0;
}

(2)C语言版:

#include<stdio.h>
#include<string.h>
char a[100005];
char b[100005];
int A[100005];
int B[100005];
int C[100005];

int main()
{
    scanf("%s",a);
    scanf("%s",b);
    int lena = strlen(a);
    int lenb = strlen(b);
	//逆序数组
    for (int i = 0; i < lena;i++)
    {
        A[lena - i - 1] = a[i] - '0';
    }

    for (int i = 0; i < lenb;i++)
    {
         B[lenb - i - 1] = b[i] - '0';
    }
  
	//判断结果的最大位数
    int lenc = (lena > lenb ? lena : lenb)+1;
	//开始加法
	int t=0;
	int i;
    for (i = 0; i < lenc;i++)
    {
    	t+=A[i]+B[i];
    	C[i]=t%10;
    	t/=10;
    }
    if(t!=0)C[i]=1;
    
    //删除前导零,避免出现:000012的情况。但是要注意0这种特殊情况
    while(C[lenc]==0&&lenc>0)
    {
         lenc--;
    }
	
    for (int i = lenc; i >= 0;i--)
    {
         printf("%d",C[i]);
    }

         return 0;
}

我们这里解释一下为什么要倒置数组,因为我们输入一个字符串后,第一个元素是最高位。但是我们上述图片举得例子中,第一个元素是个位,所以我们需要倒置数组,不要忘记剪掉:'0'
在这里插入图片描述

二、减法

1、思路:

请添加图片描述
减法依旧是去模拟一个竖式的计算,上面的图片中就是我们小学数学中所熟悉的式子。那么我们从编程的角度去审视一下上面的代码:
请添加图片描述
通过上面的代码,我们可以总结出减法中的关键思路:
创建一个临时变量t,这个t就是辅助我们去计算每一位的。那么这个t代表的是进位。同时我们也利用t来存储每一位运算的结果,然后再通过对t的取模运算得到每一位的结果,然后再通过A[i]-B[i]-t的正负去判断进位。
这里我们解释一下,当A[i]-B[i]小于0的时候,说明我们是需要借位的,借位其实就是给这个负数的结果加上一个10,因为我们借位了,所以t要等于1。所以在下一位的计算过程中,我们就要多减去一个1,那么多减去的这个1就体现在 - t 中。

当我们的结果是负数的时候,我们还需要去模拟那个负数,因此我们需要先判断一下正负,然后预先得知是否打印负号,然后我们再去求二者相减的绝对值即可。

接着下来的话,我们需要还需要解决一下前导零的问题:比如777-772=005。5之前的00是没必要打印的,我们直接删除即可。

2、模板:

C++

#include<iostream>
#include<vector>
using namespace std;
bool cmp(vector<int>&A,vector<int>&B)
{
    if(A.size()!=B.size())return A.size()>B.size();
    else
    {
        for(int i=A.size()-1;i>=0;i--)
        {
            if(A[i]!=B[i])return A[i]>B[i];
        }
    }
    return true;
}
vector<int>sub(vector<int>&A,vector<int>&B)
{
    vector<int>C;
    int t=0;
    for(int i=0;i<A.size();i++)
    {
        t=A[i]-t;
        if(i<B.size())t-=B[i];
        if(t>=0)
        {
            C.push_back(t);
            t=0;
        }
        else
        {
            C.push_back(t+10);
            t=1;
        }
    }
    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}
int main()
{
    vector<int>A,B,C;
    string a,b;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    for(int i=b.size()-1;i>=0;i--)B.push_back(b[i]-'0');
    if(cmp(A,B))
    {
        C=sub(A,B);
    }
    else
    {
        C=sub(B,A);
        cout<<"-";
    }
    for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    return 0;
}

C

#include<stdio.h>
#include<string.h>

char a[100010],b[100010];
int A[100010],B[100010],C[100010];

int cmp(int*A,int*B,int lena,int lenb)
{
    int len=lena-lenb;
    if(len!=0)
    {
        return len;
    }
    else
    {
        for(int i=lena-1;i>=0;i--)
        {
            if(A[i]!=B[i])
            {
                return A[i]-B[i];
            }
        }
    }
    return 0;
}

int sub(int*A,int*B,int*C,int lena,int lenb)
{
    int t=0,lenc=0;
    for(int i=0;i<lena;i++)
    {
        t=A[i]-t;
        
        if(i<lenb)t-=B[i];
        
        if(t>=0)
        {
            C[i]=t;
            t=0;
        }
        else
        {
            C[i]=t+10;
            t=1;
        }
        lenc++;
    }
    while(lenc>1&&C[lenc-1]==0)lenc--;
    return lenc;
}

int main()
{
    scanf("%s %s",a,b);
    int lena=strlen(a);
    int lenb=strlen(b);
    int lenC=0;
    for(int i=0;i<lena;i++)A[i] = a[lena - i - 1] - '0';
    for(int i=0;i<lenb;i++)B[i]=b[lenb-i-1]-'0';
    
    if(cmp(A,B,lena,lenb)>=0)
    {
        lenC=sub(A,B,C,lena,lenb);
    }
    else
    {
        lenC=sub(B,A,C,lenb,lena);
        printf("-");
    }
    for(int i=lenC-1;i>=0;i--)printf("%d",C[i]);
	return 0;
}

三、乘法

1、思路:

我们这里的乘法介绍的是一个较小的数乘以一个大整数,即只把大整数存储到数组中,而这个较小的数字依旧存储在一个整型当中。思路如下所示:
在这里插入图片描述

2、模板:

C++

#include<iostream>
#include<vector>
using namespace std;
vector<int> mul(vector<int>&A,int&b)
{
    vector<int>C;
    int t=0;
    for(int i=0;i<A.size()||t>0;i++)
    {
        if(i<A.size())t+=A[i]*b;
        C.push_back(t%10);
        t=t/10;
    }
    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}
int main()
{
    string a;
    int b;
    cin>>a>>b;
    vector<int>A;
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    vector<int>B=mul(A,b);
    for(int i=B.size()-1;i>=0;i--)printf("%d",B[i]);
    return 0;
}

四、除法

我们这里说的除法是一个大整数除以一个存储在int类型中的数字。

1、思路:

请添加图片描述

如上图所示,我们创建一个临时的变量t。这个t有两个作用:第一个作用是计算结果的每一位,第二个作用是记录最终的余数。

2、模板:

C++

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;

vector<int>div(vector<int>&A,int&B,int&t)
{
    t=0;
    vector<int>C;
    for(int i=A.size()-1;i>=0;i--)
    {
        t=t*10+A[i];
        C.push_back(t/B);
        t=t%B;
    }
    reverse(C.begin(),C.end());
    while(C.size()>1&&C.back()==0)C.pop_back();
    return C;
}

int main()
{
    string a;
    vector<int> A;
    int B;
    cin>>a>>B;
    for(int i=a.size()-1;i>=0;i--)A.push_back(a[i]-'0');
    vector<int> C;
    int c;
    C=div(A,B,c);
    for(int i=C.size()-1;i>=0;i--)printf("%d",C[i]);
    puts("");
    cout<<c;
    return 0;
}

五、总结:

我们发现上述的模板中都有一个相似的思路:我们都创建了一个临时变量t,这个变量的作用就是来计算每一位,同时去存储进位或者余数。当大家忘记时,自己模拟一下,即可写出上述的模板。希望大家多多研究图片中的例子。

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

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

相关文章

CMake中return的使用

CMake中的return命令用于从文件、目录或函数返回&#xff0c;其格式如下&#xff1a; return([PROPAGATE <var-name>...]) 当在包含文件中(in an included file)遇到此命令时(通过include或find_package命令)&#xff0c;它会导致当前文件的处理停止并将控制权返回给包含…

CTFHub | Cookie注入

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习&#xff0c;实训平台。提供优质的赛事及学习服务&#xff0c;拥有完善的题目环境及配套 writeup &#xff0c;降低 CTF 学习入门门槛&#xff0c;快速帮助选手成长&#xff0c;跟随主流比赛潮流。 0x01 题目描述…

Linux C/C++ 学习笔记(五):Mysql C/C++编程 创建 插入 读取 删除 存储过程

本文参考Linux C/C 开发&#xff08;学习笔记七&#xff09;&#xff1a;Mysql数据库C/C编程实现 插入/读取/删除_菊头蝙蝠的博客-CSDN博客 一、数据库建模与建库建表 在设计数据库时&#xff0c;对现实世界进行分析、抽象、并从中找出内在联系&#xff0c;进而确定数据库的结…

计算机视觉之迁移学习中的微调(fine tuning)

现在的数据集越来越大&#xff0c;都是大模型的训练&#xff0c;参数都早已超过亿级&#xff0c;面对如此大的训练集&#xff0c;绝大部分用户的硬件配置达不到&#xff0c;那有没有一种方法让这些训练好的大型数据集的参数&#xff0c;迁移到自己的一个目标训练数据集当中呢&a…

javaweb Ajax AXios异步框架 JSON 案例

AJAX概念&#xff1a;AJAX(Asynchronous JavaScript And XML)&#xff1a;异步的 JavaScript 和 XML AJAX作用&#xff1a; 与服务器进行数据交换&#xff1a;通过AJAX可以给服务器发送请求&#xff0c;并获取服务器响应的数据 使用了AJAX和服务器进行通信&#xff0c;就可以…

ArcGIS_将多个点数据整合成一个点数据

问题描述:如何将多个点合并成一个点,并保留原始点数据的字段信息 方法一:整合 打开arcgis整合工具 :“数据管理工具——要素类——整合” 容差半径可以通过arcgis测量工具获取,根据自己的目标任务,选择合适的容差半径 该方法优点在于整合后的点可以正好位于原始点数据…

牛客网之SQL非技术快速入门(7)-字符串截取、切割、删除、替换

知识点&#xff1a; &#xff08;1&#xff09;substring_indexsubstring_index(str,delim,count) str:要处理的字符串 delim:分隔符 count:计数 &#xff08;2&#xff09;切割、截取、删除、替换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 select -- 替换法 replace(string, 被…

俺把所有粉丝显示在地图上啦~【详细教程+完整源码】

文章目录&#x1f332;小逼叨&#x1f332;爬取所有粉丝的IP所属地&#x1f334;爬者基本素养&#xff1a;网页分析&#x1f334;源代码&#x1f332;数据清洗和保存&#x1f334;源代码&#x1f332;绘制地图&#x1f334;源代码&#x1f332;结束语&#x1f332;小逼叨 其实昨…

windows中使用curl

curl这个工具在linux和macOS都经常使用&#xff0c;感觉挺实用的。在windows中默认也带了一个但是用起来不太一样&#xff0c;于是就想自己手动安装一个原汁原味的curl。 下载安装 https://curl.se/windows/ 下载适合自己平台的版本&#xff0c;解压就可以直接运行了。 比如…

剑指 Offer II 026. 重排链表【链表】

难度等级&#xff1a;中等 上一篇算法&#xff1a; 剑指 Offer II 021. 删除链表的倒数第 n 个结点【链表】 力扣此题地址&#xff1a; 剑指 Offer II 026. 重排链表 - 力扣&#xff08;LeetCode&#xff09; 1.题目&#xff1a;重排链表 给定一个单链表 L 的头节点 head &…

Linux用户和权限学习笔记

认识root用户 什么是root用户 无论是Windows、MacOS、Linux均采用多用户的管理模式进行权限管理。 在Linux系统中&#xff0c;拥有最大权限的账户名为&#xff1a;root&#xff08;超级管理员&#xff09;而在前期&#xff0c;我们一直使用的账户是普通账户&#xff1a;itheim…

《Android Studio开发实战 从零基础到App上线(第3版)》出版后记

2018年11月&#xff0c;经过熬夜写作的《Android Studio开发实战 从零基础到App上线(第2版)》正式出版面世。承蒙众多读者的厚爱&#xff0c;第2版的图书在此后的三年多时间&#xff0c;一直保持在移动开发图书的销量排行榜前列&#xff0c;迄今为止京东对该书的评价已达8000多…

设计模式基础-概括

目录 一、设计原则 二、设计模式分类 1、创建型模式&#xff1a;创建对象 2、结构型模式&#xff1a;更大的结构 3、行为型模式&#xff1a;交互以及职责分配 4、对象模式与类模式区别 三、各类型模式简介 1、创建型模式 2、结构型模式 3、行为型模式 一、设计原则 …

JAVA中Function的使用

JAVA中Function的使用一、方法介绍参数类型方法介绍源码二、demo参考&#xff1a; https://blog.csdn.net/boyan_HFUT/article/details/99618833 一、方法介绍 表示接受一个参数并产生结果的函数。 参数类型 T - 函数输入的类型R - 函数的结果类型 方法介绍 R apply(T t) …

【毕业设计】45-基于单片机的智能温度/超温报警计的系统设计(原理图工程+仿真工程+源代码+答辩论文+答辩PPT)

【毕业设计】45-基于单片机的智能温度/超温报警计的系统设计&#xff08;原理图工程仿真工程源代码答辩论文答辩PPT&#xff09; 文章目录【毕业设计】45-基于单片机的智能温度/超温报警计的系统设计&#xff08;原理图工程仿真工程源代码答辩论文答辩PPT&#xff09;资料下载链…

Vue 路由

参考文献&#xff1a;Vue中的路由 目录:一、路由理解&#xff1a;二、路由管理器理解&#xff1a;三、路由的使用&#xff1a;四、嵌套路由&#xff1a;五、路由传参&#xff1a;1.query传参&#xff1a;2. params传参&#xff1a;六、编程式路由导航&#xff1a;七、响应路由参…

数字孪生技术有没有真正的实用价值?

作为一个数字孪生领域的技术公司负责人&#xff0c;我尽可能用比较直白的话来描述一下我对数字孪生行业以及数字孪生价值的理解。 纵观数字孪生相关的公司&#xff0c;主要有两个流派&#xff0c;一派是具有互联网基因的数字孪生创业公司&#xff0c;一派是在工业软件领域实力…

ConfigurableListableBeanFactory和BeanDefinitionRegistry关系

前言 &#xff1a;在查看springBoot源码的过程中&#xff0c;遇到了这个问题&#xff0c;上网查了一些资料&#xff0c;理解了一些&#xff0c;这里顺便把这个问题给记录一下。 在springBoot调用Refresh方法里面 &#xff0c;有一个叫invokeBeanFactoryPostProcessors的方法【…

HIve数仓新零售项目ODS层的构建

HIve数仓新零售项目 注&#xff1a;大家觉得博客好的话&#xff0c;别忘了点赞收藏呀&#xff0c;本人每周都会更新关于人工智能和大数据相关的内容&#xff0c;内容多为原创&#xff0c;Python Java Scala SQL 代码&#xff0c;CV NLP 推荐系统等&#xff0c;Spark Flink Kaf…

WindowsPE(二)空白区添加代码新增,扩大,合并节

空白区添加代码 在 PE 中插入一段调用 MessageBox 的代码。 获取MessageBox地址&#xff0c;构造ShellCode代码 利 OD 定位出 MessageBoxA 函数的地址为 0x77D507EA 。 构造 shellcode &#xff1a; unsigned char shellcode[] {0x6A, 0x00, // pus…