当前位置: 首页 > article >正文

大数运算之C语言实现

一、 前言

在我们代码编程过程中,我们经常需要处理各种规模的数值。从日常工作中的一些简单算术在到科学研究中的复杂计算,数字无处不在。然而,当数值变的异常庞大时,就需要用到大数运算来进行实现。本文我们将介绍大数运算的基本概念以及C语言实现大数运算的方法。

二、 概念

我们常见的数据其数据范围大都在int(-2^31 ~ 2^31-1)或者long long( -2^62 — 2^62-1)之内,但在一些情况下,我们会遇到超过这个范围的数据,这些数据我们就称之为大数。当然,大数不只是大整数,对于位数较多的小数,如123456799.987654321也适用于大数运算。

大数运算涉及对超出常规数据类型范围的数字进行加减乘除,阶乘,取余等运算。对于大数,我们通常以字符数组的形式存储,因为单个变量无法容纳如此多的位数,其中每个数组元素都对应一个数位。而实现这些运算的大致思路都是模拟竖式计算的过程,即通过模拟进位,借位,取余操作完成计算。

三、 代码实现

下面我们将对大数进行加减乘除,阶乘,取余等运算。

  • 大数加法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>// 函数声明
    char* reverseString(const char* str);
    char* addLargeNumbers(const char* num1, const char* num2);// 主函数
    int main() {// 输入两个大数char num1[1001]; // 假设大数的最大位数为1000,多加一位用于存储字符串结束符'\0'char num2[1001];printf("请输入第一个大数: ");scanf("%1000s", num1); // 限制输入长度,防止缓冲区溢出printf("请输入第二个大数: ");scanf("%1000s", num2);// 调用大数相加函数char* result = addLargeNumbers(num1, num2);// 输出结果printf("结果: %s\n", result);// 释放动态分配的内存free(result);return 0;
    }// 字符串反转函数
    char* reverseString(const char* str) {int len = strlen(str);char* reversed = (char*)malloc((len + 1) * sizeof(char)); // 分配内存用于存储反转后的字符串for (int i = 0; i < len; i++) {reversed[i] = str[len - 1 - i]; // 反转字符串}reversed[len] = '\0'; // 添加字符串结束符return reversed;
    }// 大数相加函数
    char* addLargeNumbers(const char* num1, const char* num2) {char* reversedNum1 = reverseString(num1); // 反转字符串,便于从低位开始相加char* reversedNum2 = reverseString(num2);int len1 = strlen(reversedNum1);int len2 = strlen(reversedNum2);int maxLen = len1 > len2 ? len1 : len2;int carry = 0; // 进位char* result = (char*)malloc((maxLen + 2) * sizeof(char)); // 分配内存用于存储结果,多加一位用于可能的进位和一位用于字符串结束符// 从低位开始逐位相加for (int i = 0; i < maxLen; i++) {int digit1 = i < len1 ? reversedNum1[i] - '0' : 0;int digit2 = i < len2 ? reversedNum2[i] - '0' : 0;int sum = digit1 + digit2 + carry;carry = sum / 10;result[i] = sum % 10 + '0';}// 处理剩余的进位if (carry) {result[maxLen] = carry + '0';maxLen++;}// 添加字符串结束符result[maxLen] = '\0';// 反转结果字符串,得到正确的顺序char* finalResult = reverseString(result);free(reversedNum1); // 释放反转后的输入字符串内存free(reversedNum2);free(result); // 释放中间结果字符串内存return finalResult; // 返回最终结果字符串
    }
    

    演示:在这里插入图片描述

  • 大数减法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <stdbool.h>// 函数声明
    char* reverseString(const char* str);
    char* subtractLargeNumbers(const char* num1, const char* num2);// 主函数
    int main() {// 输入两个大数char num1[1001]; // 假设大数的最大位数为1000,多加一位用于存储字符串结束符'\0'char num2[1001];printf("请输入被减数: ");scanf("%1000s", num1); // 限制输入长度,防止缓冲区溢出printf("请输入减数: ");scanf("%1000s", num2);// 验证被减数是否大于等于减数(简单比较字符串长度,如果不等则按字典序比较)if (strlen(num1) < strlen(num2) || (strlen(num1) == strlen(num2) && strcmp(num1, num2) < 0)) {printf("错误: 被减数必须大于等于减数。\n");return 1;}// 调用大数相减函数char* result = subtractLargeNumbers(num1, num2);// 输出结果printf("结果: %s\n", result);// 释放动态分配的内存free(result);return 0;
    }// 字符串反转函数(与大数加法中相同)
    char* reverseString(const char* str) {int len = strlen(str);char* reversed = (char*)malloc((len + 1) * sizeof(char));for (int i = 0; i < len; i++) {reversed[i] = str[len - 1 - i];}reversed[len] = '\0';return reversed;
    }// 大数相减函数
    char* subtractLargeNumbers(const char* num1, const char* num2) {char* reversedNum1 = reverseString(num1);char* reversedNum2 = reverseString(num2);int len1 = strlen(reversedNum1);int len2 = strlen(reversedNum2);int maxLen = len1;bool isNegative = false; // 标记结果是否为负数(本实现假设被减数大于等于减数,所以结果不为负数)char* result = (char*)malloc((maxLen + 1) * sizeof(char)); // 分配内存,注意这里不需要额外位用于借位,因为我们已经假设被减数足够大// 从低位开始逐位相减for (int i = 0; i < maxLen; i++) {int digit1 = i < len1 ? reversedNum1[i] - '0' : 0;int digit2 = i < len2 ? reversedNum2[i] - '0' : 0;int diff = digit1 - digit2;// 如果当前位不够减,则从前一位借位if (diff < 0) {diff += 10;// 注意:这里我们不需要修改前一位的值,因为我们是逐位计算并存储结果的// 但是在实际实现中,如果需要保留借位的过程,可以引入一个额外的数组来记录借位情况}result[i] = diff + '0';}// 去除结果前导零int start = 0;while (start < maxLen && result[start] == '0') {start++;}// 如果整个结果都是0,则结果应为"0"if (start == maxLen) {strcpy(result, "0");} else {// 将结果字符串前移,以去除前导零for (int i = 0; i < maxLen - start; i++) {result[i] = result[start + i];}result[maxLen - start] = '\0';}// 反转结果字符串,得到正确的顺序char* finalResult = reverseString(result);free(reversedNum1);free(reversedNum2);free(result); // 注意:这里释放的是去除前导零之前的result内存return finalResult;
    }
    

    演示:
    在这里插入图片描述

  • 大数乘法运算

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>char* multiplyLargeNumbers(const char* num1, const char* num2);int main() {char num1[1001], num2[1001];printf("请输入第一个大数: ");scanf("%1000s", num1);printf("请输入第二个大数: ");scanf("%1000s", num2);char* result = multiplyLargeNumbers(num1, num2);printf("结果: %s\n", result);free(result - (result[0] == '0' && *(result + 1) == '\0')); // Adjust free pointer if leading '0' was logically removedreturn 0;
    }char* multiplyLargeNumbers(const char* num1, const char* num2) {int len1 = strlen(num1);int len2 = strlen(num2);int maxLen = len1 + len2;char* result = (char*)calloc(maxLen + 1, sizeof(char)); // +1 for null terminator// Initialize result array with zerosmemset(result, '0', maxLen);result[maxLen] = '\0';// Perform multiplicationfor (int i = len1 - 1; i >= 0; i--) {for (int j = len2 - 1, k = i + j; j >= 0; j--, k--) {int mul = (num1[i] - '0') * (num2[j] - '0');int sum = mul + (result[k + 1] - '0');result[k + 1] = (sum % 10) + '0';result[k] += sum / 10;if (result[k] > '9') {int carry = result[k] - '0' / 10;result[k] = (result[k] - carry * 10) + '0';if (k > 0) {result[k - 1] += carry;} else {// Handle carry overflow, resize result if necessarychar* temp = (char*)realloc(result, maxLen + 2);if (temp) {result = temp;memmove(result + 1, result, maxLen + 1);result[0] = '0' + carry;maxLen++;} else {// Allocation failed, handle errorfree(result);return NULL;}}}}}// Find the start of the non-zero part of the resultint start = 0;while (start < maxLen && result[start] == '0') {start++;}// Shift the result to the left and null-terminate itmemmove(result, result + start, maxLen - start + 1);return result;
    }

    演示:
    在这里插入图片描述

  • 大数除法及取余运算

    #include <stdio.h>
    #include <string.h>void divideLargeNumber(int *largeNumberArray, int divisionFactor, int numberOfDigits);int main()
    {char largeNumberStr[1000]; // 存储大整数的字符串int divisor; // 除数printf("请输入一个大整数(不超过999位)和一个除数(以空格分隔):");scanf("%s%d", largeNumberStr, &divisor);int largeNumberDigits[1000]; // 存储大整数的每一位数字int numberLength = strlen(largeNumberStr); // 大整数的长度for (int i = 0; i < numberLength; i++)largeNumberDigits[i] = largeNumberStr[i] - '0';divideLargeNumber(largeNumberDigits, divisor, numberLength);return 0;
    }void divideLargeNumber(int *largeNumberArray, int divisionFactor, int numberOfDigits) // 开始操作
    {int partialSum = 0; // 当前部分和(累加当前位及之前的数字)int firstDigitOutputFlag = 0; // 标记是否已输出第一个非零商位int remainder = 0; // 余数for (int i = 0; i < numberOfDigits; i++) // 这里没有将商存下来,而是找到一位就输出{partialSum += largeNumberArray[i];if (partialSum < divisionFactor && firstDigitOutputFlag){remainder = partialSum; // 记录有可能最后加上最后一位还是小于divisionFactor的情况的余数printf("0"); // 输出商位为0的情况}if (partialSum >= divisionFactor){printf("商位: %d", partialSum / divisionFactor);partialSum %= divisionFactor; // 更新余数remainder = partialSum; // 记录最后加上最后一位大于divisionFactor的情况的余数partialSum *= 10; // 为下一位数字做准备firstDigitOutputFlag = 1; // 在找到第一个商位之后改变标记状态}else{partialSum *= 10; // 如果当前部分和小于除数,则继续累加下一位数字}}printf("\n");printf("余数:%d\n", remainder); // 输出余数
    }
    

    演示:
    在这里插入图片描述

  • 大数阶乘运算

    #include <stdio.h>
    #include <string.h>
    #include <stdbool.h>#define MAX_DIGITS 1000 // 假设最大阶乘位数不超过1000// 辅助函数:打印大数
    void printLargeNumber(int result[], int length) {for (int i = length - 1; i >= 0; i--) {printf("%d", result[i]);}printf("\n");
    }// 大数阶乘函数
    void factorialLargeNumber(int n, int result[], int* length) {// 初始化结果数组为1(阶乘的初始值)memset(result, 0, sizeof(int) * MAX_DIGITS);result[0] = 1;*length = 1;// 计算阶乘for (int i = 2; i <= n; i++) {int carry = 0; // 进位for (int j = 0; j < *length; j++) {int product = result[j] * i + carry;result[j] = product % 10; // 当前位的值carry = product / 10;     // 进位}// 处理进位while (carry) {result[*length] = carry % 10;carry = carry / 10;(*length)++;}}
    }int main() {int n;printf("请输入一个整数: ");scanf("%d", &n);int result[MAX_DIGITS];int length = 0;factorialLargeNumber(n, result, &length);printf("%d! = ", n);printLargeNumber(result, length);return 0;
    }
    

    演示:
    在这里插入图片描述

