Miller-Rabin Strong Pseudoprimes


16

ให้เป็นจำนวนเต็มไม่เป็นลบN, ส่งออกจำนวนเต็มบวกคี่ที่เล็กที่สุดที่เป็น pseudoprime ที่แข็งแกร่งไปยังNฐานที่สำคัญทั้งหมดแรก

นี่คือ OEIS ลำดับA014233

กรณีทดสอบ (หนึ่งดัชนี)

1       2047
2       1373653
3       25326001
4       3215031751
5       2152302898747
6       3474749660383
7       341550071728321
8       341550071728321
9       3825123056546413051
10      3825123056546413051
11      3825123056546413051
12      318665857834031151167461
13      3317044064679887385961981

กรณีทดสอบสำหรับN > 13ยังไม่พร้อมใช้งานเนื่องจากยังไม่พบค่าเหล่านั้น หากคุณจัดการเพื่อค้นหาคำถัดไปในลำดับให้แน่ใจว่าได้ส่งมัน / พวกเขาเพื่อ OEIS!

กฎระเบียบ

  • คุณอาจเลือกที่จะใช้Nเป็นศูนย์ดัชนีหรือค่าดัชนีหนึ่ง
  • เป็นที่ยอมรับได้สำหรับโซลูชันของคุณที่จะทำงานเฉพาะกับค่าที่สามารถแสดงได้ภายในช่วงจำนวนเต็มของภาษาของคุณ (จนถึงN = 12จำนวนเต็ม 64 บิตที่ไม่ได้ลงนาม) แต่โซลูชันของคุณต้องทำงานตามทฤษฎีในการป้อนข้อมูลใด ๆ

พื้นหลัง

เลขจำนวนเต็มบวกใด ๆxสามารถเขียนในรูปแบบx = d*2^sที่dเป็นเลขคี่ dและsสามารถคำนวณได้โดยการหารซ้ำn2 ด้วยซ้ำจนกระทั่งหารไม่หารด้วย 2 อีกต่อไปdคือผลหารสุดท้ายและsคือจำนวนครั้งที่ 2 nแบ่ง

ถ้าเป็นจำนวนเต็มบวกnเป็นสำคัญแล้วทฤษฎีบทเล็ก ๆ น้อย ๆ ของแฟร์มาต์ฯ :

แฟร์มาต์

ในเขตข้อมูล จำกัด ใด ๆZ/pZ(ซึ่งpมีบางช่วงที่สำคัญ) รากที่สองของ1คือ1และ-1(หรือ, เท่ากัน, 1และp-1 )

เราสามารถใช้ข้อเท็จจริงทั้งสามนี้เพื่อพิสูจน์ว่าหนึ่งในสองข้อความต่อไปนี้จะต้องเป็นจริงสำหรับนายกn(ที่d*2^s = n-1และrเป็นจำนวนเต็มบางส่วน[0, s)):

เงื่อนไข Miller-Rabin

การทดสอบแบบดั้งเดิมของ Miller-Rabinดำเนินการโดยการทดสอบข้อ จำกัด ของการอ้างสิทธิ์ข้างต้น: หากมีฐานaซึ่งเงื่อนไขทั้งสองข้างต้นเป็นเท็จนั้นnจะไม่เป็นผล ฐานaนั้นเรียกว่าพยานพยาน

ตอนนี้การทดสอบฐานทุกคนในจะมีราคาแพงในเวลาในการคำนวณขนาดใหญ่[1, n) nมีตัวแปรที่น่าจะเป็นของการทดสอบ Miller-Rabin ที่จะทดสอบเฉพาะบางฐานที่สุ่มเลือกในสนาม จำกัด อย่างไรก็ตามมันถูกค้นพบว่าการทดสอบที่สำคัญเท่านั้นaฐานนั้นเพียงพอและทำให้สามารถทำการทดสอบในลักษณะที่มีประสิทธิภาพและกำหนดขึ้นได้ ในความเป็นจริงไม่จำเป็นต้องทำการทดสอบเฉพาะบางฐาน - ต้องมีจำนวนที่แน่นอนเท่านั้นและจำนวนนั้นจะขึ้นอยู่กับขนาดของค่าที่ถูกทดสอบสำหรับความเป็นอันดับหนึ่ง

