两个数的最大公约数(GCD)、最小公倍数(LCM)用上面两个算法实现非常简单。那么如果是同时求多个数的呢?
首先约定两个数的最大公约数的函数为gcd,最小公倍数的函数为lcm。
多个数的最大公约数算法
- 自然而然会想到用逐轮用2个数计算,将上一步的最大公约数与下一个数继续计算,直到计算完所有数。
function multi_gcd_step() {
// 拿到参数,并都转成正数
let arr = Array.from(arguments).map(a => Math.abs(a));
// 参数非法过滤
if (arr.length < 2) {
throw new Error('参数个数不能少于2');
}
if (arr.some(a => !a)) {
throw new Error('参数不能包含0');
}
for (let i = 0; i < arr.length - 1; i++) {
let c = gcd(arr[i], arr[i + 1]); // gcd为两个数的最大公约数算法
if (c === 1) {
// 如果最大公约数是1,则提前结束
return 1;
}
arr[i + 1] = c;
}
return arr[arr.length - 1]; // 返回最后一项
}
- 上面的算法比较麻烦,因为不断调用内部有多次循环的 gcd 算法。其实,根据辗转相除法,我们可以这样做:
- 对这一组数进行排序(从大到小)
- 对每两个相邻的两个数进行如下操作:设相邻的两个数为A和B(A在前,因为已经排序,所以A > B),如果A = n * B(n为整数),也就是A能够被B整除,那么就令A = B;如果A不能被B整除则令A = A % B。
- 重复上面两步,直到数组中每个数都相等,则最大公约数就为这个数。
function multi_gcd_zzxcf() {
// 拿到参数
let arr = Array.from(arguments).map(a => Math.abs(a));
// TODO: 参数非法过滤
while (!arr.every(a => a === arr[0])) {
// 从大到小排序
arr.sort((a, b) => b - a);
// 假设前一个数是A,后一个是B
// 如果A能够被B整除,那么就令A = B;如果A不能被B整除则令A = A % B
for (let i = 0; i < arr.length - 1; i++) {
let c = arr[i] % arr[i + 1];
arr[i] = c || arr[i + 1];
}
}
return arr[0];
}
多个数的最小公倍数算法
我们知道,两个数a, b的最小公倍数算法,是 ab / gcd(a, b),即两者乘积除以它们的最大公约数。那么多个数的也可以这么计算吗?答案是否定的,比如(9, 10, 5),最大公约数为1,最小公倍数 90 ≠ 9 × 10 × 5 ÷ 1。所以不能基于上面的多个数的最大公约数算法。
- 不断计算两个数的最小公倍数,将上一步的最小公倍数和下一个计算,直到遍历完,最终的结果即为所有数的最小公倍数
function multi_lcm_step() {
let arr = Array.from(arguments).map(a => Math.abs(a));
// TODO: 参数非法过滤
for (let i = 0; i < arr.length - 1; i++) {
arr[i + 1] = lcm(arr[i], arr[i + 1]); // lcm 为两个数的最小公倍数算法
}
return arr[arr.length - 1];
}
-
- 计算
m=a1*a2*..*an - 把a1,a2,…,an中的所有项ai用 m/ai 代换
- 找到a1,a2,…,an中的最小非零项aj,若有多个最小非零项则任取一个
- aj以外的所有其他非0项ak用ak % aj代替;若aj以外,其他都变成了0,则转到(6)
- 转到(3)
- 最小公倍数为m/aj
- 计算
function multi_lcm_zzxcf() {
let arr = Array.from(arguments).map(a => Math.abs(a));
// TODO: 参数非法过滤
let m = arr.reduce((a, b) => a * b, 1);
arr = arr.map(a => m / a);
let aj = m;
do {
// 找非0最小项
let ajIndex = -1;
for (let i = 0; i < arr.length; i++) {
if (arr[i] < aj) {
aj = arr[i];
ajIndex = i;
}
}
// 除了非零最小项,其他都求余
for (let i = 0; i < arr.length; i++) {
if (i !== ajIndex) {
arr[i] = arr[i] % aj;
}
}
// 过滤掉0,减少和方便计算
arr = arr.filter(a => !!a);
} while (arr.length);
return m / aj;
}
参考连接:https://www.cnblogs.com/leiyuxiang/articles/3494977.html
本文介绍了如何使用辗转相除法和更相减损术求解多个数的最大公约数和最小公倍数。通过排序和迭代优化,可以高效地找出一组数的最大公约数。而对于最小公倍数,不能直接基于最大公约数计算,而是需要通过不断计算两个数的最小公倍数来得到所有数的最小公倍数。
802

被折叠的 条评论
为什么被折叠?



