Asal sayıları bulmak için en hızlı algoritma hangisidir?


184

C ++ kullanarak asal sayıları bulmak için en hızlı algoritma hangisidir? Elek algoritmasını kullandım ama hala daha hızlı olmasını istiyorum!


Bulduğum, ancak ilginç görünen eski bir makale: Prime Numbers With Fun
Mvcoile

29
@Jaider bu, 7 (111) kadar düşük sayılar için başarısız olur. Ayrıca 1001 = 9 için başarısız olur. Ve açıkça genel olarak hemen hemen tüm primler için başarısız olur (Mersenne asal sayıları - klasik olarak üretilen örnekler - her zaman 111 ... 1 biçiminde olacak olan davayı kapsamaz 2 ^ p - 1)
Daniel Kats

1
@Kasperasky - Hangi Sieve'den bahsetmedin? Muhtemelen Eranthoses Eleği demek istiyorsun!
user2618142

Eratosthenes algoritmasının
eleği

Yanıtlar:


79

Atkin Elekinin çok hızlı uygulanması Dan Bernstein'ın başbakanıdır . Bu elek Eratosthenes elekinden daha verimlidir . Sayfasında bazı karşılaştırma bilgileri var.


10
Aslında primegen'in en hızlı, hatta ikinci en hızlı olduğunu düşünmüyorum; Yafu ve primesieve genel olarak daha hızlı, bence ve kesinlikle 2 ^ 32. Her ikisi de Atkin-Bernstein elekinden ziyade (değiştirilmiş) Eratosthenes elekleridir.
Charles

6
Eratosthenes'in Primesieve Eleği ( SoE ) mümkün olan en hızlı algoritmadır ve bu cevapta bağlantılı olan Bernstein'ın da dahil olduğu Atkin SoA Elekinin herhangi bir uygulamasından her zaman daha hızlı olacaktır, çünkü primesieve SoA'ya kıyasla işlem sayısını azaltır: bit sayısı aralığı (2 ^ 32-1), primesieve yaklaşık 1.2 milyar cull, SoA yaklaşık 1.4 milyar kombine geçiş ve kare serbest operasyon yapar, her iki işlem de yaklaşık aynı karmaşıklığa sahiptir ve yaklaşık aynı şekilde optimize edilebilir yol.
GordonBGood

7
Devam: Bernstein, SoE'yi yalnızca, 32-bit sayı aralığında yaklaşık 1,83 milyar culls ile sonuçlanan 2; 3; 5 bir tekerlek olan SoA ile aynı etkili tekerlek çarpanlarına ayırma kullanarak karşılaştırdı; bu, eşdeğer diğer optimizasyonlar için SoE'nin bu sınırlı sürümünü karşılaştırırken SoA'yı yaklaşık% 30 daha hızlı hale getirir . Bununla birlikte, primesieve algoritması, işlem sayısını yaklaşık 1.2 milyara düşürmek için 2; 3; 5; 7; 11; 13; 17 tekerlek segmenti ile birlikte 2; 3; 5; 7 tekerlek kullanır Eşdeğer çalışma döngüsü optimizasyonlarıyla SoA'dan% 16,7 daha hızlı.
GordonBGood

6
Devam2: SoA con, 2; 3; 5 faktorizasyon tekerleği algoritmanın "pişmiş" bir parçası olduğu için fark yaratmak için kullanılan daha yüksek faktörlü tekerlek çarpanlarına sahip değildir.
GordonBGood

4
@Eamon Nerbonne, WP doğru; ancak, biraz daha iyi bir hesaplama karmaşıklığına sahip olmak, genel kullanım için daha hızlı bir algoritma yapmaz. Bu yorumlarda, Eratosthenes Elekinin (SoE) (Atkin-SoA Eleği için mümkün olmayan) maksimum tekerlek çarpanlarına ayırma işleminin, SoE için yaklaşık bir milyar aralığa kadar biraz daha az işlem yapmasını kastediyorum. Bu noktanın çok üzerinde, bellek sınırlamalarının üstesinden gelmek için genellikle sayfa segmentasyonunu kullanmak gerekir ve bu, SoA'nın başarısız olduğu ve artan aralıklarla hızla artan miktarlarda sabit ek yükü aldığı yerdir.
GordonBGood