หากมีการทดสอบจำนวนเฉพาะของฐานหลักไม่เพียงพอการทดสอบสามารถสร้างผลบวกปลอม - จำนวนเต็มรวมที่แปลก ๆ ซึ่งการทดสอบล้มเหลวในการพิสูจน์ความสมบูรณ์ โดยเฉพาะถ้าฐานaล้มเหลวในการพิสูจน์ compositeness ของจำนวนที่คอมโพสิตคี่, ตัวเลขที่เรียกว่าpseudoprime แข็งแกร่งaให้กับฐาน ความท้าทายนี้เกี่ยวกับการค้นหาตัวเลขคอมโพสิตแปลก ๆ ที่มี psuedoprimes ที่แข็งแกร่งไปยังทุกฐานน้อยกว่าหรือเท่ากับNจำนวนเฉพาะที่ th (ซึ่งเทียบเท่ากับการบอกว่าพวกเขามี pseudoprimes ที่แข็งแกร่งกับฐานที่สำคัญทั้งหมดน้อยกว่าหรือเท่ากับNจำนวนที่สำคัญ) .



อัลกอริทึมที่ทดสอบค่าคี่ทั้งหมดจาก 1 ถึงผลลัพธ์สำหรับกฎหลอกแบบดั้งเดิมที่อนุญาตหรือไม่
user202729

@ user202729 ฉันไม่เห็นว่าทำไมมันจะไม่เป็น อะไรที่ทำให้คุณคิดว่ามันคืออะไร?
Mego

ฉันอยากจะแนะนำให้ตั้งคำถามให้เร็วที่สุดเพราะคำตอบส่วนใหญ่จะเป็นเรื่องที่โหดร้าย
Neil A.

@NeilA ฉันไม่เห็นด้วยว่านี่จะเป็นรหัสที่เร็วที่สุด ในขณะที่มันเป็นความจริงที่คำตอบเกือบจะแน่นอนกำลังดุร้าย (เนื่องจากอัลกอริทึมอื่นยังไม่ได้รับการพัฒนาและฉันไม่คาดหวังว่าจะทำ PPCG) รหัสกอล์ฟง่ายกว่ามากมีอุปสรรคน้อยกว่าสำหรับรายการ (ตั้งแต่ผู้ส่ง สามารถให้คะแนนวิธีแก้ปัญหาของตัวเอง) ไม่ต้องการให้ฉันเรียกใช้และให้คะแนนทุกวิธีแก้ปัญหา (และจัดการกับเวลาที่มากเกินไป) และปัญหาน่าสนใจพอสำหรับความท้าทายของกอล์ฟ
Mego

คำตอบ:


4

C, 349 295 277 267 255 ไบต์

N,i;__int128 n=2,b,o,l[999];P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}main(r){for(P(scanf("%d",&N));r|!b;)for(++n,b=i=N;i--&&b;){for(b=n-1,r=0;~b&1;b/=2)++r;for(o=1;b--;o=o*l[i]%n);for(b=o==1;r--;o=o*o%n)b|=o>n-2;for(o=r=1;++o<n;r&=n%o>0);}printf("%llu",n);}

รับอินพุต 1-based บน stdin เช่น:

echo "1" | ./millerRabin

แน่นอนว่ามันจะไม่ค้นพบค่าใหม่ใด ๆ ในลำดับในเวลาไม่นาน แต่จะทำให้งานเสร็จ อัพเดท: ตอนนี้ช้าลง!

  • เร็วขึ้นและสั้นลงเล็กน้อยด้วยแรงบันดาลใจจากคำตอบของ Neil A ( a^(d*2^r) == (a^d)^(2^r))
  • ช้าลงอย่างมีนัยสำคัญอีกครั้งหลังจากตระหนักว่า การแก้ปัญหาทั้งหมดสำหรับความท้าทายนี้จะแปลกดังนั้นจึงไม่จำเป็นต้องบังคับใช้อย่างชัดเจนว่าเราตรวจสอบตัวเลขคี่เท่านั้น
  • ตอนนี้ใช้ GCC __int128ซึ่งสั้นกว่าunsigned long longในขณะที่ทำงานกับจำนวนที่มากขึ้น! นอกจากนี้สำหรับเครื่องเล็ก ๆ น้อย ๆ printf ที่%lluยังทำงานได้ดี

