该解决方案不适用于1到20之间的值

时间:2018-02-19 00:28:09

标签: javascript algorithm

我试图解决Euler's fifth problem.

  

2520是可以除以1至10中的每个数字而没有任何余数的最小数字。   可以被1到20的所有数字整除的最小正数是多少?

适用于样本编号2520。

但不是1到20之间的数字,它不会给我任何回报,所以我的错误是什么?



function findFactor(n, max) {
  var acc = 0;
  for(var i = 2; i <= max; ++i) {
    acc += !(n%i) ? 1 : 0;
  }
  return acc === max - 1 ? true : false;
}


for(var i = 2520; true; ++i) {
  if(findFactor(i, 20)) {
    console.log(i)
  }
  if(i > 1e7) console.log("Limit")
}
&#13;
&#13;
&#13;

4 个答案:

答案 0 :(得分:2)

您的代码存在一些缺陷:

  • 您永远不会退出循环for (var i = 2520; true; ++i)。即使匹配,浏览器也会冻结并且不会记录任何内容。

  • 您只需将i增加1,这是多余的。增加20,因为你的答案必须能被20整除。

  • acc += !(n%i) ? 1 : 0;也是多余的。如果n % i !== 0,您不需要进一步迭代,只需返回false。

考虑到所有这些更正,您可能会遇到以下情况:

&#13;
&#13;
function findFactor(n, max) {
    for (let i = 2; i <= max; i++) {
        if (n % i !== 0) return false;
    }
    return true;
}

let n = 20;

//measuring performance
let start = performance.now();

for (let i = n; true; i += n) {
    if (findFactor(i, n)) {
        console.log(i);
        break;
    } else if (i > 1e10) {
        console.log("Limit");
        break;
    }
}

console.log(`time spent: ${performance.now() - start}`);
&#13;
&#13;
&#13;

还有另一种方法可以计算两个以上数字的最小公倍数(LCM) - 通过迭代计算两个数字的最小公倍数:

lcm(a, b, c) = lcm(a, lcm(b, c))

两个数字can be computed as follows的最小公倍数:

lcm(a, b) = a * b / gcd(a, b)

其中gcd(a, b)是最大的公约数。要找到它,您可以使用Euclidean algorithm

&#13;
&#13;
//greatest common divisor
const gcd = (a,b) => {
    while (a !== 0 && b !== 0) {
        if (a > b) a %= b;
        else b %= a;
    }
    return a + b;
};

//least common multiple
const lcm = (a, b) => a * b / gcd(a, b);

const leastMultipleOfRange = n => {
    if (n < 3) return n;
    let acc = 2;
    for (let i = 3; i <= n ; i++) {
        acc = lcm(acc, i);
    }
    return acc;
};

let start = performance.now();

console.log(leastMultipleOfRange(20));

console.log(`time spent: ${performance.now() - start}`);
&#13;
&#13;
&#13;

最有可能的是,有一些更有效的方法来计算几个数字的最小公倍数,例如保罗所提到的,但我对数学的了解并不是很深刻来解释它们。

答案 1 :(得分:1)

解决此问题的更有效方法是计算least common multiple(lcm)。我们可以使用的基本思想类似于通过factorization计算lcm(虽然我们不直接使用分解)。

一些基础知识

我们可以表示ab平均分为a | b,而不是a∤b。如果两个数字没有共同因素,则它们是互质的;这也需要lcm(a, b) = a * b,如果ab是互质的。 m = lcm(a, b)具有属性a | m和b | m,并且不存在m_<m,使得| m_和b | m_。由于对于每个整数,存在唯一的因子分解(如fundamental theorem of arithmetic中所述),我们可以将abm表示为素数的乘积:
factorization of a
factorization of b
factorization of m

m的因式分解表明m中没有多余的因素。它与ab一样可以被整除。

多个数字的lcm可以递归地从两个数字的lcm计算:

lcm(a, b, c) = lcm(lcm(a, b), c)

这些是有效解决问题所需的基本数学工具。现在我们留下了两个问题:哪个素数在我们lcm的因子分解中具有幂> 0,哪些值具有相应的指数?

查找素数组