四、小结

好了,关于大数运算的代码实现到我们这里就结束了,后面遇到其它我会在继续接着补充,希望对大家有所帮助。

相关文章:

大数运算之C语言实现

一、 前言 在我们代码编程过程中&#xff0c;我们经常需要处理各种规模的数值。从日常工作中的一些简单算术在到科学研究中的复杂计算&#xff0c;数字无处不在。然而&#xff0c;当数值变的异常庞大时&#xff0c;就需要用到大数运算来进行实现。本文我们将介绍大数运算的基本…...

安装最小化的CentOS7后,执行yum命令报错Could not resolve host mirrorlist.centos.org; 未知的错误

文章目录 安装最小化的CentOS7后&#xff0c;执行yum命令报错"Could not resolve host: mirrorlist.centos.org; 未知的错误"错误解决方案&#xff1a; 安装最小化的CentOS7后&#xff0c;执行yum命令报错"Could not resolve host: mirrorlist.centos.org; 未知…...

Avalonia+ReactiveUI跨平台路由:打造丝滑UI交互的奇幻冒险

一、引言 在当今数字化时代&#xff0c;跨平台应用开发已成为大势所趋。开发者们迫切需要一种高效、灵活的方式&#xff0c;能够让应用程序在不同操作系统上无缝运行&#xff0c;为用户提供一致的体验。Avalonia 和 ReactiveUI 的组合&#xff0c;宛如一对天作之合的舞者&…...

