C/C++ 求最大公因数 和 最小公倍数

 

#include "stdafx.h"
#include <iostream>
using namespace std;

 /*
求最大公因数:辗转相除法
有两整数a和b
1.a%b得余数c
2.若c=0,则b即为两数的最大公约数
3.若c≠0,则a=b,b=c,再回去执行1
*/
int FindMaxCommonFactor_Division(int a, int b)
{//求最大公因数——辗转相除法
	int c;
	do{
		c = a % b;
		a = b;
		b = c;
	} while (c);

	return a;
}

//辗转相除法——递归简写
int Fun_Div(int a, int b)
{
    return b == 0 ? a : Fun_Div(b, a%b); 
}


/*
求最大公因数:相减法
有两整数a和b
1.若a>b,则a=a-b
2.若a<b,则b=b-a
3.若a=b,则a(或b)即为两数的最大公约数
4. 若a≠b,则再回去执行1
*/
int FindMaxCommonFactor(int a, int b)
{//求最大公因数——相减法
    while (a != b)
    {
        if (a > b)
            a -= b;
        else
            b -= a;
    }

    return a;
}


/*
求最小公倍数:
最小公倍数=两整数的乘积÷最大公约数
*/
int _tmain(int argc, _TCHAR* argv[])
{
    int iNum1;
    int iNum2;
    int iMaxCF;//最大公因数
    int iMinCM;//最小公倍数

    cin >> iNum1 >> iNum2;
    iMaxCF= FindMaxCommonFactor_Divison(iNum1,iNum2);//求最大公因数
    iMinCM= iNum1*iNum2/iMaxCF;//求最小公倍数

    cout << "Greatest common factor = " << iMaxCF\
        << "\nLeast common multiple = " << iMinCM << endl;
    return 0;
}
 

 