29

Gerçekten hızlı olması gerekiyorsa, bir asal sayı listesi ekleyebilirsiniz:
http://www.bigprimes.net/archive/prime/

Belirli bir sayının asal sayı olup olmadığını bilmeniz gerekiyorsa , wikipedia'da listelenen çeşitli asal testler vardır . Büyük sayıların asal olup olmadığını belirlemek için muhtemelen en hızlı yöntemdir, özellikle de bir sayının asal olmadığını söyleyebilirler .


2
Tüm asal sayıların bir listesi? Sanırım ilk birkaç
asilin

9
Eğer 100000000'ü birkaç ararsanız, evet. :)
Georg Schölly

58
şüphesiz 100000000 sonsuza kıyasla "birkaç";)
Timofey

9
Neden Atkin Eleklerinin (SoA) Eratosthenes Eleklerinden (SoE) daha hızlı olduğunu düşünüyorsunuz? Bağlandığınız Wikipedia makalesinde olduğu gibi sahte kod kullanarak bir program uygulandığında kesinlikle değil. Eğer SoE, SoA ile kullanılanla aynı seviyede olası optimizasyonlarla uygulanırsa, SoA için SoE'ye göre çok büyük eleme aralıkları için biraz daha az işlem vardır, ancak bu kazanç genellikle artan karmaşıklık ve pratik hesaplamalar için SoE daha iyi olacak şekilde bu hesaplama karmaşıklığının ek sabit faktör yükü.
GordonBGood

26

O, eski sorulara cevap veren bir soru büyücüsü olduğumu biliyorum, ama bu soruyu, net asal sayı testleri yapmanın yollarını arayarak buldum.

Şimdiye kadar, en hızlı asal sayı test algoritmasının Güçlü Olası Başbakan (SPRP) olduğuna inanıyorum. Nvidia CUDA forumlarından alıntı yapıyorum:

Sayı teorisindeki daha pratik niş sorunlarından biri asal sayıların tanımlanmasıyla ilgilidir. N verildiğinde, asal olup olmadığını nasıl verimli bir şekilde belirleyebilirsiniz? Bu sadece eterik bir sorun değildir, belki de belirli aralıklarda dinamik bir asal tablo boyutu bulmanız gerektiğinde, kodda gerçek bir sorun olabilir. N, 2 ^ 30 düzeyindeki bir şeyse, herhangi bir faktörü aramak için gerçekten 30000 bölüm testi yapmak ister misiniz? Belli ki değil.

Bu sorunun ortak pratik çözümü, Euler olası ana testi adı verilen basit bir test ve Güçlü Olası Başbakan (SPRP) adı verilen daha güçlü bir genellemedir. Bu, bir tamsayı N için onu olasılıkla birincil olarak sınıflandırıp sınıflandıramayacağı ve tekrarlanan testlerin doğruluk olasılığını artırabileceği bir testtir. Testin yavaş kısmı çoğunlukla A ^ (N-1) modulo N'ye benzer bir değer hesaplamayı içerir. RSA ortak anahtar şifreleme varyantlarını uygulayan herkes bu algoritmayı kullanmıştır. Hem büyük tam sayılar (512 bit gibi) hem de normal 32 veya 64 bit ints için yararlıdır.

