Cách hiệu quả nhất để tính giai thừa modulo là số nguyên tố là gì?


20

Bạn có biết thuật toán nào tính toán giai thừa sau khi mô đun hiệu quả không?

Ví dụ, tôi muốn lập trình:

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

Nhưng, plà một con số lớn (nguyên tố) để áp dụng thừa trực tiếp (p108) .

Trong Python, nhiệm vụ này thực sự dễ dàng, nhưng tôi thực sự muốn biết cách tối ưu hóa.


6
Có vẻ như vấn đề muốn bạn sử dụng định lý của Wilson. Đối với nguyên tố p , (p1)!=1modp . Vì vậy, không sử dụng bất kỳ ngôn ngữ lập trình: câu trả lời là100 . Có lẽ bạn muốn khái quát vấn đề của bạn?
Aryabhata

5
Bạn có thể nói rõ vấn đề hơn không? Bạn có muốn tính toán (X!) (mod (X+1)), hoặc tổng quát hơn (X!) (mod Y)? Và tôi cho rằng điều factorial(100!)đó không thực sự có nghĩa là bạn muốn áp dụng chức năng giai thừa hai lần.
Keith Thompson

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

8
Lưu ý rằng Định lý Wilson chỉ áp dụng khi là số nguyên tố. Câu hỏi của bạn không nói rằng là số nguyên tố, vì vậy những gì bạn viết không đúng. ppp
Dave Clarke

Câu trả lời:


11

(Câu trả lời này ban đầu được đăng bởi người hỏi jonaprieto bên trong câu hỏi.)

Tôi nhớ định lý của Wilson và tôi nhận thấy những điều nhỏ nhặt:

Trong chương trình trên, sẽ tốt hơn nếu tôi viết:

(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)

Và bạn có thể tìm thấy vì , vì vậy với thuật toán Euclidian mở rộng, bạn có thể tìm thấy giá trị của , đó là mô đun nghịch đảo. gcd ( p , p - i ) = 1(pi)1gcd(p,pi)=1(pi)1

Bạn cũng có thể xem các đồng loại tương tự, như: vì vậy, tổng bằng: và nếu bạn tính hệ số ban đầu, các yếu tố bạn nhận được Và, voila, mô đun nghịch đảo hiệu quả hơn các yếu tố.

(p5)!(p24)1(modp)(p4)!(p+6)1(modp)(p3)!(p2)1(modp)(p2)!1(modp)(p1)!1(modp)
(24)1+(6)1+(2)1
8(24)1(modp)

Vì vậy, về cơ bản . Khéo léo! (pk)!(p+(k1)!(1)k)1(modp)
Thomas Ahle

Xin lỗi nhưng khi tôi tính hệ số , tôi nhận được:(24)1+61+(2)1
9(24)1=38

1

Ví dụ mà bạn đang đăng có liên quan rất chặt chẽ đến vấn đề Euler # 381. Vì vậy, tôi sẽ đăng một câu trả lời không giải quyết được vấn đề Euler. Tôi sẽ đăng làm thế nào bạn có thể tính giai thừa modulo một số nguyên tố.

Vậy: Cách tính n! modulo p?

Quan sát nhanh: Nếu n ≥ p, thì n! có hệ số p, do đó kết quả là 0. Rất nhanh. Và nếu chúng ta bỏ qua yêu cầu rằng p phải là một số nguyên tố thì hãy để q là thừa số nguyên tố nhỏ nhất của p và n! modulo p bằng 0 nếu n ≥ q. Cũng không có nhiều lý do để yêu cầu p là số nguyên tố để trả lời câu hỏi của bạn.

Bây giờ trong ví dụ của bạn (n - i)! cho 1 ≤ i ≤ 5 đi lên. Bạn không phải tính năm yếu tố: Bạn tính (n - 5)!, Nhân với (n - 4) đi lấy (n - 4)!, Nhân với (n - 3) để lấy (n - 3)! vv Điều này làm giảm công việc gần như một yếu tố 5. Đừng giải quyết vấn đề theo nghĩa đen.

Câu hỏi là làm thế nào để tính n! modulo m. Cách rõ ràng là tính n!, Một số có khoảng n log n chữ số thập phân và tính modulo còn lại p. Đó là công việc khó khăn. Câu hỏi: Làm thế nào chúng ta có thể nhận được kết quả này nhanh hơn? Bằng cách không làm điều rõ ràng.

Chúng ta biết rằng ((a * b * c) modulo p = (((a * b) modulo p) * c) modulo p.

Để tính n!, Chúng ta thường bắt đầu bằng x = 1, sau đó nhân x với 1, 2, 3, ... n. Sử dụng công thức modulo, chúng tôi tính n! modulo p mà không tính n!, bằng cách bắt đầu với x = 1, và sau đó với i = 1, 2, 3, .., n chúng ta thay x bằng (x * i) modulo p.

Chúng tôi luôn có x <p và i <n, vì vậy chúng tôi chỉ cần đủ độ chính xác để tính x * p, chứ không phải độ chính xác cao hơn nhiều để tính n!. Vậy để tính n! modulo p cho p 2 chúng tôi thực hiện các bước sau:

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. 

.


-1

Đây là cách sử dụng của tôi đối với định lý của wilson:

Hàm factMOD là hàm để gọi để tính (n!)% MOD khi MOD-n nhỏ so với n.

Có ai biết một cách tiếp cận hiệu quả khác khi không phải như vậy (ví dụ: n = 1e6 và 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
Mã không thực sự về chủ đề, ở đây. Một mô tả về thuật toán hữu ích hơn nhiều vì nó không yêu cầu mọi người hiểu bất kỳ ngôn ngữ nào bạn quyết định viết mã của mình và vì các triển khai thực tế thường được tối ưu hóa theo cách khiến chúng khó hiểu hơn. Và hãy đặt câu hỏi của bạn dưới dạng câu hỏi riêng biệt, thay vì trong câu trả lời của bạn. Stack Exchange là một trang web câu hỏi và câu trả lời, không phải là một bảng thảo luận và rất khó tìm ra câu hỏi nếu chúng bị ẩn không phải là câu trả lời. Cảm ơn!
David Richerby
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.