C++程序设计上机作业(1)

目录

每日一句

日出未必意味着光明

太阳也无非是一颗晨星而已
只有在我们醒着的时候
才是真正的破晓

------ 梭罗

实验目的

本实验旨在通过实际编程练习,深入理解和掌握 C++ 函数的相关知识与应用技巧,具体包括:

(1)函数的基本知识理解和运用,掌握函数的定义、声明、调用及参数传递方式

(2)函数默认参数值的使用方法,理解默认参数在函数重载和函数调用中的作用

(3)递归函数的设计与实现,掌握递归算法的基本思想和递归函数的执行过程

(4)函数重载的概念与应用,学会通过函数重载实现多态性,提高代码的灵活性和可读性

通过完成本次实验,培养分析问题、设计算法和编写程序的能力,为后续更复杂的程序设计打下坚实基础。

实验环境

操作系统:Windows 10专业版 X64

开发工具:VS code

编程语言:C++

编译标准:C++11

1、水仙花数(Narcissistic number)

水仙花数也被称为超完全数字不变数、自幂数等,水仙花数是指一个 3 位数,它的每个数位上的数字的 3次幂之和等于它本身。例如:1^3 + 5^3+ 3^3 = 153。

四叶玫瑰数是指一个 4 位数,它的每个数位上的数字的 4次幂之和等于它本身。

使用C++编程实现判断一个数是否是水仙花数或者四叶玫瑰数,函数原型如下:

// number : 带判断整数

// n : 数字位数,3-水仙花数,4-四叶玫瑰数

// 返回值: true-是,false-否

bool IsNarcis(int number, int n=3);

基本思路

要判断一个数是否为自幂数(水仙花数或四叶玫瑰数),需要完成以下步骤:

验证输入参数的合法性:n 只能是 3 或 4,对应 3 位数和 4 位数的自幂数

验证数字的位数是否与 n 一致:3 位数的范围是 100-999,4 位数的范围是 1000-9999

分解数字的每一位,计算每一位的 n 次幂之和

判断幂之和是否等于原数字,如果等于则是自幂数,否则不是

思路实现设计

1.参数合法性检查 :首先检查 n 是否为 3 或 4,如果不是则返回 false

2.数字范围检查 :根据 n 的值确定数字的合法范围(3 位数 100-999,4 位数 1000-9999),如果数字不在相应范围内则返回 false

3.数字分解与幂计算

  • 使用循环结构,通过取余运算(number % 10)提取数字的最后一位
  • 计算该位的 n 次幂并累加到总和中
  • 通过除法运算(number / 10)移除数字的最后一位

重复上述步骤直到数字变为 0

4.结果判断:比较累加的幂之和与原数字,如果相等则返回 true,否则返回 false

代码

cpp 复制代码
#include <iostream>
using namespace std;

// 判断数
bool IsNarcis(int number, int n = 3)
{
    // 检查n是否合法(仅支持3或4)
    if (n != 3 && n != 4)
    {
        return false;
    }

    // 确定位数范围
    int min_val = (n == 3) ? 100 : 1000; // 3位数最小100,4位数最小1000
    int max_val = (n == 3) ? 999 : 9999; // 3位数最大999,4位数最大9999

    // 检查数字是否在合法范围内
    if (number < min_val || number > max_val)
    {
        return false;
    }

    int temp = number;
    int sum = 0;

    // 计算每个数位的n次幂之和
    while (temp > 0)
    {
        int digit = temp % 10; // 提取最后一位数字
        if (n == 3)
        {
            sum += digit * digit * digit;
        }
        else
        {
            sum += digit * digit * digit * digit; // 4次方
        }
        temp /= 10; // 移除最后一位数字
    }

    // 判断总和是否等于原数字
    return (sum == number);
}

