(C++)大数计算问题

news2024/11/25 22:35:42

文章目录

  • 一、实验目的、内容
  • 二、实验程序设计及结构
    • 1.需求分析
      • 变量
      • 函数
    • 2.设计结构或流程图
  • 三、设计过程
  • 四、测试分析
    • 第一组
    • 第二组
    • 实验中出现的bug及解决方案
  • 五、设计的特点和结果

一、实验目的、内容

大数是超过整数表示范围的整数,针对正整数运算,定义一个大数类,并编写两个大数类对象的加法和减法函数。

二、实验程序设计及结构

1.需求分析

bigu,内含p(unsigned char*)、n(size_t)数据成员及构造函数、析构函数等成员函数。

变量

bigu类型数据ab,用于测试数据。

函数

主函数、运算符重载函数及bigu类的成员函数。

2.设计结构或流程图

  1. 进行ab的输入。(调用构造函数及istream& operator>>(istream& c, bigu& x)
    1. 从输入的第一个数字字符开始,定义unsigned short临时数组(看作256进制数)。
    2. 每输入一个数字字符,每个数组元素乘10。
    3. 遍历临时数组,将超过255的元素执行进位操作;若数组长度不够,重新定义数组并拷贝。
    4. 不断输入直到遇到非数字字符为止。
    5. 将临时数组的数据拷贝进unsigned char数组并存储。
  2. 进行ab的加法并输出。(调用bigu operator+(const bigu& x, const bigu& y)ostream& operator<<(ostream& c, const bigu& x)
    1. 加法运算
      1. 根据整数运算进位的性质,和的元素个数为max(x.n,y.n)max(x.n,y.n)+1,故定义max(x.n,y.n)+1长度的临时数组。
      2. x.ny.n的大小分类讨论。
      3. 将临时数组初始化为xy各位元素之和。
      4. 遍历临时数组,对值超过255的执行进位操作。
      5. 将临时数组的数据拷贝进unsigned char数组并存储。
    2. 输出
      1. 定义临时数组。
      2. 从最高位开始,将超过9的数据进行十进制展开。
      3. 每次加新数之前将原来的所有元素乘256并对超过9的数进行进位。
      4. 输出并释放临时数组。
  3. 比较ab的大小,作差后输出。(调用bool operator>(const bigu& x, const bigu& y)bigu operator-(const bigu& x, const bigu& y)ostream& operator<<(ostream& c, const bigu& x)
    1. 大于运算
      1. 若两者元素个数不同,直接返回结果。
      2. 逐位比较,直到两者不相等为止。
    2. 减法运算(与加法类似)

三、设计过程

#include <iostream>
using namespace std;
class bigu
{
public:
    unsigned char *p;                                  // 指向正整数数组
    size_t n;                                          // 数组长度
    bigu() { *(p = new unsigned char[n = 1]) = '\0'; } // 默认构造函数:初始化为0
    // 带参数构造(无符号整型)
    bigu(unsigned long long x)
    {
        unsigned char a[8], *q(a), *t(q);
        *q = x;
        while (x >>= 8)
            *++q = x; // 逐字节读取
        *(p = new unsigned char[n = ++q - t]) = *t;
        while (++t < q)
            *++p = *t;
        ++(p -= n);
    }
    // 为了防止浅拷贝的问题,重写拷贝构造函数
    bigu(const bigu &x)
    {
        delete[] p; // 防止内存泄漏
        const unsigned char *q(x.p);
        *(p = new unsigned char[n = x.n]) = *q;
        while (--n)
            *++p = *++q;
        ++(p -= n = x.n);
    }
    ~bigu() { delete[] p; } // 析构函数
    // 为了加快效率、避免拷贝的有参构造函数
    bigu(unsigned char *a, size_t b)
    {
        p = a;
        n = b;
    }
};
// 重载关系运算符
bool operator>(const bigu &x, const bigu &y)
{
    if (x.n > y.n)
        return true;
    if (y.n > x.n)
        return false;
    const unsigned char *p(x.p + x.n), *q(y.p + y.n);
    do
        if (*--p > *--q)
            return true;
        else if (*p < *q)
            return false;
    while (p > x.p);
    return false;
}
// 重载加法运算符
bigu operator+(const bigu &x, const bigu &y)
{
    const unsigned char *p(x.p), *q(y.p); // 读取数据
    // 考虑新数组大小将会是x,y中较大的那个或是较大的加1
    // 故分类讨论
    if (y.n > x.n)
    {
        size_t n(x.n);
        unsigned short *a(new unsigned short[y.n + 1]), *t(a + y.n); // 定义临时数组
        // 向临时数组输入数据
        *a = *p + *q;
        while (--n)
            *++a = *++p + *++q;
        while (++a < t)
            *a = *++q;
        // 处理首位
        if (*(a -= (n = y.n)--) > 255)
        {
            *(a + 1) += *a / 256;
            *a %= 256;
        }
        // 处理头尾之间
        while (--n)
            if (*++a > 255)
            {
                *(a + 1) += *a / 256;
                *a %= 256;
            }
        // 处理最高位
        if (*++a > 255) // 超过了unsigned char表示的范围
        {
            // 拷贝、释放并返回
            unsigned char *u(new unsigned char[y.n + 1] + (n = y.n));
            *u = *a / 256;
            *a %= 256;
            do
                *--u = *--t;
            while (--n);
            delete[] t;
            return bigu(u, y.n + 1);
        }
        // 未超过unsigned char表示的范围
        unsigned char *u(new unsigned char[n = y.n] + y.n);
        *--u = *a;
        while (--n)
            *--u = *--a;
        delete[] a;
        return bigu(u, y.n);
    }
    // 此时x的位数大于等于y的位数
    // 操作过程同理
    if (x.n == 1)
        return bigu(*p + *q);
    size_t n(y.n);
    unsigned short *a(new unsigned short[x.n + 1]), *t(a + x.n);
    *a = *p + *q;
    while (--n)
        *++a = *++p + *++q;
    while (++a < t)
        *a = *++p;
    if (*(a -= (n = x.n)--) > 255)
    {
        *(a + 1) += *a / 256;
        *a %= 256;
    }
    while (--n)
        if (*++a > 255)
        {
            *(a + 1) += *a / 256;
            *a %= 256;
        }
    if (*++a > 255)
    {
        unsigned char *u(new unsigned char[x.n + 1] + (n = x.n));
        *u = *a / 256;
        *a %= 256;
        do
            *--u = *--t;
        while (--n);
        delete[] t;
        return bigu(u, x.n + 1);
    }
    unsigned char *u(new unsigned char[n = x.n] + x.n);
    *--u = *a;
    while (--n)
        *--u = *--a;
    delete[] a;
    return bigu(u, x.n);
}
// 重载减法运算符
// 默认x>=y
bigu operator-(const bigu &x, const bigu &y)
{
    if (x.n == 1) // 由于y<=x,只需要1字节内存空间
    {
        unsigned char *a(new unsigned char[1]);
        *a = *x.p - *y.p;
        return bigu(a, 1);
    }
    const unsigned char *p(x.p), *q(y.p);             // 读取
    short *a(new short[x.n]), *t(a), *r(a + x.n - 1); // 定义临时数组
    *t = *p - *q;
    size_t n(y.n);
    while (--n)
        *++t = short(*++p) - short(*++q); // 由于p,q为无符号指针,需要进行强制类型转换
    while (++t <= r)
        *t = *++p;
    if (*(t = a) < 0)
    {
        --*(t + 1);
        *t += 256;
    } // 对首位进行退位操作
    while (++t < r)
        if (*t < 0)
        {
            *(t + 1) += *t / 256 - 1;
            (*t %= 256) += 256;
        } // 对其余各位进行退位操作
    ++t;
    while (!*--t)
        ; // 找到首个大于0的高位
    // 拷贝数据进新的合适大小的数组
    unsigned char *u(new unsigned char[n = ++t - a] + n);
    do
        *--u = *--t;
    while (t > a);
    delete[] a; // 防止内存泄漏
    return bigu(u, n);
}
// 重载右移运算符,用以输入
istream &operator>>(istream &c, bigu &x)
{
    delete[] x.p; // 释放原来的数组,防止内存泄漏
    char s;       // 由于不知道大数的上限,采用逐字节读取方式
    do
        c.get(s);
    while (s == ' ' || s == '\n' || s == '\t'); // 排除空格、水平制表符和换行
    if (s < '0' || s > '9')                     // 如果输入的不是数字,按0处理
    {
        *(x.p = new unsigned char[x.n = 1]) = 0;
        return c;
    }
    unsigned short *a(new unsigned short[x.n = 10]), *b(a), *p; // 定义临时数组,用范围比unsigned char稍大的类型进行临时存储,a指向最低位,b指向目前的最高位
    *a = s - '0';                                               // 存储第一位
    c.get(s);
    while (s >= '0' && s <= '9')
    {
        (*(p = a) *= 10) += s - '0'; // 加上新数并乘10(位权)
        while (++p <= b)
            *p *= 10;       // 每一位乘10
        if (*(p = a) > 255) // 最低位超过了unsigned char的范围
            if (p == b)     // 只有1位的情况
            {
                *++b = *p / 256; // 改变尾指针
                *p %= 256;
                goto F; // 直接进行下一位的读取
            }
            else
            {
                *(p + 1) += *p / 256;
                *p %= 256;
            }
        else if (p == b)
            goto F;     // 只有1位而且不需要处理
        while (++p < b) // 处理头尾之间的数据
            if (*p > 255)
            {
                *(p + 1) += *p / 256;
                *p %= 256;
            }
        if (*p > 255)           // 最高位需要进位
            if (++b - a == x.n) // 事先的上限不够用,重新申请更大的存储空间并拷贝
            {
                unsigned short *u(a), *v(new unsigned short[x.n += 10]);
                *(p = v) = *a;
                while (++a < b)
                    *++p = *a;
                delete[] u;
                a = v;
                *(b = p + 1) = *p / 256;
                *p %= 256;
            }
            else // 够用,直接处理
            {
                *b = *p / 256;
                *p %= 256;
            }
    F:
        c.get(s);
    }
    // 将处理结果拷贝进unsigned char数组并释放临时数组
    unsigned char *q(x.p = new unsigned char[x.n = ++b - (p = a)]);
    *q = *p;
    while (++p < b)
        *++q = *p;
    delete[] a;
    return c;
}
// 重载左移运算符,用以输出
ostream &operator<<(ostream &c, const bigu &x)
{
    size_t m(10);                                           // 先假定一个上限
    unsigned short *t(new unsigned short[m]), *a(t), *b(t); // 用范围比unsigned char稍大的类型进行临时存储,a指向最低位,b指向目前的最高位
    const unsigned char *q(x.p + x.n);                      // 用于读取数据的指针
    if ((*t = *--q) > 9)
        do
        {
            *++b = *t / 10;
            *t %= 10;
        } while (*++t > 9); // 如果最高位大于等于10
    while (--q >= x.p)
    {
        (*(t = a) *= 256) += *q; // 新位加上原来的乘上位权(256)
        while (++t <= b)
            *t *= 256; // 各高位乘位权(256)
        t = a;
        while (t != b) // 处理头尾之间的位,使它们都小于10
        {
            *(t + 1) += *t / 10;
            *t %= 10;
            ++t;
        }
        if (*t > 9) // 如果最高位需要处理
        {
            if (false)
            F:
            { // 预先的上限不够用,重新申请更大的存储空间
                unsigned short *u(t = new unsigned short[m += 30]), *v(a);
                *u = *a;
                while (++a < b)
                    *++u = *a;
                delete[] v;
                a = t;
                t = b = u;
            }
                // 处理最高位
                do
                {
                    if (++b - a == m)
                        goto F;
                    *b = *t / 10;
                    *t++ %= 10;
                } while (*b > 9);
        }
    }
    c << *b; // 输出最高位
    while (--b >= a)
        c << *b; // 输出其余各位
    delete[] a;  // 释放临时数组
    return c;
}
// 主函数
// 用以测试数据
int main()
{
    bigu a, b;
    cout << "请输入大数a:\n";
    cin >> a;
    cout << "请输入大数b:\n";
    cin >> b;
    cout << "a+b=" << a + b << endl;
    if (a > b)
        cout << "a-b=" << a - b << endl;
    else
        cout << "b-a=" << b - a << endl;
    system("pause");
    return 0;
}

四、测试分析

第一组

在这里插入图片描述

第二组

在这里插入图片描述

实验中出现的bug及解决方案

实验中出现的bug:忽略了大数的进位、退位问题。

解决方案:定义更大的临时数据数组并逐位处理。

五、设计的特点和结果

设计采用的数据结构为数组。为了表示大数,把unsigned char数组看作256进制的数,而不是十进制数,节约了内存空间,并利用动态内存分配技术,解决了大数问题。在实验过程中,我原本是打算逐个扩充数组(每次数组长度加1),后来发现运行的效率很低,就采用了和标准库类型vector类似的内存分配方法,每次多扩充29位,解决了效率低的问题。

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

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

相关文章

VSCODE使用CMAKE显示命令无法找到

背景&#xff1a;使用了code server&#xff0c;安装CMAKE和CMAKE TOOLS&#xff0c;但是通过ctrlshiftp打开命令面板&#xff0c;运行随便一个cmake指令&#xff0c;都出现了指令无法找到。具体为“命令"CMake: 配置"导致错误 (command ‘cmake.configure’ not fou…

Tomcat10.X部署老版本axis2 webservice项目不生效

目录 一、使用场景 二、问题描述 三、原因排查 四、解决方案 一、使用场景 原来项目是OpenJDK8tomcat9构建&#xff0c;现在需要升级到OpenJDK17tomcat10的组合。原来的webservice项目打包成aar格式&#xff0c;通过axis2部署在tomcat上。 二、问题描述 在配置好jdk和to…

【办公类-21-01】20240117育婴员操作题word合并1.0

背景需求&#xff1a; 最近学校组织老师们学习“育婴员”高级&#xff0c;每周学习2题操作&#xff0c;所以我是把每个学习内容单独做在一个word文件里 上周8套保健操作学完了&#xff0c;需要整理&#xff0c;并将8份Word文件合并 第一步&#xff1a;doc装docx 合并时程序报…

Kafka-消费者-KafkaConsumer分析-ConsumerCoordinator

在前面介绍了Kafka中Rebalance操作的相关方案和原理。 在KafkaConsumer中通过ConsumerCoordinator组件实现与服务端的GroupCoordinator的交互&#xff0c;ConsumerCoordinator继承了AbstractCoordinator抽象类。 下面我们先来介绍AbstractCoordinator的核心字段&#xff0c;如…

mysql 容器化安装(docker)离线和在线

前言&#xff1a;在部署hive或airflow 升级过程中&#xff0c;总需要一个对应的数据库存储元数据&#xff0c;一个轻量级的mysql容器刚刚好。轻量、可快速移植、具有隔离性。 文章目录 1、查看机器版本2、安装 docker3、启动docker 服务4、docker 常用命令docker5、拉取mysql …

更适合3D项目的UI、事件交互!纯国产数字孪生引擎持续升级中!!!

UI和事件交互是3D可视化项目中最常见的模块&#xff0c;主要用于信息添加、展示&#xff0c;用来确保按照用户需求呈现内容并完成交互。 平时工作在进行UI和交互设计时&#xff0c;经常出现以下问题&#xff1a;UI过于复杂导致3D项目内交互效率低下&#xff0c;或者是结合3D项目…

ubuntu18.04 安装mysql(命令)

1.安装MySQL #命令1 sudo apt-get update #命令2 sudo apt-get install mysql-server 2.配置MySQL sudo mysql_secure_installation 2.2 检查mysql服务状态 systemctl status mysql.service 3.配置远程访问 在Ubuntu下MySQL缺省是只允许本地访问的 3.1 首先用根用户进入…

NR SCell Addition/Modification/Release过程详解

当配置 CA 时&#xff0c;UE 与网络只会有一个RRC 连接。 在 RRC 连接建立/重建/切换时&#xff0c;只有一个serving cell会提供 NAS mobility info&#xff0c;在 RRC connection reestablishment/handover时&#xff0c;同样只有一个serving cell会提供security info&#xf…

关于KT6368A双模蓝牙芯片的BLE在ios的lightblue大数量数据测试

测试简介 关于KT6368A双模蓝牙芯片的BLE在ios的lightblue app大数量数据测试 测试环境&#xff1a;iphone7 。KT6368A双模程序96B6 App&#xff1a;lightblue ios端 可以打开log日志查看通讯流程 测试数据&#xff1a;长度是1224个字节&#xff0c;单次直接发给KT6368A&a…

C++ mapsetOJ

目录 1、138. 随机链表的复制 2、692. 前K个高频单词 3、349. 两个数组的交集 1、138. 随机链表的复制 /* // Definition for a Node. class Node { public:int val;Node* next;Node* random;Node(int _val) {val _val;next NULL;random NULL;} }; */class Solution { pub…

vscode开发java项目

安装java扩展 创建项目 vscode命令行面板搜索java命令行 出现如下提示 No build tools构建项目如下 java项目使用vscode单独打开文件夹&#xff08;工作区中运行有问题&#xff09;&#xff0c;vscode中可直接点击右上角运行按钮执行 maven中spring boot项目 代码错误可以点…

Chrome 浏览器插件 cookies API 解析

Chrome.cookie 前端开发肯定少不了和 cookie 打交道&#xff0c;此文较详细的介绍下 chrome.cookie 的 API 以及在 popup、service worker、content 中如何获取的 一、权限&#xff08;Permissions&#xff09; 如果需使用 Cookie API&#xff0c;需要在 manifest.json 文件…

第90讲:MySQL数据库主从复制集群原理概念以及搭建流程

文章目录 1.MySQL主从复制集群的核心概念1.1.什么是主从复制集群1.2.主从复制集群中的专业术语1.3.主从复制集群工作原理1.4.主从复制中的小细节1.5.搭建主从复制集群的前提条件1.6.MySQL主从复制集群的架构信息 2.搭建MySQL多实例环境2.1.在mysql-1中搭建身为主库的MySQL实例2…

2024年1月中国数据库排行榜: OPOT 组合续写贺新年,达梦、腾讯发力迎升势

2024年开局&#xff0c;墨天轮中国数据库流行度排行火热出炉&#xff0c;292个国产数据库齐聚榜单。整体来看&#xff0c;榜单前十整体变化不大&#xff0c;“O-P-O”格局稳固&#xff0c;前五位名次未发生变动。但新年伊始&#xff0c;各家数据库热度上升迅猛&#xff0c;分数…

Linux下进程子进程的退出情况

进程的退出分为了两大类&#xff0c;一类是正常的退出&#xff0c;另一类是非正常的退出。 正常退出时有五种情况&#xff0c;分别是 ①main函数调用return ②进程调用exit(),标准c库 ③进程调用_exit()或者_Exit()&#xff0c;属于系统调用 ④进程最后一个线程返回 ⑤最…

密码产品推介 | 沃通安全电子签章系统(ES-1)

产品介绍 沃通安全电子签章系统&#xff08;ES-1&#xff09;是一款基于密码技术、完全自主研发的商用密码产品&#xff0c;严格遵循国家密码管理局制定的相关标准&#xff0c;可为企业和个人提供安全、合规的电子签章功能服务。产品的主要用途是为各类文书、合同、表单等电子…

Java-NIO 开篇(1)

NIO简介 高性能的Java通信&#xff0c;离不开Java NIO组件&#xff0c;现在主流的技术框架或中间件服务器&#xff0c;都使用了Java NIO组件&#xff0c;譬如Tomcat、 Jetty、 Netty、Redis、RabbitMQ等的网络通信模块。在1.4版本之前&#xff0c; Java IO类库是阻塞式IO&…

如何将信用卡或借记卡关联到您的PayPal账户

第1步&#xff1a;在PayPal控制面板中关联 您在登录到PayPal账户之后&#xff0c;前往顶部菜单条中的 “钱包”&#xff0c;然后点击“关联卡”。 第2步&#xff1a;输入您的信用卡或借记卡详情 第3步&#xff1a;添加新的借记卡或信用卡 使用PayPal绑定信用卡吧&#xff0c;…

Node.js基础---fs文件系统 读取和写入

什么是nodejs? 脚本语言需要一个解析器才能运行&#xff0c;JavaScript是脚本语言&#xff0c;在不同的位置有不一样的解析器&#xff0c;如写入html的js语言&#xff0c;浏览器是它的解析器角色。而对于需要独立运行的JS&#xff0c;nodejs就是一个解析器。 每一种解析器都是…

vue3项目eslint配置、配置prettier(格式化配置)

文章链接: 全部配置链接 第一步:eslint配置、配置prettier(代码格式化):点击链接 (1) .eslint.cjs—eslint配置文件 (2).eslintignore—校验忽略文件 (3).prettierrc.json添加规则 (4).prettierignore忽略文件 prettierrc规范说明: 第二步:styleLint配置 样式链接 第三…