น้อยลดขนาดลง

N,i;
__int128 n=2,b,o,l[999];
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}
main(r){
    for(P(scanf("%d",&N));r|!b;)
        for(++n,b=i=N;i--&&b;){
            for(b=n-1,r=0;~b&1;b/=2)++r;
            for(o=1;b--;o=o*l[i]%n);
            for(b=o==1;r--;o=o*o%n)b|=o>n-2;
            for(o=r=1;++o<n;r&=n%o>0);
        }
    printf("%llu",n);
}

(ล้าสมัย) รายละเอียด

unsigned long long                  // Use the longest type available
n=2,N,b,i,d,p,o,                    // Globals (mostly share names with question)
l[999];                             // Primes to check (limited to 999, but if you
                                    // want it to be "unlimited", change to -1u)
m(){for(o=1;p--;o=o*l[i]%n);}       // Inefficiently calculates (l[i]^p) mod n

// I cannot possibly take credit for this amazing prime finder;
// See /codegolf//a/5818/8927
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}

main(r){
    for(
        P(scanf("%llu",&N));        // Read & calculate desired number of primes
        r|!b;){                     // While we haven't got an answer:
        n=n+1|1;                    // Advance to next odd number
        for(b=1,i=N;i--&&b;){       // For each prime...
            for(d=n-1,r=0;~d&1;d/=2)++r; // Calculate d and r: d*2^r = n-1
            // Check if there exists any r such that a^(d*2^r) == -1 mod n
            // OR a^d == 1 mod n
            m(p=d);
            for(b=o==1;r--;b|=o==n-1)m(p=d<<r);
            // If neither of these exist, we have proven n is not prime,
            // and the outer loop will keep going (!b)
        }
        // Check if n is actually prime;
        // if it is, the outer loop will keep going (r)
        for(i=r=1;++i<n;r&=n%i!=0);
    }
    printf("%llu",n);               // We found a non-prime; print it & stop.
}

ตามที่กล่าวมาสิ่งนี้ใช้อินพุตแบบอิง 1 แต่สำหรับ n = 0 มันผลิตที่ 9 ซึ่งต่อไปนี้ลำดับที่เกี่ยวข้องhttps://oeis.org/A006945 ไม่อีกแล้ว; ตอนนี้มันแขวนที่ 0

ควรใช้ได้กับทุก n (อย่างน้อยก็จนกว่าเอาต์พุตจะถึง 2 ^ 64) แต่ก็ช้าอย่างไม่น่าเชื่อ ฉันได้ยืนยันไว้ใน n = 0, n = 1 และ (หลังจากที่จำนวนมากของการรอคอย) n = 2


ฉันทำการค้นพบวิธีแก้ปัญหาของฉันแล้วคุณจะทำให้ฉันเป็นหนึ่งเดียว ... Nice!
Neil A.

@NeilA ขออภัย! ฉันกำลังเล่นกับ int ชนิดที่สั้นกว่าก่อนที่คุณโพสต์การปรับปรุงของคุณ ฉันแน่ใจว่าคุณจะพบ 2 ไบต์ที่ใดที่หนึ่ง; สิ่งนี้กลายเป็นการแข่งขันที่น่าประหลาดใจเนื่องจากมีภาษาที่ไม่ใช่กอล์ฟ 2 ภาษาที่แตกต่างกัน: D
Dave

3

Python 2, 633 465 435 292 282 275 256 247 ไบต์

0 การจัดทำดัชนี

ตั้งคำถามกับการใช้งานและลองสิ่งใหม่ ๆ

การแปลงจากฟังก์ชั่นไปเป็นโปรแกรมจะบันทึกบางส่วนของไบต์ ...

ถ้า Python 2 ให้วิธีที่สั้นกว่าในการทำสิ่งเดียวกันฉันจะใช้ Python 2 การหารด้วยจำนวนเต็มเริ่มต้นดังนั้นวิธีที่ง่ายกว่าในการหารด้วย 2 และprintไม่ต้องใช้วงเล็บ