Test, N aralıkları için her zaman başarılı olduğu bilinen bazı test giriş parametrelerini önceden hesaplayarak, olasılıkla reddedilmenin kesin bir ilkel kanıtına dönüştürülebilir. Ne yazık ki bu "en iyi bilinen testlerin" keşfi, büyük bir ( aslında sonsuz). 1980'de Carl Pomerance tarafından (Kuadratik Seive algoritmasıyla RSA-129'u faktör olarak kabul ettiği için ünlü) ilk yararlı testler listesi oluşturuldu. Daha sonra Jaeschke 1993'te sonuçları önemli ölçüde geliştirdi. 2004'te Zhang ve Tang teoriyi geliştirdi ve arama alanının sınırları. Greathouse ve Livingstone, bugüne kadar web'de en modern sonuçları , büyük bir arama alanının en iyi sonuçları olan http://math.crg4.com/primes.html adresinde yayınladı .

Daha fazla bilgi için buraya bakın: http://primes.utm.edu/prove/prove2_3.html ve http://forums.nvidia.com/index.php?showtopic=70483

Sadece çok büyük asal sayılar üretmenin bir yoluna ihtiyacınız varsa ve tüm asal sayılar <bir tamsayı n üretmekle ilgilenmiyorsanız, Mersenne asal sayılarını doğrulamak için Lucas-Lehmer testini kullanabilirsiniz. Bir Mersenne asal numarası 2 ^ p -1 biçimindedir. Ben Lucas-Lehmer testinin Mersenne asal sayıları için keşfedilen en hızlı algoritma olduğunu düşünüyorum.

Ve sadece en hızlı algoritmayı değil, aynı zamanda en hızlı donanımı da kullanmak istiyorsanız, Nvidia CUDA kullanarak uygulamayı deneyin, CUDA için bir çekirdek yazın ve GPU'da çalıştırın.

Yeterince büyük asal sayılar bulursanız bile para kazanabilirsiniz, EFF 50 bin dolardan 250 bin dolara kadar ödül veriyor: https://www.eff.org/awards/coop


17

AKS Primality Test olarakP adlandırılan bir sayının asal veya kompozit olup olmadığını kontrol edecek% 100 matematiksel bir test vardır .

Kavram basittir: bir sayı verildiğinde P, tüm katsayıları (x-1)^P - (x^P-1)ile bölünebilirse P, o zaman Pasal bir sayıdır, aksi takdirde bileşik bir sayıdır.

Örneğin, verilen P = 3, polinomu verir:

   (x-1)^3 - (x^3 - 1)
 = x^3 + 3x^2 - 3x - 1 - (x^3 - 1)
 = 3x^2 - 3x

Ve katsayıların her ikisi de bölünebilir 3, bu nedenle sayı asaldır.

Ve P = 4asal olmayan DEĞİL nerede örnek :

   (x-1)^4 - (x^4-1)
 = x^4 - 4x^3 + 6x^2 - 4x + 1 - (x^4 - 1)
 = -4x^3 + 6x^2 - 4x

Ve burada katsayıların 6bölünemez olmadığını görebiliriz 4, bu nedenle birincil değildir.

Polinom (x-1)^Pirade P+1şart ve kombinasyonu kullanılarak bulunabilir. Yani, bu test O(n)çalışma zamanında çalışacaktır , bu yüzden sadece i0'dan daha fazla yineleyebildiğiniz pve geri kalanını test edebildiğiniz için bunun ne kadar yararlı olacağını bilmiyorum .


5
AKS uygulamada çok yavaş bir yöntemdir, bilinen diğer yöntemlerle rekabetçi değildir. Açıkladığınız yöntem AKS değil, optimize edilmemiş deneme bölümünden daha yavaş olan bir açılış lemmasıdır (belirttiğiniz gibi).
DanaJ

merhaba @Kousha, ne anlama geliyor x? içinde (x-1)^P - (x^P-1). bunun için örnek bir kod var mı? tam sayı asal olup olmadığını belirlemek için C ++?
kiLLua

