Який найефективніший спосіб обчислення фабрики фабрики простим?


20

Чи знаєте ви який-небудь алгоритм, який ефективно розраховує факторіал за модулем?

Наприклад, я хочу програмувати:

for(i=0; i<5; i++)
  sum += factorial(p-i) % p;

Але, pє велика кількість (простим) для прямого застосування факторіалу (p108) .

У Python це завдання дійсно просте, але мені дуже хочеться знати, як оптимізувати.


6
Здається, що проблема хоче, щоб ви використали теорему Вілсона. Для простих p , (p1)!=1modp . Отже, не використовуючи жодної мови програмування: відповідь -100 . Можливо, ви хотіли б узагальнити свою проблему?
Ар'ябхата

5
Чи можете ви чіткіше заявити про проблему? Ви хочете обчислити (X!) (mod (X+1))чи більш загальне (X!) (mod Y)? І я припускаю, що factorial(100!)насправді це не означає, що ви хочете двічі застосувати функціональну функцію.
Кіт Томпсон

1
(mn)modp=(mmodp)(nmodp)

8
Зауважте, що теорема Вілсона застосовується лише тоді, коли є простим. У вашому запитанні не зазначено, щоpвказано, p є простим, тому те, що ви написали, не є правильним. p
Дейв Кларк

Відповіді:


11

(Цю відповідь спочатку поставив запитувач jonaprieto всередині питання.)

Я пам’ятаю теорему Вілсона , і я помітив дрібниці:

У наведеній програмі краще, якщо я напишу:

(p1)!1(modp)(p2)!(p1)!(p1)11(modp)(p3)!(p2)!(p2)1(p2)1(modp)(p4)!(p3)!(p3)1(p2)1(p3)1(modp) (p5)!(p4)!(p4)1(p2)1(p3)1(p4)1(modp)

І ви можете знайти оскільки gcd ( p , p - i ) = 1 , тому за розширеним евклідовим алгоритмом ви можете знайти значення ( p - i ) - 1(pi)1gcd(p,pi)=1(pi)1 , тобто зворотний модуль.

Ви також можете переглядати ті самі конгруенції, як:

(p-5)!(p-24)-1(модp)(p-4)!(p+6)-1(модp)(p-3)!(p-2)-1(модp)(p-2)!1(модp)(p-1)!-1(модp)
(-24)-1+(6)-1+(-2)-1
8(-24)-1(модp)

Так в основному (p-к)!(p+(к-1)!(-1)к)-1(модp). Акуратно!
Томас Ейл

Вибачте, але коли я факторно (-24)-1+6-1+(-2)-1, Я отримав :
9(-24)-1=-38

1

Приклад, який ви публікуєте, дуже тісно пов'язаний з проблемою Ейлера № 381. Тож я опублікую відповідь, яка не вирішує проблему Ейлера. Я розміщу повідомлення про те, як ви можете обчислити фабрику по модулю просте.

Отже: Як обчислити n! модуль p?

Швидке спостереження: Якщо n ≥ p, то n! має коефіцієнт р, тому результат дорівнює 0. Дуже швидко. І якщо ми ігноруємо вимогу, що р має бути простим, нехай q є найменшим простим фактором p, і n! модуль p дорівнює 0, якщо n ≥ q. Існує також не так багато причин вимагати, щоб р - це прем'єр-мінімум, щоб відповісти на ваше запитання.

Тепер у вашому прикладі (n - i)! для 1 ≤ i ≤ 5 підійшов. Вам не доведеться обчислювати п’ять факторіалів: Ви обчислюєте (n - 5) !, множите на (n - 4) go get (n - 4) !, множте на (n - 3), щоб отримати (n - 3)! і т.д. Це зменшує роботу майже в 5 разів. Не вирішуйте проблему буквально.

Питання в тому, як обчислити n! модуль м. Очевидний спосіб - обчислити n !, число з приблизно n log n десятковими цифрами та обчислити решту модуля p. Це важка робота. Питання: Як можна швидше отримати цей результат? Не роблячи очевидної речі.

Ми знаємо, що ((a * b * c) modulo p = (((a * b) modulo p) * c) модуль p.

Для обчислення n !, ми зазвичай починаємо з x = 1, а потім множимо x на 1, 2, 3, ... n. Використовуючи формулу модуля, обчислюємо n! модуль p без обчислення n !, починаючи з x = 1, а потім для i = 1, 2, 3, .., n замінюємо x на (x * i) модулем p.

У нас завжди є x <p і i <n, тому для обчислення х * р нам потрібна лише достатня точність, а не значна більша точність для обчислення n !. Отже, обчислити n! по модулю p для p ≥ 2 робимо наступні кроки:

Step 1: Find the smallest prime factor q of p. If n ≥ q then the result is 0.
Step 2: Let x = 1, then for 1 ≤ i ≤ n replace x with (x * i) modulo p, and x is the result. 

(Деякі відповіді згадують теорему Вілсона, яка відповідає лише на питання в особливому випадку з наведеного прикладу, і дуже корисна для вирішення проблеми Ейлера № 381, але взагалі не корисно вирішувати задане питання).


-1

Це моє використання теореми Вілсона:

Функція factMOD - це виклик для обчислення (n!)% MOD, коли MOD-n мало проти n.

Хтось знає інший ефективний підхід, коли це не так (наприклад: n = 1e6 та MOD = 1e9 + 7)?

ll powmod(ll a, ll b){//a^b % MOD
  ll x=1,y=a;
  while(b){
    if(b&1){
      x*=y; if(x>=MOD)x%=MOD;
    }
    y*=y; if(y>=MOD)y%=MOD;
    b>>=1;
  }
  return x;
} 
ll InverseEuler(ll n){//modular inverse of n
  return powmod(n,MOD-2);
}
ll factMOD(ll n){ //n! % MOD efficient when MOD-n<n
   ll res=1,i;
   for(i=1; i<MOD-n; i++){
     res*=i;
     if(res>=MOD)res%=MOD;
   }
   res=InverseEuler(res);   
    if(!(n&1))
      res= -res +MOD;
  }
  return res%MOD;
}

1
Код тут насправді не тематичний. Опис алгоритму набагато корисніше, оскільки він не вимагає від людей, щоб вони розуміли, на якій мові ви вирішили написати свій код, і тому, що фактичні реалізації часто оптимізуються таким чином, що ускладнює їх розуміння. І будь-ласка, задавайте свої питання окремо, а не як відповідь. Обмін стеками - це сайт із запитаннями та відповідями, а не дошка для обговорень, і питання важко знайти, чи ховаються вони під відповідями. Спасибі!
Девід Річербі,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.