int main()
{
    int num;
    cout << "请输入一个3位或4位整数:";
    cin >> num;

    // 检查输入的数字是否为3位或4位
    if ((num >= 100 && num <= 999) || (num >= 1000 && num <= 9999))
    {
        // 判断并输出结果
        bool isNarcissus = IsNarcis(num, 3);
        bool isRose = IsNarcis(num, 4);

        cout << num << " 是水仙花数(3位)吗?" << (isNarcissus ? " 是" : " 否") << endl;
        cout << num << " 是四叶玫瑰数(4位)吗?" << (isRose ? " 是" : " 否") << endl;
    }
    // 不符合要求的数
    else
    {
        cout << "数字不符合要求" << endl;
    }

    return 0;
}

代码解析

1.函数定义与参数说明:

  • 函数IsNarcis接收两个参数,number是待判断的整数,n是数字的位数,默认值为 3
  • 返回值为布尔类型,表示number是否为n位的自幂数

2.参数合法性检查:

  • 首先检查n是否为 3 或 4,如果不是则返回 false,因为函数只支持这两种情况
  • 根据n的值确定数字的合法范围,3 位数为100-999,4 位数为 1000-9999
  • 如果number不在相应的范围内,则返回 false

3.数字分解与幂计算:

  • 使用临时变量temp存储number的值,避免修改原变量
  • 通过temp % 10提取最后一位数字
  • 根据n的值计算该数字的 3 次方或 4次方,并累加到sum中
  • 通过temp /= 10移除最后一位数字,继续处理下一位
  • 循环直到temp变为 0,此时已处理完所有位数

4.主函数实现:

  • 接收用户输入的数字,并检查其是否为 3 位或 4 位
  • 调用IsNarcis函数分别判断该数字是否为水仙花数(n=3)和四叶玫瑰数(n=4)
  • 输出判断结果
  • 增加了额外的测试案例,自动测试已知的水仙花数和四叶玫瑰数

运行截图

结果分析

程序与预定结果一样,可以接收 3 位或 4 位整数,判断是否为水仙花数(各位立方和等于自身)或四叶玫瑰数(各位四次方和等于自身),位数不合则提示 "数字不合要求"。

2、辗转相除法

辗转相除法,又称欧几里得算法,是一种用于计算两个正整数最大公约数(GCD)的经典算法,其核心原理基于数学公式 ‌gcd(a, b) = gcd(b, a mod b)‌,通过反复用除数除以余数直至余数为零,最终得到最大公约数。‌

使用C++编程实现上述方法,要求:

1)按照如下函数原型,使用循环结构实现函数。

cpp 复制代码
int GreatestCommonDivisor(int a, int b);

2)使用递归方式实现函数。

cpp 复制代码
int gcd(int a, int b);

3)使用4组以上测试数据测试上述代码的正确性。

基本思路

最大公约数(GCD)是指两个或多个整数共有约数中最大的一个。例如,8 和 12 的最大公约数是 4,因为 4 是能同时整除 8 和 12 的最大整数。

辗转相除法的基本思想是:

  • 对于两个正整数 a 和 b(a > b),用 a 除以 b 得到余数 r
  • 如果 r = 0,则 b 就是最大公约数
  • 如果 r ≠ 0,则以 b 和 r 为新的两个数,重复上述过程,直到余数为 0
  • 最后一个非零余数就是原来两个数的最大公约数
    此外,需要考虑以下特殊情况:
  • 如果其中一个数为 0,则最大公约数是另一个非零数如果两个数都是 0,则最大公约数无定义(通常返回 0)
  • 负数的最大公约数与它们的绝对值的最大公约数相同

思路实现设计

1.循环结构实现(GreatestCommonDivisor 函数):

  • 处理负数:将输入的 a 和 b 转换为它们的绝对值
  • 处理边界情况:如果 b 为 0,则返回 a
  • 循环计算:当 b 不为 0 时,计算 a 除以 b 的余数 r,然后将 b 赋值给 a,将 r 赋值给 b
  • 循环结束时,a 的值就是最大公约数
    2.递归结构实现(gcd 函数):
  • 处理负数:将输入的 a 和 b 转换为它们的绝对值
  • 递归基线条件:如果 b 为 0,则返回 a
  • 递归调用:否则返回 gcd (b, a % b)
    3.测试数据设计:
  • 两个正整数,如 (8, 12)
  • 包含一个负数,如 (18, -12)
  • 两个数成倍数关系,如 (15, 45)
  • 两个质数,如 (7, 13)