@ kiLLua X sadece bir değişkendir. Sayının asal olup olmadığını belirleyen X katsayısıdır. Ve hayır kodum yok. Bir sayının asal olup olmadığını belirlemek için aslında bu yöntemi kullanmanızı önermiyorum. Bu, asal sayıların çok güzel bir matematiksel davranışıdır, ancak aksi halde inanılmaz derecede verimsizdir.
Kousha

5

Sorununuz belirli bir sayının asal olup olmadığına karar vermek mi? O zaman bir öncelik testine ihtiyacınız var (kolay). Yoksa belirli bir sayıya kadar tüm asal sayılara mı ihtiyacınız var? Bu durumda ana elekler iyidir (kolay, ancak bellek gerektirir). Veya bir sayının asal faktörlerine mi ihtiyacınız var? Bu, çarpanlara ayırmayı gerektirir (gerçekten en verimli yöntemleri istiyorsanız büyük sayılar için zor). Hangi rakamlara bakıyorsunuz? 16 bit? 32 bit? Daha büyük?

Akıllı ve etkili bir yol, asal tabloları önceden hesaplamak ve bit düzeyinde kodlama kullanarak bir dosyada tutmaktır. Dosya bir uzun bit vektörü olarak kabul edilirken, n biti n tamsayısını temsil eder. N asal ise, biti bire ve sıfır olarak ayarlanır. Arama çok hızlıdır (bayt uzaklığını ve bit maskesini hesaplarsınız) ve dosyanın belleğe yüklenmesini gerektirmez.


İyi bir primality testi, makul şekilde sığabilen ana tablolar için ana bellek gecikmesi ile rekabetçidir, bu yüzden L2'ye sığmadığı sürece bunu kullanmam.
Charles

3