Java导出通过Word模板导出docx文件并通过QQ邮箱发送

一、创建Word模板 {{company}}{{Date}}服务器运行情况报告一、服务器&#xff1a;总告警次数&#xff1a;{{ServerTotal}} 服务器IP:{{IPA}}&#xff0c;总共告警次数:{{ServerATotal}} 服务器IP:{{IPB}}&#xff0c;总共告警次数:{{ServerBTotal}} 服务器IP:{{IPC}}&#x…...

Linux系统编程:进程状态和进程优先级/nice

目录 一,相对于OS的进程状态 1.1运行状态 1.2阻塞状态 1.3挂起状态 二,并发执行与进程切换 2.1,CPU并发执行 2.2进程切换 三,Linux内核管理进程状态的方法 3.1查看进程状态 3.2R状态 3.3S状态 3.4D状态 3.5T状态 3.6X状态 3.7Z状态 3.8孤儿进程 四,进程优先级 …...

JavaScript系列(40)--虚拟DOM实现详解

JavaScript虚拟DOM实现详解 &#x1f333; 今天&#xff0c;让我们深入了解虚拟DOM的实现原理&#xff0c;这是现代前端框架中非常重要的一个概念&#xff0c;它通过最小化实际DOM操作来提升应用性能。 虚拟DOM基础概念 &#x1f31f; &#x1f4a1; 小知识&#xff1a;虚拟D…...