代码

cpp 复制代码
#include <iostream>
#include <cstdlib>
using namespace std;

// 1. 循环结构
int GreatestCommonDivisor(int a, int b) {
    // 处理负数(GCD结果恒为正数)
    a = abs(a);
    b = abs(b);
    
    // 辗转相除法核心循环:b不为0时持续迭代
    while (b != 0) {
        int remainder = a % b;  // a除以b的余数
        a = b;                 // 更新a为当前除数b
        b = remainder;         // 更新b为余数,进入下一轮计算
    }
    
    return a;  // 当b=0时,a为最大公约数
}

// 2. 递归方式
int gcd(int a, int b) {
    // 处理负数,转为非负值
    a = abs(a);
    b = abs(b);
    
    // 递归基线条件:b为0,返回a(此时a为最大公约数)
    if (b == 0) {
        return a;
    } else {
        // 递归调用
        return gcd(b, a % b);
    }
}
int main() {
    int num1, num2;
    cout << "请输入两个整数:";
    cin >> num1 >> num2;
    
    // 调用循环结构函数
    int result1 = GreatestCommonDivisor(num1, num2);
    // 调用递归结构函数
    int result2 = gcd(num1, num2);
    
    cout << "循环法计算的最大公约数:" << result1 << endl;
    cout << "递归法计算的最大公约数:" << result2 << endl;
    
    return 0;
}

代码解析

1.循环结构实现(GreatestCommonDivisor 函数):

  • 首先使用abs函数将输入的 a 和 b 转换为绝对值,以处理负数情况
  • 使用while循环进行辗转相除:
    • 计算 a 除以 b 的余数remainder
    • 将 b 的值赋给 a,将余数的值赋给 b
    • 当 b 变为 0 时,循环结束,此时 a 的值就是最大公约数
  • 返回 a 作为结果

2.递归结构实现(gcd 函数):

  • 同样先将 a 和 b 转换为绝对值
  • 递归的基线条件:如果 b 为 0,则返回 a 作为结果
  • 递归调用:否则返回gcd(b, a % b),体现了辗转相除法的核心思想

3.测试函数(testGCD):

  • 接收两个整数作为参数
  • 分别调用循环法和递归法计算最大公约数
  • 输出测试数据和两种方法的计算结果
  • 验证两种方法的结果是否一致

4.主函数(main):

  • 预设了 8 组测试数据,涵盖了各种情况
  • 调用 testGCD 函数对每组数据进行测试
  • 提供用户交互部分,允许用户输入两个整数并计算它们的最大公约数
  • 输出两种方法的计算结果

运行截图

结果分析

  • 两种实现方法(循环和递归)对于所有测试数据都能得到相同的结果
  • 程序能够正确处理正数、负数和零的各种组合情况
  • 对于较大的整数,算法仍然能够高效地计算出结果
  • 递归实现没有出现栈溢出的情况,说明算法的递归深度是可控的

3、最小公倍数

函数重载求解最小公倍数lcm(Least Common Multiple)。实现以下函数以求解2、3、4个整数的最小公倍数,并使用3组对应数量的数据进行测试。

cpp 复制代码
int  lcm(int a, int b);
int  lcm(int a, int b, int c);
int  lcm(int a, int b, int c, int d);

基本思路

最小公倍数与最大公约数之间存在密切的数学关系:对于两个正整数 a 和 b,它们的最小公倍数等于它们的乘积除以它们的最大公约数,即:

LCM(a, b) = |a × b| / GCD(a, b)

这个公式是计算最小公倍数的基础。对于多个数的最小公倍数,可以通过逐步计算的方式得到:

  • 三个数的最小公倍数:LCM (a, b, c) = LCM (LCM (a, b), c)
  • 四个数的最小公倍数:LCM (a, b, c, d) = LCM (LCM (a, b, c), d)