Rabin-Miller standart bir olasılıksal öncelik testidir. (K kez çalıştırırsınız ve giriş numarası kesinlikle kompozittir veya muhtemelen 4 -K hata olasılığı ile asaldır . (birkaç yüz yineleme ve neredeyse kesinlikle gerçeği söyler)

Rabin Miller'ın olasılık dışı (deterministik) bir çeşidi vardır .

Büyük İnternet Mersenne Prime Arama büyük kanıtlanmış asal için dünya rekoru bulmuştur (gimps) (2 74207281 - Haziran 2017 tarihi itibariyle 1) kullanır çeşitli algoritmalar , ama bu özel formlarda Asal sayılar. Bununla birlikte, yukarıdaki GIMPS sayfası bazı genel deterministik öncelik testleri içerir. Hangi algoritmanın "en hızlı" olduğunu, test edilecek sayının boyutuna bağlı olduğunu belirtiyorlar. Numaranız 64 bit'e uyuyorsa, muhtemelen birkaç milyon basamaklı primerlerde çalışmak için bir yöntem kullanmamalısınız.


2

Uygulamanıza bağlıdır. Bazı düşünceler var:

  • Sadece birkaç sayının asal olup olmadığı bilgisine mi ihtiyacınız var, belirli bir sınıra kadar tüm asal sayılara mı ihtiyacınız var, yoksa (potansiyel olarak) tüm asal sayılara mı ihtiyacınız var?
  • Başa çıkmak zorunda olduğunuz rakamlar ne kadar büyük?

Miller-Rabin ve analog testleri, belirli bir büyüklükteki sayılar için sadece bir elekten daha hızlıdır (birkaç milyon civarında bir yere inanıyorum). Bunun altında, bir deneme bölümü (sadece birkaç numaranız varsa) veya bir elek kullanmak daha hızlıdır.


0

En hızlı olup olmadığına karar vermene izin vereceğim.

using System;
namespace PrimeNumbers
{

public static class Program
{
    static int primesCount = 0;


    public static void Main()
    {
        DateTime startingTime = DateTime.Now;

        RangePrime(1,1000000);   

        DateTime endingTime = DateTime.Now;

        TimeSpan span = endingTime - startingTime;

        Console.WriteLine("span = {0}", span.TotalSeconds);

    }


    public static void RangePrime(int start, int end)
    {
        for (int i = start; i != end+1; i++)
        {
            bool isPrime = IsPrime(i);
            if(isPrime)
            {
                primesCount++;
                Console.WriteLine("number = {0}", i);
            }
        }
        Console.WriteLine("primes count = {0}",primesCount);
    }



    public static bool IsPrime(int ToCheck)
    {

        if (ToCheck == 2) return true;
        if (ToCheck < 2) return false;


        if (IsOdd(ToCheck))
        {
            for (int i = 3; i <= (ToCheck / 3); i += 2)
            {
                if (ToCheck % i == 0) return false;
            }
            return true;
        }
        else return false; // even numbers(excluding 2) are composite
    }

    public static bool IsOdd(int ToCheck)
    {
        return ((ToCheck % 2 != 0) ? true : false);
    }
}
}

2,40 GHz işlemcili Core 2 Duo dizüstü bilgisayarımda 1 ila 1,000,000 aralığında asal sayıları bulmak ve yazdırmak yaklaşık 82 saniye sürer . Ve 78.498 asal sayı buldu .


3
bu çok yavaş. Sorun şu ki i <= (ToCheck / 3). olmalı i <= (ToCheck / i). onunla, bunun yerine 0.1 saniye içinde çalışabilir.
Ness

-1

Her zaman elek algoritması ile takip eden asal sayıları hesaplamak için bu yöntemi kullanıyorum.

void primelist()
 {
   for(int i = 4; i < pr; i += 2) mark[ i ] = false;
   for(int i = 3; i < pr; i += 2) mark[ i ] = true; mark[ 2 ] = true;
   for(int i = 3, sq = sqrt( pr ); i < sq; i += 2)
       if(mark[ i ])
          for(int j = i << 1; j < pr; j += i) mark[ j ] = false;
  prime[ 0 ] = 2; ind = 1;
  for(int i = 3; i < pr; i += 2)
    if(mark[ i ]) ind++; printf("%d\n", ind);
 }

-3
#include<stdio.h>
main()
{
    long long unsigned x,y,b,z,e,r,c;
    scanf("%llu",&x);
    if(x<2)return 0;
    scanf("%llu",&y);
    if(y<x)return 0;
    if(x==2)printf("|2");
    if(x%2==0)x+=1;
    if(y%2==0)y-=1;
    for(b=x;b<=y;b+=2)
    {
        z=b;e=0;
        for(c=2;c*c<=z;c++)
        {
            if(z%c==0)e++;
            if(e>0)z=3;
        }
        if(e==0)
        {
            printf("|%llu",z);
            r+=1;
        }
    }
    printf("|\n%llu outputs...\n",r);
    scanf("%llu",&r);
}    

r başlatılmadan önce kullanılır
zumalifeguard

-3

Önceden tanımlanmış herhangi bir algoritma bilmiyorum ama çok hızlı olan kendi algoritmamı oluşturdum. 1 saniyeden daha kısa sürede 20 basamaklı sayıları işleyebilir. Bu programın maksimum kapasitesi 18446744073709551615'dir. Program:

#include <iostream>
#include <cmath>
#include <stdlib.h>

using namespace std;

unsigned long long int num = 0;

bool prime() {
    if (num % 2 == 0 || num == 1) {
        return false;
    }

    unsigned long int square_root = sqrt(num);
    for (unsigned long int i = 3; i <= square_root; i += 2) {
        if (num % i == 0) {
            return false;
        }
    }

    return true;
}

int main() {
    do {
        system("cls");
        cout << "Enter number : ";
        cin >> num;

        if (prime()) {
            cout << "The number is a prime number" << endl << endl << endl << endl;
        } else {
            cout << "The number is not a prime number" << endl << endl << endl << endl;
        }
        system("pause");
    } while (1);

    return 0;
}

-4
#include <iostream>

using namespace std;

int set [1000000];

int main (){

    for (int i=0; i<1000000; i++){
        set [i] = 0;
    }
    int set_size= 1000;
    set [set_size];
    set [0] = 2;
    set [1] = 3;
    int Ps = 0;
    int last = 2;

    cout << 2 << " " << 3 << " ";

    for (int n=1; n<10000; n++){
        int t = 0;
        Ps = (n%2)+1+(3*n);
        for (int i=0; i==i; i++){
            if (set [i] == 0) break;
            if (Ps%set[i]==0){
                t=1;
                break;
            }
        }
        if (t==0){
            cout << Ps << " ";
            set [last] = Ps;
            last++;
        }
    }
    //cout << last << endl;


    cout << endl;

    system ("pause");
    return 0;
}

12
bu, "GOTO'yu kullanmadan yapılandırılmamış kod nasıl yazılır" üzerine bir cevap olmalıdır. Tüm bu kafa karışıklığı sadece basit bir deneme bölümünü kodlamak için! (n%2)+1+(3*n)gerçi güzel. :)
Ness

