วิธีที่มีประสิทธิภาพมากที่สุดในการคำนวณแฟคทอเรียลโมดูโลคืออะไร?


20

คุณรู้จักอัลกอริธึมที่คำนวณแฟคทอเรียลหลังจากโมดูลัสอย่างมีประสิทธิภาพหรือไม่?

ตัวอย่างเช่นฉันต้องการโปรแกรม:

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

แต่pเป็นจำนวนมาก (นายก) สำหรับการใช้ปัจจัยโดยตรง8)(p108)

ใน Python งานนี้ง่ายมาก แต่ฉันอยากรู้วิธีเพิ่มประสิทธิภาพ


6
ดูเหมือนว่าปัญหาต้องการให้คุณใช้ทฤษฎีบทของวิลสัน สำหรับนายก , (P-1)! = -1 \ mod P ดังนั้นโดยไม่ต้องใช้ภาษาการเขียนโปรแกรมใด ๆ : คำตอบคือ100 บางทีคุณต้องการจะพูดคุยปัญหาของคุณ? p(p1)!=1modp100
Aryabhata

5
คุณสามารถระบุปัญหาให้ชัดเจนยิ่งขึ้นได้หรือไม่? คุณต้องการที่จะคำนวณ(X!) (mod (X+1))หรือทั่วไปมากขึ้น(X!) (mod Y)? และฉันคิดว่านั่นfactorial(100!)ไม่ได้หมายความว่าคุณต้องการใช้ฟังก์ชันแฟคทอเรียลสองครั้ง
Keith Thompson

1
แม้ว่าคุณจะไม่มีทฤษฎีบทของวิลสันคุณก็มี(mn)modp=(mmodp)(nmodp)ซึ่งอย่างน้อยก็จะช่วยหลีกเลี่ยงปัญหาล้น
Dave Clarke

8
โปรดทราบว่าทฤษฎีบทของวิลสันใช้เฉพาะเมื่อpเป็นนายก คำถามของคุณไม่ได้ระบุว่าpนั้นสำคัญดังนั้นสิ่งที่คุณเขียนไม่ถูกต้อง
Dave Clarke

คำตอบ:


11

(คำตอบนี้ถูกโพสต์ครั้งแรกโดยผู้ถาม jonaprietoภายในคำถาม)

ฉันจำทฤษฎีบทของ Wilsonและฉันสังเกตเห็นสิ่งเล็กน้อย:

ในโปรแกรมข้างต้นจะดีกว่าถ้าฉันเขียน:

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

และคุณสามารถหาเพราะดังนั้นด้วยอัลกอริธึมแบบขยาย Euclidianคุณสามารถหาค่าของนั่นคือ โมดูลัสของผกผัน gcd ( p , p - i ) = 1 ( p - i ) - 1(pi)1gcd(p,pi)=1(pi)1

คุณสามารถดูความสอดคล้องกันเช่นกันเช่น: ดังนั้นผลรวมเท่ากับ: และถ้าคุณแยกตัวประกอบในการเริ่มต้นแฟกทอเรียลคุณจะได้ และ, voila, โมดูลัสของผกผันมีประสิทธิภาพมากกว่าแฟคทอเรียล (-24)-1+(6)-1+(-2)-18(-24)-1

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

ดังนั้นโดยทั่วไปพี เรียบร้อย! (pk)!(p+(k1)!(1)k)1(modp)
โทมัส Ahle

ขออภัย แต่เมื่อฉันแยกตัวประกอบฉันได้รับ: 9 ( - 24 ) - 1 = - 3(24)1+61+(2)1
9(24)1=38

1

ตัวอย่างที่คุณกำลังโพสต์นั้นเกี่ยวข้องกับปัญหาออยเลอร์อย่างมาก # 381 ดังนั้นฉันจะโพสต์คำตอบที่ไม่แก้ปัญหาออยเลอร์ ฉันจะโพสต์วิธีที่คุณสามารถคำนวณแฟคทอเรียลแบบโมดูโล

ดังนั้น: วิธีคำนวณ n! modulo p

การสังเกตอย่างรวดเร็ว: ถ้า n ≥ p ดังนั้น n! มีตัวประกอบ p ดังนั้นผลลัพธ์คือ 0 เร็วมาก และถ้าเราเพิกเฉยต่อข้อกำหนดที่ว่า p ควรเป็นไพร์มแล้วให้ q เป็นตัวประกอบที่เล็กที่สุดของ p และ n! modulo p คือ 0 ถ้า n ≥ q นอกจากนี้ยังมีเหตุผลไม่มากที่ต้องการให้ p เป็นตัวเลือกสำคัญในการตอบคำถามของคุณ

ในตัวอย่างของคุณ (n - i)! สำหรับ 1 ≤ i ≤ 5 ขึ้นมา คุณไม่ต้องคำนวณห้าแฟคทอเรียล: คุณคำนวณ (n - 5)! คูณด้วย (n - 4) ไปรับ (n - 4)! คูณด้วย (n - 3) เพื่อรับ (n - 3)! ฯลฯ ซึ่งจะช่วยลดการทำงานเกือบเป็นปัจจัย 5 อย่าแก้ปัญหาอย่างแท้จริง

คำถามคือวิธีการคำนวณ n! โมดูโลเอ็ม วิธีที่ชัดเจนคือการคำนวณ n !, ตัวเลขที่มีตัวเลขทศนิยม n log n โดยประมาณและคำนวณโมดูโลส่วนที่เหลือ นั่นเป็นงานหนัก คำถาม: เราจะได้ผลลัพธ์นี้เร็วขึ้นได้อย่างไร โดยไม่ทำสิ่งที่ชัดเจน

เรารู้ว่า ((a * b * c) modulo p = (((* * *) modulo p) * c) modulo p

ในการคำนวณ n! เรามักจะเริ่มต้นด้วย x = 1 แล้วคูณ x ด้วย 1, 2, 3, ... n การใช้สูตรโมดูโล่เราคำนวณ n! modulo p โดยไม่คำนวณ n!, โดยเริ่มต้นด้วย x = 1, และสำหรับ i = 1, 2, 3, .. , n เราแทนที่ x ด้วย (x * i) modulo p

เรามักจะมี x <p และ i <n ดังนั้นเราจึงต้องการเพียงความแม่นยำเพียงพอในการคำนวณ x * p ไม่ใช่ความแม่นยำที่สูงกว่าในการคำนวณ n! ดังนั้นเพื่อคำนวณ n! modulo 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. 

(บางคำตอบพูดถึงทฤษฎีบทของ Wilson ซึ่งตอบเฉพาะคำถามในกรณีพิเศษของตัวอย่างที่กำหนดและมีประโยชน์มากในการแก้ปัญหาออยเลอร์ # 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
รหัสไม่ได้อยู่ในหัวข้อจริงๆที่นี่ คำอธิบายของอัลกอริทึมนั้นมีประโยชน์มากกว่าเพราะไม่ต้องการให้ผู้คนเข้าใจภาษาใดก็ตามที่คุณตัดสินใจที่จะเขียนโค้ดและเนื่องจากการใช้งานจริงมักจะได้รับการปรับให้เหมาะสมที่สุดในวิธีที่ทำให้พวกเขาเข้าใจยากขึ้น และโปรดถามคำถามของคุณเป็นคำถามแยกต่างหากแทนที่จะตอบคำถามของคุณ การแลกเปลี่ยนสแต็คเป็นเว็บไซต์คำถามและคำตอบไม่ใช่กระดานสนทนาและคำถามยากที่จะหาหากพวกเขาซ่อนอยู่ในคำตอบ ขอบคุณ!
David Richerby
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.