SpringAI基于API对大语言模型调用

引言 随着人工智能技术的迅猛发展&#xff0c;大型语言模型&#xff08;LLM&#xff09;在各个领域的应用越来越广泛。SpringAI作为一个旨在简化AI集成的框架&#xff0c;为开发者提供了高效、便捷的工具来连接和调用这些大模型。本文将详细探讨如何使用SpringAI整合通义千问等…...

0 基础学运维:解锁 K8s 云计算运维工程师成长密码

前言&#xff1a;作为一个过来人&#xff0c;我曾站在技术的门槛之外&#xff0c;连电脑运行内存和内存空间都傻傻分不清&#xff0c;完完全全的零基础。但如今&#xff0c;我已成长为一名资深的k8s云计算运维工程师。回顾这段历程&#xff0c;我深知踏上这条技术之路的艰辛与不…...

在 vscode + cmake + GNU 工具链的基础上配置 JLINK

安装 JLINK JLINK 官网链接 下载安装后找到安装路径下的可执行文件 将此路径添加到环境变量的 Path 中。 创建 JFlash 项目 打开 JFlash&#xff0c;选择新建项目 选择单片机型号 在弹出的窗口中搜索单片机 其他参数根据实际情况填写 新建完成&#xff1a; 接下来设置…...

【全栈】SprintBoot+vue3迷你商城(9)

【全栈】SprintBootvue3迷你商城&#xff08;9&#xff09; 往期的文章都在这里啦&#xff0c;大家有兴趣可以看一下 后端部分&#xff1a; 【全栈】SprintBootvue3迷你商城&#xff08;1&#xff09; 【全栈】SprintBootvue3迷你商城&#xff08;2&#xff09; 【全栈】Spr…...

自动化实现的思路变化