1
@ Ness'i bunu bu sorunun cevabı olarak reddederdim; Neden bir makro yapıldığında for döngüsü kullanılır? :)
Rob Grant

-4

Biraz sonra olduğunu biliyorum, ama bu aramalardan gelen insanlar için yararlı olabilir. Her neyse, burada sadece asal faktörlerin test edilmesi gerektiğine dayanan bazı JavaScriptler var, bu nedenle kod tarafından oluşturulan önceki asal değerler daha sonraki olanlar için test faktörleri olarak yeniden kullanılıyor. Tabii ki, önce tüm çift ve mod 5 değerleri filtrelenir. Sonuç P dizisinde olacaktır ve bu kod bir i7 PC'de (veya yaklaşık 20'de 100 milyon) 1,5 saniyenin altında 10 milyon primu kırabilir. C'de yeniden yazıldığında çok hızlı olmalı.

var P = [1, 2], j, k, l = 3

for (k = 3 ; k < 10000000 ; k += 2)
{
  loop: if (++l < 5)
  {
    for (j = 2 ; P[j] <= Math.sqrt(k) ; ++j)
      if (k % P[j] == 0) break loop

    P[P.length] = k
  }
  else l = 0
}

2
Çok sayıda asal üretiyorsanız bu size çok fazla sıkıntı verecektir ve karşılaştırmalar için P [j] * P [j] <= k'yi daha iyi kullanın, çünkü sqrt oldukça yavaştır
Simon

-11
#include<iostream>
using namespace std;

void main()
{
    int num,i,j,prime;
    cout<<"Enter the upper limit :";
    cin>>num;

    cout<<"Prime numbers till "<<num<<" are :2, ";

    for(i=3;i<=num;i++)
    {
        prime=1;
        for(j=2;j<i;j++)
        {
            if(i%j==0)
            {
                prime=0;
                break;
            }
        }

        if(prime==1)
            cout<<i<<", ";

    }
}

60
bu en yavaş gidebileceğiniz şeyle ilgili.
Ness

1
Bu çok yavaş, üst sınır 10000000 diyelim, bu kod çok zaman harcayacak !!
Dixit Singla

bu kod O'dur (N ^ 2 / log N). olmadan break;daha yavaş olurdu, O (N ^ 2), ama bu zaten bir kodlama hatası olarak görülebilirdi. primerlerle kaydetme ve test etme O (N ^ 2 / (log N) ^ 2) ve sadece sayının kare kökünün altındaki primerlerle test O (N ^ 1.5 / (log N) ^ 2).
Ness Ness

@WillNess Belki biraz hiperbolik. For döngüsünü 2 yerine 1'den kolayca başlatabilir ve j <i yerine j <= i ekleyebilir. :)
Kenny Cason

3
Bu yazının silinmesi gerektiğini düşünmüyorum, değerli bir karşı örnek olarak hizmet ediyor.
Ness
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.