n=input()
f=i=3
z={2}
a=lambda:min([i%k for k in range(2,i)])
while n:
 if a():z|={i};n-=1
 i+=2
while f:
 i+=2;d,s,f=~-i,0,a()
 while~d&1:d/=2;s+=1
 for y in z:
  x=y**d%i
  if x-1:
   for _ in[]*s:
    x*=x
    if~x%i<1:break
   else:f=1
print i

ลองออนไลน์!

Python ช้ามากเมื่อเทียบกับภาษาอื่น

กำหนดการทดสอบแผนกทดลองเพื่อความถูกต้องสมบูรณ์จากนั้นใช้การทดสอบ Miller-Rabin ซ้ำ ๆ จนกว่าจะพบ pseudoprime

ลองออนไลน์!

แก้ไข : ในที่สุด golfed คำตอบ

แก้ไข : ใช้minสำหรับการทดสอบทดลองใช้ส่วน primality lambdaและเปลี่ยนไป มีประสิทธิภาพน้อยกว่า แต่สั้นกว่า ยังไม่สามารถช่วยตัวเองได้และใช้ตัวดำเนินการสองบิต (ไม่มีความยาวต่างกัน) ในทางทฤษฎีมันควรจะทำงานได้เร็วขึ้นเล็กน้อย

แก้ไข : ขอบคุณ @Dave บรรณาธิการของฉันหมุนรอบฉัน ฉันคิดว่าฉันใช้แท็บ แต่มันถูกแปลงเป็น 4 ช่องว่างแทน นอกจากนี้ยังได้ผ่านเคล็ดลับ Python เดียวและใช้มัน

แก้ไข : เปลี่ยนเป็นการทำดัชนี 0 อนุญาตให้ฉันบันทึกสองสามไบต์ด้วยการสร้างช่วงเวลา ลองคิดเปรียบเทียบดูบ้าง

แก้ไข : ใช้ตัวแปรเพื่อเก็บผลลัพธ์ของการทดสอบแทนที่จะเป็นfor/elseข้อความสั่ง

แก้ไข : ย้ายlambdaภายในฟังก์ชันเพื่อกำจัดความต้องการพารามิเตอร์

แก้ไข : แปลงเป็นโปรแกรมเพื่อบันทึกไบต์

แก้ไข : Python 2 ช่วยฉันไบต์! นอกจากนี้ฉันไม่ต้องแปลงอินพุตเป็นint


+1 สำหรับวิธีที่คุณจัดการa^(d*2^r) mod n!
เดฟ

คุณทราบหรือไม่ว่าคุณสามารถใช้การเว้นวรรค (หรือแท็บเดี่ยว) ใน Python เพื่อบันทึก ... ไบต์จำนวนมากจริง ๆ แล้ว
เดฟ

@Dave: นี่คือการใช้ 1 แท็บต่อระดับการเยื้อง
Neil A.

ฉันคิดว่า IDE ของคุณยุ่งกับคุณและประหยัดพื้นที่ในขณะที่บอกคุณว่าใช้แท็บ เมื่อฉันแทนที่พวกเขาเป็นช่องว่างเดียวฉันได้รับนับไบต์เพียง 311 ไบต์! ลองออนไลน์!
เดฟ

@Dave: โอเคแปลกดีขอบคุณฉันจะอัปเดตคำตอบ
Neil A.

2

Perl + Math :: Prime :: Util, 81 +27 = 108 bytes

1 until!is_provable_prime(++$\)&&is_strong_pseudoprime($\,2..nth_prime($_));$_=""

ทำงานด้วย-lpMMath::Prime::Util=:all(การลงโทษ 27 ไบต์, อุ๊ย)

คำอธิบาย

