用于范围 LCM 查询的 JavaScript 程序

什么是范围 LCM?

在介绍如何查询范围 LCM 之前,先来了解一下什么是范围 LCM。LCM(最小公倍数)是指一个数的倍数中同时也是另一个数的倍数的最小正整数。范围 LCM 就是求多个数字的 LCM,在一定范围内的最小值。

举个例子:在数字 1~5 中找到它们的 LCM。1 的倍数为 {1,2,3,4,5},2 的倍数为 {2,4},3 的倍数为 {3},4 的倍数为 {4},5 的倍数为 {5}。它们的公共倍数为 {2,4},即 LCM(1,2,3,4,5) = 2*2 = 4。

范围 LCM 查询的实现思路

暴力解法

最简单的思路是暴力枚举每一个数字,然后找出在给定范围内的最小 LCM。但是,如果有数的值特别大,这种方法将会非常耗时。因此,这种算法只适用于数字比较小的情况。

function rangeLCM(start, end, arr) {

let lcm = Number.MAX_SAFE_INTEGER;

let found = false;

for (let i = start; i <= end; i++) {

let tempLCM = 1;

for (let j = 0; j < arr.length; j++) {

if (i % arr[j] !== 0) {

break;

}

tempLCM = LCM(tempLCM, arr[j]);

if (j === arr.length - 1) {

found = true;

lcm = Math.min(lcm, tempLCM);

}

}

}

return found ? lcm : -1;

}

优化算法

上述算法的时间复杂度是 O(nmlog(m)),其中 n 是查询范围,m 是数组 arr 的长度。接下来我们介绍两种优化算法,分别是线性筛和倍增。

线性筛

我们可以利用线性筛的方法,利用一个 isPrime 数组记录每个数是否为质数。

function rangeLCM(start, end, arr) {

let lcm = Number.MAX_SAFE_INTEGER;

let found = false;

let isPrime = Array(end + 1).fill(true);

let primes = [];

for (let i = 2; i <= end; i++) {

if (isPrime[i]) {

primes.push(i);

}

for (let j = 0; j < primes.length && i * primes[j] <= end; j++) {

isPrime[i * primes[j]] = false;

if (i % primes[j] === 0) {

break;

}

}

}

for (let i = start; i <= end; i++) {

let tempLCM = 1;

let flag = true;

for (let j = 0; j < primes.length && primes[j] <= i; j++) {

if (i % primes[j] === 0 && arr.indexOf(primes[j]) !== -1) {

if (flag) {

tempLCM = primes[j];

flag = false;

} else {

tempLCM = LCM(tempLCM, primes[j]);

}

}

}

if (!flag && arr.every(num => tempLCM % num === 0)) {

found = true;

lcm = Math.min(lcm, tempLCM);

}

}

return found ? lcm : -1;

}

倍增

我们可以利用倍增的方法,将查询范围拆成若干个区间。在每个区间内单独求 LCM,然后将所有区间的 LCM 两两相乘。

function rangeLCM(start, end, arr) {

let lcm = Number.MAX_SAFE_INTEGER;

let found = false;

const N = end - start + 1;

let f = Array(N + 1).fill(0);

for (let j = 0; j < arr.length; j++) {

for (let i = Math.ceil(start / arr[j]); i * arr[j] <= end; i++) {

let idx = i * arr[j] - start;

let k = idx + 1;

while (k % arr[j] === 0) {

k /= arr[j];

}

if (f[idx]) {

f[idx] = LCM(f[idx], k);

} else {

f[idx] = k;

}

}

}

for (let i = 0; i < N; i++) {

if (f[i] === 0) {

continue;

}

if (found) {

lcm = LCM(lcm, f[i]);

} else {

lcm = f[i];

found = true;

}

}

return found ? lcm : -1;

}

总结

范围 LCM 查询的优化算法主要有线性筛和倍增两种方法。线性筛的时间复杂度为 O(mlog(log(m)) + nlog(m)),其中 n 是查询范围,m 是数组 arr 的长度。倍增的时间复杂度为 O(m + nlog(n/m)),其中 n 是查询范围,m 是计算区间的长度。当 m 很大时,倍增的时间复杂度比线性筛更优。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。