阶段一&#xff1a; 1、成功调用。第一步&#xff0c;一般是用现用的工具&#xff0c;或者脚本成功调用接口 2、解决关联接口的参数传递。有的接口直接&#xff0c;存在参数的传递&#xff0c;一般的思路&#xff0c;就是将这个参数设置为变量。 3、简化代码。总会有些东西是重…...

省市区三级联动

引言 在网页中&#xff0c;经常会遇到需要用户选择地区的场景&#xff0c;如注册表单、地址填写等。为了提供更好的用户体验&#xff0c;我们可以实现一个三级联动的地区选择器&#xff0c;让用户依次选择省份、城市和地区。 效果展示&#xff1a; 只有先选择省份后才可以选择…...

Mac安装Redis并设置launchd自启动

下载和编译redis源码 方便mac同学&#xff0c;不想使用brew方式安装&#xff0c;又想开机自启动redis&#xff0c;简单记录一下。首先下载redis7.0.15.tar.gz源码包 tar -xf tar -zxf redis-7.0.15.tar.gz开始编译源码 cd redis-7.0.15 sudo cp redis.conf /etc/redis.conf …...

Fullcalendar @fullcalendar/react 样式错乱丢失问题和导致页面卡顿崩溃问题

问题描述&#xff1a; 我使用 fullcalendar的react版本时&#xff0c;出现了一个诡异的问题&#xff0c;当我切换到 一个iframe页面时&#xff08;整个页面是一个iframe嵌入的&#xff09;&#xff0c;再切换回来日历的样式丢失了&#xff01;不仅丢失了样式还导致页面崩溃了&…...

dm8在Linux环境安装精简步骤说明(2024年12月更新版dm8)

dm8在Linux环境安装详细步骤 - - 2025年1月之后dm8 环境介绍1 修改操作系统资源限制2 操作系统创建用户3 操作系统配置4 数据库安装5 初始化数据库6 实例参数优化7 登录数据库配置归档与备份8 配置审计9 创建用户10 屏蔽关键字与数据库兼容模式11 jdbc连接串配置12 更多达梦数据…...

Linux MySQL离线安装

一、准备工作 1. 下载MySQL安装包 访问MySQL官方网站&#xff0c;选择适合您Linux系统的MySQL版本进行下载。通常推荐下载Generic Linux (glibc 2.12)版本的.tar.gz压缩包&#xff0c;例如mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz。将下载好的安装包拷贝到Linux服务器的某…...

S4 HANA更改Tax base Amount的字段控制

本文主要介绍在S4 HANA OP中Tax base Amount的字段控制相关设置。具体请参照如下内容&#xff1a; 1. 更改Tax base Amount的字段控制 以上配置用于控制FB60/FB65/FB70/FB75/MIRO的页签“Tax”界面是否可以修改“Tax base Amount”&#xff0c; 如果勾选Change 表示可以修改T…...

JVM堆空间

一、堆空间的核心概述 一个JVM实例只存在一个堆内存&#xff0c;堆也是Java内存管理的核心区域。Java堆区在JVM启动的时候即被创建&#xff0c;其空间大小也就确定了。是JVM管理的最大一块内存空间。 堆内存的大小是可以调节的。堆可以处于物理上不连续的内存空间中&#xff…...

《深入解析:DOS检测的技术原理与方法》

DDOS入侵检测与防御 一、实现Linux下DDOS的入侵检测与防御 利用Python编程实现对wrk的泛洪攻击检测&#xff0c;并让程序触发调用Linux命令实现防御: 1、泛洪攻击的检测&#xff0c;可以考虑使用的命令&#xff0c;这些命令可以通过Python进行调用和分析 (1) netstat -ant …...

PID如何调试,如何配置P,I,D值,如何适配pwm的定时器配置,如何给小车配电源

首先你要搞清楚PID公式原理 PID算法解析PID算法解析_pid滤波算法-CSDN博客 然后你要明白调试原理 首先要确定一个电源 电源决定了你后面调试时电机转动速度大小和pwm占空比的关系&#xff0c;电源电压越大那要转到同一速度所需的占空比越小&#xff0c;反之电源电压越小那要…...

小马模拟器-第三方全街机游戏模拟器

