В чем ошибка в реализации теста Ферма нахождения простого числа?

function isPrime(num) {
  
    if(num <= 1) return false;
    
    for(let i = 0; i < 100; i++){
      
      let a = Math.floor(Math.random()*(num-1) + 1);
      if(gcd(a, num) !== 1) return false;
      
      if((a ** (num - 1)) % num !== 1 ) return false; 
    
    }
    
    return true;
  }
  
  function gcd(x, y){
    
    if(y == 0) return x;
    else return gcd(y, x % y);
  }

Если входное число, например, 73, то возвращает false. Основание степени А генерит корректно. Судя по всему, проблема в выражении в 10 строке. Подскажите, пожалуйста, где брешь.


Ответы (1 шт):

Автор решения: Stanislav Volodarskiy

Возведение в степень выполняется неточно на вещественных числах. Вам нужно написать возведение в степень по модулю, не опираясь на стандартную операцию **.

Например, вычислим 3 ** (73 - 1). Это число требует 115 бит. Числа в JavaScript вещественные с мантиссой в 53 бита. Следовательно 62 младших бита будут отброшены. Получившееся округлённое число бесполезно в тесте Ферма.

Вам нужно возводить числа в степень по модулю точно: Возведение в степень по модулю.

В коде ниже найдите функцию pow, которая возводит число в степень быстро и без переполнений.

const gcd = (a, b) => {
    return (b == 0) ? a : gcd(b, a % b);
}

// a^b mod m
const pow = (a, b, m) => {
    if (b == 0) {
        return 1;
    }
    if (b % 2 == 0) {
        return pow(a * a % m, b / 2, m);
    }
    return a * pow(a, b - 1, m) % m;
};
    
const isPrimeFermat = n => {
    if (n <= 1) {
        return false;
    }

    for (let i = 0; i < 100; ++i) {
        const a = Math.floor(Math.random() * (n - 1) + 1);
        if (gcd(a, n) !== 1) {
            return false;
        }

        if (pow(a, n - 1, n) !== 1) {
            return false;
        }
    }

    return true;
};
→ Ссылка