Я пытаюсь выполнить задачу алгоритма, чтобы найти наибольший простой множитель из 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
3
tyl-er 28 Май 2017 в 11:33

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). Это можно объяснить здесь.

2
arboreal84 28 Май 2017 в 09:33

Проблема не в вашем алгоритме, он совершенно корректен, проверьте приведенный ниже слегка измененный алгоритм, все, что я сделал, заменил вашу начальную точку 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)). Допустим, ваш компьютер может выполнять 500 миллионов циклов в секунду, и каждый из циклов от 300 425 737 571 до 1 займет 167 часов, даже при 5 миллиардах циклов в секунду это займет 16 с половиной часов. Ваш метод крайне неэффективен, но вернет правильный ответ. Причина, по которой вы не получаете ответ на JSBin, скорее всего связана с ограничениями браузера / службы.


Спойлеры о более эффективном решении ниже


В следующей реализации используется простое сито (Сито из эратосфена) для создания любого списка простых чисел запрашивает, а затем проверяет, полностью ли они учитывают данное число, при условии, что вы используете достаточно большой список простых чисел, это будет работать точно так, как задумано. следует отметить, что, поскольку он генерирует большой список простых чисел, он может занять некоторое время, если он выполняется неправильно, следует создать список single простых чисел и использовать его для всех вызовов ниже, а также кэшированный список простых чисел в конечном итоге окупится, выполнив меньше вычислений позже:

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));
3
Nick 28 Май 2017 в 10:38