【问题标题】:Greatest Prime Factor最大素数
【发布时间】:2017-05-28 08:33:39
【问题描述】:

我正在尝试填写algorithm challenge to find the largest prime factor of 600851475143。我不一定要求答案。只是想弄清楚为什么这段代码不起作用。为什么它返回“未定义”而不是数字?

let isPrime = n => {
    let div = n - 1;
    while (div > 1) {
        if (n % div == 0) return false;
        div--;
    }
    return true;
};

let primeFactor = x => {
    for (let i = Math.floor(x / 2); i > 1; i--) {
        if (x % i == 0 && isPrime(i) == true) {
            return i;
        }
    }
};

console.log(primeFactor(35)); // 7
console.log(primeFactor(13195)); // 29
console.log(primeFactor(600851475143)); // undefined

【问题讨论】:

  • 你在哪里运行你的代码?
  • 我把它贴在 JSBin 上
  • 它在我的本地 Node.js 副本中运行了 5 分钟,但尚未完成。我认为浏览器实现可能只是触发 此脚本没有响应 对话框。
  • @tyl-er 前几天我在回答一个使用 JSBin 的问题,由于某种原因,他们的脚本收到不一致的答案,我建议您尝试其他服务,除此之外,从 300000000000 到 1 的循环需要很长时间,但应该可以,我建议你改变你的方法
  • @NickA:啊,你是对的,在这种情况下它只有 40 位。

标签: javascript algorithm


【解决方案1】:

问题不是你的算法是完全有效的,检查下面稍微修改的算法,我所做的只是用你可以选择的参数替换你的起点Math.floor(x/2)

let isPrime = n => {
        let div = n - 1;
    while (div > 1) {
        if (n % div == 0) return false;
        div--;
    }
    return true;
};

function primeFactor(x, n){
    for (let i = n; i > 1; i--) {
        if (x % i == 0 && isPrime(i) == true) {
            return i;
        }
    }
}

console.log(primeFactor(35, 35));
console.log(primeFactor(13195, 13195));
console.log(primeFactor(600851475143, 100000))

使用上述方法,您将得到一个证明您的实现有效的答案,但循环太大而无法完成整个事情(即来自Math.floor(600851475143/2))。假设您的计算机每秒可以执行 5 亿次循环,从 300,425,737,571 次到 1 次循环每一次需要 167 小时,即使每秒 50 亿次循环也需要 16 个半小时。您的方法极其效率低下,但返回正确答案。您在 JSBin 上没有得到答案的原因更有可能与浏览器/服务限制有关。


以下更有效解决方案的剧透


以下实现使用素数筛(Sieve of Eratosthenes)来生成任何请求的素数列表,然后检查它们是否完全考虑到给定的数字,只要您使用足够大的素数列表,这将完全按预期工作。应该注意的是,因为它会生成一个大的素数列表,如果运行不正确可能需要一些时间,所以应该生成一个单个素数列表并用于下面的所有调用,以及缓存的素数列表最终将通过减少稍后执行的计算来获得回报:

function genPrimes(n){
  primes = new Uint32Array(n+1);
  primes.fill(1)
  for(var i = 2; i < Math.sqrt(n); i++){
    if(primes[i]){
      for(var j = 2*i; j < n; j+=i){
        primes[j] = 0;
      }
    }
  }
  primeVals = []
  for(var i = 2; i < primes.length; i++){
    if(primes[i]){
      primeVals.push(i);
    }
  }
  return primeVals;
}
    
function primeFactor(x, primes){
  var c = x < primes.length ? x : primes.length
  for (var i = c; i > 1; i--) {
    if(x % primes[i] == 0){
      return primes[i];
    }
  }
}

primes = genPrimes(15487457);
console.log(primeFactor(35, primes));
console.log(primeFactor(13195, primes));
console.log(primeFactor(600851475143, primes));
console.log(primeFactor(30974914,primes));

【讨论】:

  • 谢谢我从那个答案中学到了很多。因此,如果这是一个普通的网络应用程序,并且我们使用的是 13195 等较小的数字,那么效率是否仍然存在明显差异?
  • @arboreal84 "只要你使用足够大的素数列表",然后生成更多素数
  • @tyl-er 拥有如此规模的数字,除非您多次运行此代码,否则我不会担心效率
  • 与其创建一个空数组,不如创建一个大小为nArray,然后填充它。不同之处在于调整数组大小的成本。
  • 然后,您可以使用Uint32Array 代替Array,这比Array 快得多。而不是使用 for 循环初始化数组,而是使用Uint32Array.prototype.fill。如果你还想让它浮动,你可以使用Float64Array
【解决方案2】:

let primeFactor = x => {
    if (x === 1 || x === 2) {
        return x;
    }

    while (x % 2 === 0) {
        x /= 2;
    }
    
    if (x === 1) {
        return 2;
    }

    let max = 0;
    for (let i = 3; i <= Math.sqrt(x); i += 2) {
        while (x % i === 0) {
            x /= i;
            max = Math.max(i, max);
        }
    }

    if (x > 2) {
        max = Math.max(x, max);
    }
    
    return max;
};

console.log(primeFactor(35));
console.log(primeFactor(13195));
console.log(primeFactor(27));
console.log(primeFactor(1024));
console.log(primeFactor(30974914));
console.log(primeFactor(600851475143));

优化

  • 将数字除以 2 直到奇数,因为没有偶数是素数。

  • 迭代增量为2 而不是1 以跳过所有偶数。

  • 迭代在sqrt(x) 处停止。对此的解释是here

【讨论】:

  • @arboreal84:正如尼克指出的那样,答案没有回答,正如詹姆斯指出的那样,算法不正确。因此,这只会留下一个实际上并没有尝试回答的错误答案。为什么投反对票是错误的?
  • @arboreal84 我很欣赏这个答案。我有点想自己解决这个问题,但这没什么大不了的。
  • 仅作记录,感谢@James 的反馈,算法已得到纠正。
猜你喜欢
  • 2014-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-25
  • 1970-01-01
  • 2013-10-05
  • 2016-03-25
  • 2019-01-05
相关资源
最近更新 更多