มันไม่ได้เป็นเพียง Mathematica ที่มี builtin สำหรับอะไรโดยทั่วไป Perl มี CPAN ซึ่งเป็นหนึ่งในคลังเก็บของขนาดใหญ่แห่งแรกและมีการรวบรวมโซลูชั่นสำเร็จรูปจำนวนมากสำหรับงานเช่นนี้ น่าเสียดายที่มันไม่ได้นำเข้า (หรือติดตั้ง) โดยค่าเริ่มต้นซึ่งหมายความว่าโดยทั่วไปแล้วมันจะไม่เป็นตัวเลือกที่ดีในการใช้งานในแต่เมื่อหนึ่งในนั้นเกิดขึ้นเพื่อให้พอดีกับปัญหาอย่างสมบูรณ์ ...

เราวิ่งผ่านจำนวนเต็มต่อเนื่องจนกว่าเราจะพบว่ามันไม่ได้เป็นไพร์ม แต่ก็ยังเป็น pseudoprime ที่แข็งแกร่งไปยังฐานจำนวนเต็มทั้งหมดตั้งแต่ 2 ถึงไพน์ลำดับที่n ตัวเลือกบรรทัดคำสั่งนำเข้าไลบรารีที่มี builtin ในคำถามและยังตั้งค่าอินพุตโดยนัย (เป็น line-at-a-time;Math::Prime::Utilมีห้องสมุด builtin bignum ของตัวเองที่ไม่ชอบการขึ้นบรรทัดใหม่ในจำนวนเต็มของมัน) สิ่งนี้ใช้เคล็ดลับ Perl มาตรฐานของการใช้$\(ตัวแยกบรรทัดเอาต์พุต) เป็นตัวแปรเพื่อลดการแยกวิเคราะห์ที่ไม่เหมาะสมและอนุญาตให้สร้างผลลัพธ์โดยปริยาย

โปรดทราบว่าเราจำเป็นต้องใช้ is_provable_primeที่นี่เพื่อขอทดสอบแบบกำหนดค่าล่วงหน้าแทนที่จะทดสอบความน่าจะเป็น (โดยเฉพาะเมื่อมีการทดสอบความน่าจะเป็นที่น่าจะเป็นไปได้ในการใช้ Miller-Rabin ในตอนแรกซึ่งเราไม่สามารถคาดหวังได้ว่าจะให้ผลลัพธ์ที่เชื่อถือได้ในกรณีนี้!)

Perl + Math :: Prime :: Util, 71 + 17 = 88 bytes โดยความร่วมมือกับ @Dada

1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..n‌​th_prime$_}{

ทำงานด้วย-lpMntheory=:all(การลงโทษ 17 ไบต์)

นี้ใช้ไม่กี่ Perl เทคนิคการเล่นกอล์ฟที่ฉันอาจไม่ได้รู้ (เห็นได้ชัดคณิตศาสตร์ :: นายกรัฐมนตรี :: Util มีตัวย่อ!) รู้เรื่อง แต่ไม่ได้คิดว่าการใช้ ( }{การส่งออก$\โดยปริยายครั้งมากกว่า"$_$\"โดยปริยายทุกบรรทัด) หรือรู้เกี่ยวกับ แต่มีการจัดการอย่างผิดพลาด (ลบวงเล็บออกจากการเรียกใช้ฟังก์ชัน) ขอบคุณ @Dada ที่ชี้ให้ฉันเห็น นอกเหนือจากนั้นมันเหมือนกัน


แน่นอนว่ามีภาษากอล์ฟและการเต้นที่เหลือ ทำได้ดี!
Neil A.

คุณสามารถใช้แทนntheory Math::Prime::Utilนอกจากนี้}{แทนที่จะ;$_=""เป็นเรื่องปกติ และคุณสามารถเว้นช่องว่างหลัง1และวงเล็บของการเรียกใช้ฟังก์ชันบางอย่าง นอกจากนี้ทำงานแทน& &&นั่นควรให้ 88 ไบต์:perl -Mntheory=:all -lpe '1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..nth_prime$_}{'
Dada

}{ฉันลืมสมบูรณ์เกี่ยวกับ (แปลกมากฉันจำสิ่งที่อยู่ในวงเล็บได้ แต่มันก็ไม่นานแล้วตั้งแต่ฉันเล่นกอล์ฟใน Perl และจำกฎไม่ได้ว่าจะปล่อยทิ้งไว้) ฉันไม่รู้เกี่ยวกับntheoryตัวย่อเลย
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.