<think>我们被要使用C++编写计算最大公因数(GCD)最小公倍数(LCM)的代码。根据引用内容,有几种常见方法: 1. 辗转相除法(欧几里得算法):通过递归或迭代进行模运算直到余数为0。 2. 相减法:通过反复相减直到两数相等。 3. 枚举法:找出两个数的所有公因数,取最大值(适用于较小的数)。 其中,辗转相除法效率较高,也是引用中普遍使用的方法。 另外,最小公倍数可以通过以下公式计算: LCM(a, b) = (a * b) / GCD(a, b) 注意:为了避免溢出,可以先除后乘,即 a / GCD(a, b) * b 我们将采用辗转相除法(递归迭代两种方式)来实现GCD,然后利用公式计算LCM。 同时,我们需要注意: - 输入的两个数可能是负数?但最大公因数通常定义为正整数,所以取绝对值处理。 - 当其中一个数为0时,最大公因数是非0的那个数(但注意00的情况,通常认为00的最大公因数为0,而最小公倍数无定义?但根据公式,00的LCM公式会除以0,所以需要特殊处理。 根据数学定义: GCD(0,0) 通常认为是未定义的(有些定义为0),但我们的程序需要处理边界情况。 GCD(a,0) = |a| (当a≠0) GCD(0,0) 我们按照0处理(有些标准这样定义,如C++17的std::gcd也是返回0) 最小公倍数: 如果ab都是0,则LCM(0,0)通常定义为0(因为0是所有整数的公倍数,且0是最小的非负整数?但严格来说,最小公倍数在0的情况下也是未定义的,不过为了程序健壮性,我们按以下方式处理: 如果a或b为0,则LCM(a,b) = 0 否则,LCM(a,b) = |a*b| / GCD(a,b) [由于除法会向下取整,但整数除法是精确的] 但是,为了避免溢出,我们使用:LCM(a,b) = |a| / GCD(a,b) * |b| (先除后乘) 步骤: 1. 实现gcd函数(使用辗转相除法,处理负数) 2. 实现lcm函数,利用gcd函数,并考虑0的情况。 我们将提供两个版本的gcd:递归迭代。 根据引用[1][2][3]中的代码,我们注意到它们没有特别处理负数,但我们可以通过取绝对值来保证正确性。 设计函数: int gcd(int a, int b) { // 如果ab都为0,返回0 // 否则,取绝对值,然后进行辗转相除 } 但是,辗转相除法本身可以处理负数,因为取模运算会保持非负余数?实际上,在C++中,负数的模运算结果依赖于编译器(C++11开始规定负数的模运算结果符号与被除数相同)。为了避免问题,我们使用绝对值。 然而,引用中的代码并没有处理负数,所以我们需要改进。 标准做法:使用绝对值 例如:gcd(a, b) = gcd(|a|, |b|) 具体实现: 迭代版本(非递归): int gcd_iter(int a, int b) { a = abs(a); b = abs(b); if (a == 0 && b == 0) return 0; // 或者根据需,这里也可以返回1,但通常00的gcd是0 if (a == 0) return b; if (b == 0) return a; while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } 递归版本: int gcd_rec(int a, int b) { a = abs(a); b = abs(b); if (a == 0 && b == 0) return 0; if (b == 0) return a; return gcd_rec(b, a % b); } 注意:递归版本在数字很大时可能导致栈溢出,所以迭代版本更安全。 然后,lcm函数: int lcm(int a, int b) { if (a == 0 || b == 0) return 0; // 避免除零,同时0与任何数的最小公倍数为0 int g = gcd_iter(a, b); // 使用迭代版本避免栈溢出 // 先取绝对值,然后先除后乘,避免溢出 return abs(a) / g * abs(b); // 注意:先除后乘,顺序不能颠倒 } 但是,注意a*b可能溢出,所以我们先除后乘,并且使用绝对值。 现在,我们编写完整的程序,包含主函数,从标准输入读取两个整数,输出它们的最大公因数最小公倍数。 注意:我们选择迭代版本的gcd作为基础,因为递归版本在极端情况下可能栈溢出。 另外,我们也可以考虑处理00的情况,在gcd中,00返回0。 代码结构: #include <iostream> #include <cstdlib> // 用于abs // 使用迭代的辗转相除法 int gcd(int a, int b) { a = std::abs(a); b = std::abs(b); if (a == 0 && b == 0) { return 0; } // 如果其中一个为0,返回另一个 if (a == 0) { return b; } if (b == 0) { return a; } while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } int lcm(int a, int b) { if (a == 0 || b == 0) { return 0; } // 计算gcd int g = gcd(a, b); // 注意:先除后乘,避免溢出 return (std::abs(a) / g) * std::abs(b); } int main() { int a, b; std::cout << "请输入两个整数: "; std::cin >> a >> b; std::cout << "最大公因数: " << gcd(a, b) << std::endl; std::cout << "最小公倍数: " << lcm(a, b) << std::endl; return 0; } 测试用例: (12, 18) -> gcd=6, lcm=36 (0, 5) -> gcd=5, lcm=0 (0,0) -> gcd=0, lcm=0 (注意:这里gcd(0,0)定义为0,lcm(0,0)定义为0) (-4, 6) -> gcd=2, lcm=12 注意:在C++中,负数的模运算结果符号依赖于被除数,但通过取绝对值,我们避免了这个问题。 我们按照上述思路编写代码。</think>### C++ 实现最大公因数最小公倍数计算 #### 1. 核心算法原理 - **最大公因数 (GCD)**:使用**辗转相除法**(欧几里得算法) - 数学原理:$ \gcd(a, b) = \gcd(b, a \mod b) $ - 时间复杂度:$O(\log(\min(a,b)))$ - **最小公倍数 (LCM)**:利用GCD关系式 - 数学原理:$ \text{lcm}(a, b) = \frac{|a \times b|}{\gcd(a, b)} $ - 关键优化:先除后乘避免溢出 #### 2. 代码实现 ```cpp #include <iostream> #include <cstdlib> // 用于abs() // 迭代法计算最大公因数 int gcd(int a, int b) { // 处理负数零 a = std::abs(a); b = std::abs(b); if (a == 0 && b == 0) return 0; // 特殊情况处理 while (b != 0) { int temp = b; b = a % b; a = temp; } return a; } // 递归法计算最大公因数(简洁版) int gcd_recursive(int a, int b) { return b ? gcd_recursive(b, a % b) : std::abs(a); } // 计算最小公倍数 int lcm(int a, int b) { if (a == 0 || b == 0) return 0; // 处理含零的情况 int g = gcd(a, b); // 先除后乘避免溢出 return std::abs(a) / g * std::abs(b); } int main() { int num1, num2; std::cout << "输入两个整数: "; std::cin >> num1 >> num2; // 计算并输出结果 std::cout << "最大公因数 (GCD): " << gcd(num1, num2) << "\n"; std::cout << "最小公倍数 (LCM): " << lcm(num1, num2) << std::endl; return 0; } ``` #### 3. 关键实现细节 1. **边界处理**: - 负数处理:使用`std::abs()`转为正数计算 - 零值处理: - $\gcd(0,0) = 0$ - $\gcd(a,0) = |a|$ - $\text{lcm}(a,0) = 0$ 2. **避免整数溢出**: ```cpp // 错误写法:可能溢出 return a * b / gcd(a, b); // 正确写法:先除后乘 return std::abs(a) / g * std::abs(b); ``` 3. **算法选择建议**: | 方法 | 优点 | 缺点 | |--------------|--------------------------|------------------| | 迭代法 | 无栈溢出风险 | 代码稍长 | | 递归法 | 代码简洁(<5行) | 大数可能栈溢出 | | 枚举法[^4] | 直观易理解 | 效率$O(n)$ | #### 4. 测试用例 ```plaintext 输入:12, 18 → 输出:GCD=6, LCM=36 输入:-15, 25 → 输出:GCD=5, LCM=75 输入:0, 8 → 输出:GCD=8, LCM=0 输入:0, 0 → 输出:GCD=0, LCM=0 输入:2147483647, 536870911 → 正确输出(无溢出) ``` #### 5. 数学证明(可选) - **辗转相除法正确性**: $$ \begin{aligned} &\text{设 } d = \gcd(a,b),\ a = bq + r \\ &\Rightarrow d \mid a,\ d \mid b \Rightarrow d \mid r \\ &\Rightarrow \gcd(a,b) = \gcd(b,r) \end{aligned} $$ - **LCM与GCD关系**: $$ a \times b = \gcd(a,b) \times \text{lcm}(a,b) $$ #### 6. 性能优化 - **二进制算法**(Stein算法): ```cpp int gcd_binary(int a, int b) { if (a == 0) return b; if (b == 0) return a; int shift = __builtin_ctz(a | b); // 公共因子2的个数 a >>= __builtin_ctz(a); // 移除a中的因子2 do { b >>= __builtin_ctz(b); // 移除b中的因子2 if (a > b) std::swap(a, b); b -= a; } while (b != 0); return a << shift; // 恢复公共因子2 } ``` - 优势:用位运算替代取模,提升大数计算效率 - 时间复杂度:$O(\log(\max(a,b)))$ --- ### 相关问题 1. 如何处理超大整数(如1000位)的GCD计算? 2. 为什么先除后乘能避免整数溢出? 3. Stein算法相比辗转相除法有哪些性能优势? 4. 如何扩展算法计算多个数的GCD/LCM? 5. 负数的最小公倍数在数学上如何定义?[^1][^2][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值