链接&#xff1a;https://pan.xunlei.com/s/VOHSiB6st-f3RWlIK01MS2fUA1?pwd44v7# 1.小马模拟器是一款完全免费的游戏模拟器软件&#xff0c;支持街机&#xff08;FBA,MAME,PGM2&#xff09;,3DS,WII,NGC,DC,SS,DOS,MD,WSC,NDS,JAVA,PCE,FC,SFC,GBA,GBC,PSP,PS,N64等多种游戏…...

Qwen2-VL:在任何分辨率下增强视觉语言模型对世界的感知 (大型视觉模型 核心技术 分享)

摘要 我们推出了Qwen2-VL系列,这是对之前Qwen-VL模型的高级升级,重新定义了视觉处理中的常规预设分辨率方法。Qwen2-VL引入了Naive Dynamic Resolution机制,使模型能够动态地将不同分辨率的图像转换为不同的视觉令牌数量。这种方法允许模型生成更高效和准确的视觉表示,紧密…...

微信小程序date picker的一些说明

微信小程序的picker是一个功能强大的组件&#xff0c;它可以是一个普通选择器&#xff0c;也可以是多项选择器&#xff0c;也可以是时间、日期、省市区选择器。 官方文档在这里 这里讲一下date picker的用法。 <view class"section"><view class"se…...

MySQL 基础学习(2): INSERT 操作

在这篇文章中&#xff0c;我们将专注于 MySQL 中的 INSERT 操作&#xff0c;深入了解如何高效地向表中插入数据&#xff0c;并探索插入操作中的一些常见错误与解决方案。 一、基础 INSERT 语法 在 MySQL 中&#xff0c;INSERT 操作用于向表中插入新记录&#xff0c;基本语法如…...

关于opensips的帮助命令的解释

opensips -help以下是 opensips 命令及其选项的中文解释&#xff08;基于 3.6.0-dev 版本&#xff09;&#xff1a; 命令用法 opensips -l 地址 [-l 地址 ...] [选项]选项说明 选项功能-f 文件指定配置文件&#xff08;默认为 /usr/local//etc/opensips/opensips.cfg&#x…...

新项目传到git步骤

1.首先创建远程仓库,创建一个空白项目,即可生成一个克隆URL,可以是http也可以是SSH,copy下这个地址 2.找到项目的本机目录,进入根目录,打开git bash here命令行 3.初始化: git init 4.关联远程地址: git remote add origin "远程仓库的URL" 5.查看关联 git re…...

【力扣每日一题】LeetCode 2412: 完成所有交易的初始最少钱数

LeetCode 2412: 完成所有交易的初始最少钱数 题目解析 问题描述 给定一个二维数组 transactions&#xff0c;每个元素 transactions[i] [costi, cashbacki] 表示一个交易。对于每笔交易&#xff0c;要求你完成该交易时有足够的初始资金 money&#xff0c;并且交易会减少或增…...

【算法】递归型枚举与回溯剪枝初识

递归型枚举与回溯剪枝初识 1.枚举子集2.组合型枚举3.枚举排列4.全排列问题 什么是搜索&#xff1f;搜索&#xff0c;是一种枚举&#xff0c;通过穷举所有的情况来找到最优解&#xff0c;或者统计合法解的个数。因此&#xff0c;搜索有时候也叫作暴搜。搜索一般分为深度优先搜索…...

pytorch 多机多卡训练方法

在深度学习训练中&#xff0c;使用多机多卡&#xff08;多台机器和多块 GPU&#xff09;可以显著加速模型训练过程。 PyTorch 提供了多种方法来实现多机多卡训练&#xff0c;以下是一些常用的方法和步骤&#xff1a; 1. 使用 torch.distributed 包 PyTorch 的 torch.distribut…...

InfiniBand客户端注册机制详解:ib_register_client函数的作用与实现

在Linux内核的InfiniBand(IB)子系统中,ib_register_client函数扮演着至关重要的角色。它允许上层用户(如特定的IB设备驱动程序或相关应用模块)注册为IB客户端,并定义在IB设备添加或移除时应执行的回调函数。这一机制确保了IB设备的动态管理,以及资源的有效分配和回收。本…...