需要考虑的特殊情况:

  • 如果其中一个数为 0,则最小公倍数为 0
  • 负数的最小公倍数与它们的绝对值的最小公倍数相同

思路实现设计

1.最大公约数计算:

复用之前实现的循环结构的最大公约数函数GreatestCommonDivisor

2.两个数的最小公倍数(lcm 函数):

  • 处理负数:将输入的 a 和 b 转换为它们的绝对值
  • 处理特殊情况:如果其中一个数为 0,则返回 0
  • 应用公式:LCM (a, b) = (a × b) / GCD (a, b)
    3.三个数的最小公倍数(重载 lcm 函数):
  • 先计算前两个数的最小公倍数
  • 再计算结果与第三个数的最小公倍数
  • 即:lcm (a, b, c) = lcm (lcm (a, b), c)
    4.四个数的最小公倍数(重载 lcm 函数):
  • 先计算前三个数的最小公倍数
  • 再计算结果与第四个数的最小公倍数
  • 即:lcm (a, b, c, d) = lcm (lcm (a, b, c), d)
    5.测试数据设计:
  • 两个数的情况:如 (4, 6)、(5, 7)、(0, 5)
  • 三个数的情况:如 (2, 3, 4)、(6, 8, 12)、(5, 0, 10)
  • 四个数的情况:如 (2, 3, 4, 5)、(3, 6, 9, 12)、(7, 14, 21, 28)

代码

cpp 复制代码
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

// 最大公约数计算
int GreatestCommonDivisor(int a, int b)
{
    a = abs(a);
    b = abs(b);
    while (b != 0)
    {
        int remainder = a % b;
        a = b;
        b = remainder;
    }
    return a;
}

// 2个整数的最小公倍数
int lcm(int a, int b)
{
    a = abs(a);
    b = abs(b);
    return (a == 0 || b == 0) ? 0 : (a * b) / GreatestCommonDivisor(a, b);
}

// 3个整数的最小公倍数(重载)
int lcm(int a, int b, int c)
{
    return lcm(lcm(a, b), c);
}

// 4个整数的最小公倍数(重载)
int lcm(int a, int b, int c, int d)
{
    return lcm(lcm(a, b, c), d);
}

int main()
{
    vector<int> nums; // 存储输入的整数
    int num;

    cout << "请输入2-4个整数(用空格分隔,回车结束):";

    // 读取输入的所有整数,直到回车或输入错误
    while (cin >> num)  // ctrl+Z 结束
    {
        nums.push_back(num);
        // 输入数量超过4个,立刻停止读取
        if (nums.size() >= 4)
            break;
    }

    // 判断输入的数量来调用对应函数
    switch (nums.size())
    {
    case 2:
        cout << "最小公倍数:" << lcm(nums[0], nums[1]) << endl;
        break;
    case 3:
        cout << "最小公倍数:" << lcm(nums[0], nums[1], nums[2]) << endl;
        break;
    case 4:
        cout << "最小公倍数:" << lcm(nums[0], nums[1], nums[2], nums[3]) << endl;
        break;
    default:
        cout << "输入错误!请输入2-4个整数。" << endl;
    }

    return 0;
}

代码解析

1.最大公约数函数(GreatestCommonDivisor):

  • 复用之前实现的循环结构的最大公约数计算函数
  • 用于支持最小公倍数的计算

2.两个数的最小公倍数函数(lcm):

  • 首先将输入的 a 和 b 转换为绝对值,处理负数情况
  • 检查是否有 0,如果有则返回 0
  • 应用公式计算并返回最小公倍数:(a * b) / GCD (a, b)

3.三个数的最小公倍数函数(重载 lcm):

  • 利用两个数的 lcm 函数,先计算前两个数的最小公倍数
  • 再计算该结果与第三个数的最小公倍数
  • 体现了多个数的最小公倍数可以通过逐步计算得到的思想

4.四个数的最小公倍数函数(重载 lcm):

  • 利用三个数的 lcm 函数,先计算前三个数的最小公倍数
  • 再计算该结果与第四个数的最小公倍数
  • 进一步扩展了最小公倍数的计算能力