我们可以使用以下事实确定lcm([1..n])的因式分析中的哪些素数:让pεP和p <= n,然后p显然在序列中,所以它也必须是最不常见的倍数。现在p > n怎么样?让我们从两个值的lcm开始:ab,其中a < pb < p。由此我们可以得出结论p∤a和p∤b,所以p | lcm(a,b)也不能保持。一般来说,如果p∤a和p∤b,那么p∤lcm(a,b)必须成立。证明:

Assume m = lcm(a, b) and p | m
m = a * n1 = b * n2

但是因为p∤a和p∤b我们也得到了

m = a * p * n1_ = b * p * n2_
n1_ * p = n1
n2_ * p = n2

因此我们可以使用以下属性构造m_

m_ * p = m
a|m_
b|m_

因此,大于ab的素数永远不会是lcm(a, b)的因子。由于lcm超过两个整数的递归定义,我们可以很容易地证明,这需要n不能成为lcm([1..n])因子的任何素数。

因此,我们的因子分解将包含的素数都在[1..n]范围内。例如。对于n=20,就像项目euler中的问题一样,这将是素数[2, 3, 5, 7, 11, 13, 17, 19]

指数

仍然是要解决的最后一个问题:分解中每个素数的指数。在第一步中,我们可以查看单个数字的权力:

lcm(x^e1,x^e2) = x^e2, if e1 < e2

因此,例如在我们的问题中,2的指数必须是4:
[1..20]范围内2的最大幂为16 = 2 ^ 4。任何较小的2的幂除以16.因此对于给定的n,我们可以将指数计算为
calculation of the exponent

所以现在我们有一部分序列的lcm(素数的所有幂):

lcm(2,4,8,16, 3,9, 5, 7, 11, 13, 17, 19) = 
lcm(lcm(2, 4, 8, 16), lcm(3, 9), 5, 7, 11, 13, 17, 19) =
lcm(16, 9, 5, 7, 11, 13, 17, 19) =  
2^4 * 3^2 * 5^1 * 7^1 * ... * 19^1

上述等式的最后几行是由于素数和它们的能力总是彼此相互作用(见上文)。

但序列中的剩余数字呢?我们实际上并不需要它们。每个不是素数本身的数字都是素数幂的乘积(unique factorization)。所以我们假设c = p1^e1 * p2^e2以及a = p1^f1b = p2^f2,其中abc的范围为{{1} }}和[1..n]f1是最大的。然后f2e1 <= f1必须保留,否则e2 <= f2无法保持(请记住c <= nf1已经是相应的最大指数素数,例如f2)。因此p1^(f1 + 1) > n c | lcm(a, b) abc如上所定义,可以基于lcm(a, b) a的因子分解得出,{ {1}}(见上文)。

实际实施

嗯,这是理论部分的数字,一些实际代码的时间(以防万一你仍然读到这个:D)。至少我们现在有一些非常漂亮的代码:

b
 
run = function(){
    document.getElementById('output_id').innerHTML = 'Calculating...'
    var n = document.getElementById('input_n_id').value;

    // sieve of eratosthenes, the quick and dirty way
    var primes = Array((n - 1) >> 1).fill(0).map((v, i) => i * 2 + 3).reduce((p, v) => {!~p.findIndex(p_ => !(v % p_)) && p.push(v); return p;}, [2]);

    // actually calculating n
    var sol = primes.map(p => Math.pow(p, Math.floor(Math.log(n) / Math.log(p))))
                    .reduce((a, b) => a * b, 1);

    // output
    document.getElementById('output_id').innerHTML = 'Solution: ' + sol;
}

所以现在只有一个问题需要回答:所有数学的重点是什么?答案:速度(和美丽;))。使用此代码,您可以计算最大<input maxlength="512" id="input_n_id" placeholder="Enter a value here"/> <button onclick="run()">Start</button> <p id="output_id">Waiting for input...</p> 的任何数字范围的最小公倍数(实际上您可以更进一步,但从709开始,解决方案超出了javascripts浮点数的范围)。

答案 2 :(得分:0)

您将max设置为20,然后您的循环依赖于2和max(20)之间的所有数字,即n的因子。如果n <20,则该方法将不起作用,因为显然大于n的数字不能是n的因子。如果n

答案 3 :(得分:0)

这是关于素数的。想想哪些素数使得所有数字都在1到20之间,记住计算你需要的每个素数的最小数量,并将它们相乘以得到解。例如,对于9,我们需要两个3,对于16,我们需要4个2,等等。