5.测试函数:

  • testLCM2:测试两个数的最小公倍数计算
  • testLCM3:测试三个数的最小公倍数计算
  • testLCM4:测试四个数的最小公倍数计算
  • 每个测试函数都输出输入数据和计算结果

6.主函数(main):

  • 预设了多组测试数据,涵盖不同情况
  • 调用测试函数对预设数据进行测试
  • 提供用户交互部分,允许用户输入 2-4 个整数
  • 根据用户输入的整数数量,调用相应的 lcm 函数并输出结果

运行截图

结果分析

  • 函数重载工作正常,根据参数数量的不同,调用了相应的函数
  • 所有测试案例的计算结果都符合预期
  • 程序能够正确处理包含负数和零的情况
  • 多参数的最小公倍数计算通过逐步计算的方式实现,逻辑正确

总结

通过本次实验,我深入理解和掌握了 C++ 函数的相关知识和应用技巧,具体包括以下几个方面:

1.函数的基本应用:

  • 掌握了函数的定义、声明和调用方法
  • 学会了通过函数参数传递数据和通过返回值获取结果
  • 理解了函数的封装性,将特定功能封装到函数中,提高了代码的复用性和可读性

2.函数默认参数:

  • 在水仙花数判断函数中使用了默认参数,使函数调用更加灵活
  • 理解了默认参数的使用规则,如默认参数必须放在参数列表的后面

3.递归函数:

  • 实现了递归版本的最大公约数计算函数
  • 深入理解了递归的基本思想:将大问题分解为小问题,通过解决小问题来解决大问题
  • 掌握了递归函数的设计要点:必须有明确的基线条件,避免无限递归

4.函数重载:

  • 通过函数重载实现了计算 2、3、4 个整数的最小公倍数
  • 理解了函数重载的概念:允许有多个同名函数,只要它们的参数列表不同
  • 体会到函数重载带来的好处:提高了代码的可读性和易用性,体现了多态性

5.算法设计与实现:

  • 学会了分析问题并设计相应的算法
  • 掌握了自幂数判断、辗转相除法、最小公倍数计算等经典算法
  • 学会了设计测试用例,验证算法的正确性在实验过程中,我也遇到了

6.一些问题和挑战:

  • 边界情况处理:如处理 0、负数等特殊情况,需要仔细考虑算法的正确性
  • 递归深度控制:在实现递归函数时,需要确保有正确的基线条件,避免栈溢出
  • 函数重载的使用:需要注意参数列表的差异,确保重载函数能够被正确调用

通过解决这些问题,我的编程能力和问题分析能力得到了提升。同时,我也认识到编写健壮的程序需要考虑各种可能的情况,不仅要使程序在正常情况下工作,还要能处理各种异常情况。

相关推荐
Chen--Xing2 小时前
OpenMP并行化编程指南
c++·密码学·openmp
乱飞的秋天2 小时前
C++中的特殊成员函数
开发语言·c++
小严家3 小时前
Flutter完整开发指南 | Flutter&Dart – The Complete Guide
开发语言·flutter
宇宙的尽头是PYTHON3 小时前
用生活中的实例解释java的类class和方法public static void main
java·开发语言·生活
道传科技上位机3 小时前
C# 循环和条件用法大全(while dowhile for foreach if Switch try)全站最全
开发语言·c#
寻星探路3 小时前
Java EE初阶启程记04---线程的状态
java·开发语言·jvm·java-ee
攻城狮7号3 小时前
【AI时代速通QT】第八节:Visual Studio与Qt-从项目迁移到多版本管理
c++·qt·跨平台·visual studio·qt vs tools
努力也学不会java3 小时前
【Java并发】揭秘Lock体系 -- 深入理解ReentrantLock
java·开发语言·人工智能·python·机器学习·reentrantlock
郝学胜-神的一滴3 小时前
QAxios研发笔记(一):在Qt环境下,构建Promise风格的Get请求接口
开发语言·c++·spring boot·qt·ajax·前端框架·软件工程