Il modo più elegante per generare numeri primi [chiuso]


84

Qual è il modo più elegante per implementare questa funzione:

ArrayList generatePrimes(int n)

Questa funzione genera i primi nnumeri primi (modifica: dove n>1), quindi generatePrimes(5)restituirà un ArrayListcon {2, 3, 5, 7, 11}. (Lo sto facendo in C #, ma sono soddisfatto di un'implementazione Java o di qualsiasi altro linguaggio simile (quindi non Haskell)).

So come scrivere questa funzione, ma quando l'ho fatta ieri sera non è andata così bene come speravo. Ecco cosa mi è venuto in mente:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();
    primes.Add(2);
    primes.Add(3);
    while (primes.Count < toGenerate)
    {
        int nextPrime = (int)(primes[primes.Count - 1]) + 2;
        while (true)
        {
            bool isPrime = true;
            foreach (int n in primes)
            {
                if (nextPrime % n == 0)
                {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
            {
                break;
            }
            else
            {
                nextPrime += 2;
            }
        }
        primes.Add(nextPrime);
    }
    return primes;
}

Non sono troppo preoccupato per la velocità, anche se non voglio che sia ovviamente inefficiente. Non mi importa quale metodo viene utilizzato (ingenuo o setaccio o qualsiasi altra cosa), ma voglio che sia abbastanza breve e ovvio come funziona.

Modifica : grazie a tutti coloro che hanno risposto, anche se molti non hanno risposto alla mia vera domanda. Per ribadire, volevo un bel pezzo di codice pulito che generasse un elenco di numeri primi. So già come farlo in molti modi diversi, ma sono incline a scrivere codice che non è così chiaro come potrebbe essere. In questo thread sono state proposte alcune buone opzioni:

  • Una versione migliore di quello che avevo originariamente (Peter Smit, jmservera e Rekreativc)
  • Un'implementazione molto pulita del setaccio di Eratostene (starblue)
  • Usa Java BigIntegere nextProbablePrimeper codice molto semplice, anche se non riesco a immaginare che sia particolarmente efficiente (dfa)
  • Usa LINQ per generare pigramente l'elenco dei numeri primi (Maghis)
  • Metti molti numeri primi in un file di testo e leggili quando necessario (tesoro)

Modifica 2 : ho implementato in C # un paio dei metodi forniti qui e un altro metodo non menzionato qui. Tutti trovano efficacemente i primi n primi (e ho un metodo decente per trovare il limite da fornire ai setacci).


12
no, e non è nemmeno per Project Euler :-)
David Johnstone,

1
sarebbe meglio se mi ritutassi innumerevoli <int> e cedessi uno ad uno
Felice Pollano

4
Quello che vorrei sapere è qual è il modo meno elegante per generare numeri primi. Sto pensando a qualcosa che coinvolge un database di Access?
j_random_hacker

1
per il confronto, un codice di 2008 Haskell per BMeph : nubBy (((>1).).gcd) [2..]. Lascia solo i non duplicati tra i numeri naturali, a partire da 2, mentre considera duplicato qualsiasi numero il cui numero uno gcdqualsiasi dei numeri precedentemente trovati sia maggiore di 1. È molto inefficiente, quadratico per numero di primi prodotti. Ma è elegante .
Will Ness

il più elegante , IMO, è quello di Haskell, import Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (\p-> [p*p, p*p+p*2..]) ) }ma ovviamente è interamente basato sull'opinione .
Will Ness

Risposte:


48

Usa la stima

pi(n) = n / log(n)

per il numero di numeri primi fino an per trovare un limite, quindi utilizzare un setaccio. La stima sottostima il numero di numeri primi fino an un po ', quindi il setaccio sarà leggermente più grande del necessario, il che è ok.

Questo è il mio setaccio Java standard, calcola il primo milione di numeri primi in circa un secondo su un normale laptop:

public static BitSet computePrimes(int limit)
{
    final BitSet primes = new BitSet();
    primes.set(0, false);
    primes.set(1, false);
    primes.set(2, limit, true);
    for (int i = 0; i * i < limit; i++)
    {
        if (primes.get(i))
        {
            for (int j = i * i; j < limit; j += i)
            {
                primes.clear(j);
            }
        }
    }
    return primes;
}

3
Questa è una bella implementazione del setaccio di Eratostene
David Johnstone il

1
non dovrebbe essere sufficiente eseguire il loop mentre si è i <= Math.sqrt(limit)nel loop esterno?
Christoph

1
@David Johnstone No, pi (n) = n / log (n) sottostima il numero di numeri primi fino a n, che va nella direzione opposta. Sono contento che tu abbia trovato un'approssimazione molto migliore, però.
starblue

1
se sei disposto a rimuovere tutti i multipli di 2 nel suo ciclo, puoi usare j + = 2 * i come incremento del tuo ciclo per risparmiare un po 'di tempo di esecuzione extra, e puoi calcolarlo una volta usando un po' shift
Nick Larsen

5
Sostituendo BitSet con una classe che implementa la fattorizzazione della ruota per 2, 3 e 5, diventa quasi 3 volte più veloce.
starblue

37

Molte grazie a tutti coloro che hanno dato risposte utili. Ecco le mie implementazioni di alcuni metodi diversi per trovare i primi n numeri primi in C #. I primi due metodi sono praticamente ciò che è stato pubblicato qui. (I nomi dei poster sono accanto al titolo.) Ho intenzione di fare il setaccio di Atkin qualche volta, anche se sospetto che non sarà così semplice come i metodi qui attualmente. Se qualcuno può vedere un modo per migliorare uno qualsiasi di questi metodi mi piacerebbe sapere :-)

Metodo standard ( Peter Smit , jmservera , Rekreativc )

Il primo numero primo è 2. Aggiungilo a un elenco di numeri primi. Il numero primo successivo è il numero successivo che non è divisibile in modo uniforme per alcun numero in questo elenco.

public static List<int> GeneratePrimesNaive(int n)
{
    List<int> primes = new List<int>();
    primes.Add(2);
    int nextPrime = 3;
    while (primes.Count < n)
    {
        int sqrt = (int)Math.Sqrt(nextPrime);
        bool isPrime = true;
        for (int i = 0; (int)primes[i] <= sqrt; i++)
        {
            if (nextPrime % primes[i] == 0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            primes.Add(nextPrime);
        }
        nextPrime += 2;
    }
    return primes;
}

Questo è stato ottimizzato testando solo la divisibilità fino alla radice quadrata del numero testato; e testando solo numeri dispari. Questo può essere ulteriormente ottimizzato testando solo i numeri del modulo 6k+[1, 5], 30k+[1, 7, 11, 13, 17, 19, 23, 29]o così via .

Setaccio di Eratostene ( starblue )

Questo trova tutti i numeri primi a k . Per fare un elenco dei primi n primi, dobbiamo prima approssimare il valore del primo n- esimo. Il metodo seguente, come descritto qui , esegue questa operazione.

public static int ApproximateNthPrime(int nn)
{
    double n = (double)nn;
    double p;
    if (nn >= 7022)
    {
        p = n * Math.Log(n) + n * (Math.Log(Math.Log(n)) - 0.9385);
    }
    else if (nn >= 6)
    {
        p = n * Math.Log(n) + n * Math.Log(Math.Log(n));
    }
    else if (nn > 0)
    {
        p = new int[] { 2, 3, 5, 7, 11 }[nn - 1];
    }
    else
    {
        p = 0;
    }
    return (int)p;
}

// Find all primes up to and including the limit
public static BitArray SieveOfEratosthenes(int limit)
{
    BitArray bits = new BitArray(limit + 1, true);
    bits[0] = false;
    bits[1] = false;
    for (int i = 0; i * i <= limit; i++)
    {
        if (bits[i])
        {
            for (int j = i * i; j <= limit; j += i)
            {
                bits[j] = false;
            }
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfEratosthenes(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfEratosthenes(limit);
    List<int> primes = new List<int>();
    for (int i = 0, found = 0; i < limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(i);
            found++;
        }
    }
    return primes;
}

Setaccio di Sundaram

Ho scoperto questo setaccio solo di recente, ma può essere implementato in modo molto semplice. La mia implementazione non è veloce come il setaccio di Eratostene, ma è significativamente più veloce del metodo ingenuo.

public static BitArray SieveOfSundaram(int limit)
{
    limit /= 2;
    BitArray bits = new BitArray(limit + 1, true);
    for (int i = 1; 3 * i + 1 < limit; i++)
    {
        for (int j = 1; i + j + 2 * i * j <= limit; j++)
        {
            bits[i + j + 2 * i * j] = false;
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfSundaram(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfSundaram(limit);
    List<int> primes = new List<int>();
    primes.Add(2);
    for (int i = 1, found = 1; 2 * i + 1 <= limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(2 * i + 1);
            found++;
        }
    }
    return primes;
}

FYI - Ho dovuto cambiare il contatore del ciclo principale in "for (int i = 0; i * i <= limit && i * i> 0; i ++)" per evitare un overflow.
Jacobs Data Solutions

Questa implementazione del Setaccio di Sundaram è una delle pochissime esatte là fuori. La maggior parte di loro utilizza limiti errati per i e j durante il calcolo che i+j+2*i*jporta a un output errato.
jahackbeth

16

Ressurando una vecchia domanda, ma ci sono inciampato mentre giocavo con LINQ.

Questo codice richiede .NET4.0 o .NET3.5 con estensioni parallele

public List<int> GeneratePrimes(int n) {
    var r = from i in Enumerable.Range(2, n - 1).AsParallel()
            where Enumerable.Range(1, (int)Math.Sqrt(i)).All(j => j == 1 || i % j != 0)
            select i;
    return r.ToList();
}

1
Perché non è questa la risposta accettata? Il codice qui è molto più breve, più elegante e molto più veloce del codice nella risposta accettata. Vorrei poter votare più di una volta!
Avrohom Yisroel

9

Sei sulla buona strada.

Alcuni commenti

  • numeri primi. Aggiungi (3); fa in modo che questa funzione non funzioni per numero = 1

  • Non è necessario testare la divisione con numeri primi più grandi della radice quadrata del numero da testare.

Codice suggerito:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();

    if(toGenerate > 0) primes.Add(2);

    int curTest = 3;
    while (primes.Count < toGenerate)
    {

        int sqrt = (int) Math.sqrt(curTest);

        bool isPrime = true;
        for (int i = 0; i < primes.Count && primes.get(i) <= sqrt; ++i)
        {
            if (curTest % primes.get(i) == 0)
            {
                isPrime = false;
                break;
            }
        }

        if(isPrime) primes.Add(curTest);

        curTest +=2
    }
    return primes;
}

1
testare che prime * prime <= curTest nel ciclo invece di precalcolare la radice quadrata probabilmente lo renderà più veloce e lo renderà più generico (funzionerà per bignum, ecc.)
yairchu

Perché usare la radice quadrata? Qual è il background matematico di tale opzione? Io, probabilmente ottusamente, dividerei solo per 2.
Luis Filipe

3
Perché se un numero ha fattori primi, almeno uno di essi deve essere minore o uguale alla radice quadrata. Se a * b = ce a <= b allora a <= sqrt (c) <= b.
David Johnstone,

8

dovresti dare un'occhiata ai probabili numeri primi . In particolare, date un'occhiata agli algoritmi randomizzati e al test di primalità di Miller-Rabin .

Per motivi di completezza potresti usare java.math.BigInteger :

public class PrimeGenerator implements Iterator<BigInteger>, Iterable<BigInteger> {

    private BigInteger p = BigInteger.ONE;

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public BigInteger next() {
        p = p.nextProbablePrime();
        return p;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public Iterator<BigInteger> iterator() {
        return this;
    }
}

@Test
public void printPrimes() {
    for (BigInteger p : new PrimeGenerator()) {
        System.out.println(p);
    }
}

2
Miller-Rabbin è molto veloce e il codice è molto semplice. Dandogli iterazioni sufficienti lo rende abbastanza affidabile da essere in concorrenza con guasti casuali della CPU in termini di probabilità di un falso positivo. Lo svantaggio dell'algoritmo è che capire perché funziona effettivamente è un compito difficile.
Brian

6

Per niente efficiente, ma forse il più leggibile:

public static IEnumerable<int> GeneratePrimes()
{
   return Range(2).Where(candidate => Range(2, (int)Math.Sqrt(candidate)))
                                     .All(divisor => candidate % divisor != 0));
}

con:

public static IEnumerable<int> Range(int from, int to = int.MaxValue)
{
   for (int i = from; i <= to; i++) yield return i;
}

In realtà solo una variazione di alcuni post qui con una formattazione migliore.


5

Copyrights 2009 di St.Wittum 13189 Berlino GERMANIA con licenza CC-BY-SA https://creativecommons.org/licenses/by-sa/3.0/

Il modo semplice ma più elegante per calcolare TUTTI I PRIMI sarebbe questo, ma in questo modo è lento e i costi di memoria sono molto più alti per numeri più alti perché usando la funzione facoltà (!) ... ma dimostra una variazione del Teorema di Wilson in un'applicazione per genera tutti i numeri primi dall'algoritmo implementato in Python

#!/usr/bin/python
f=1 # 0!
p=2 # 1st prime
while True:
    if f%p%2:
        print p
    p+=1
    f*=(p-2)

4

Usa un generatore di numeri primi per creare primes.txt e poi:

class Program
{
    static void Main(string[] args)
    {
        using (StreamReader reader = new StreamReader("primes.txt"))
        {
            foreach (var prime in GetPrimes(10, reader))
            {
                Console.WriteLine(prime);
            }
        }
    }

    public static IEnumerable<short> GetPrimes(short upTo, StreamReader reader)
    {
        int count = 0;
        string line = string.Empty;
        while ((line = reader.ReadLine()) != null && count++ < upTo)
        {
            yield return short.Parse(line);
        }
    }
}

In questo caso utilizzo Int16 nella firma del metodo, quindi il mio file primes.txt contiene numeri da 0 a 32767. Se vuoi estenderlo a Int32 o Int64, il tuo primes.txt potrebbe essere significativamente più grande.


3
Citando l'OP: "Non mi importa quale metodo viene utilizzato (ingenuo o setaccio o qualsiasi altra cosa), ma voglio che sia abbastanza breve e ovvio come funziona". Penso che la mia risposta sia perfettamente pertinente. È anche il metodo più veloce.
Darin Dimitrov,

14
Anche se dice "Non mi importa quale metodo ..." non credo che includa "apri un elenco di numeri primi". Sarebbe come rispondere alla domanda "come costruire un computer" con "comprare un computer". -1
stevenvh

8
Sarebbe più veloce se scrivessi effettivamente i numeri primi nel codice sorgente stesso, invece di leggerli da un file.
Daniel Daranas

3
Consuma molta memoria? più che leggere l'elenco completo dei numeri primi come testo nella ... memoria? Sai come funzionano le stringhe in .net?
jmservera

6
L'elenco dei numeri primi è un elenco infinito ma immutabile, quindi ha perfettamente senso utilizzare un elenco precalcolato fino al probabile limite superiore per l'applicazione. Perché perdere tempo a scrivere codice che potrebbe essere difettoso quando è disponibile un elenco pubblico corretto che può essere utilizzato per soddisfare i requisiti.
AndyM

4

Posso offrire la seguente soluzione C #. Non è affatto veloce, ma è molto chiaro cosa fa.

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    for (int n = 3; n <= limit; n += 2)
    {
        Int32 sqrt = (Int32)Math.Sqrt(n);

        if (primes.TakeWhile(p => p <= sqrt).All(p => n % p != 0))
        {
            primes.Add(n);
        }
    }

    return primes;
}

Ho tralasciato qualsiasi controllo - se il limite è negativo o inferiore a due (per il momento il metodo restituirà sempre almeno due come numero primo). Ma è tutto facile da risolvere.

AGGIORNARE

Con i seguenti due metodi di estensione

public static void Do<T>(this IEnumerable<T> collection, Action<T> action)
{
    foreach (T item in collection)
    {
        action(item);
    }
}

public static IEnumerable<Int32> Range(Int32 start, Int32 end, Int32 step)
{
    for (int i = start; i < end; i += step)
    }
        yield return i;
    }
}

puoi riscriverlo come segue.

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    Range(3, limit, 2)
        .Where(n => primes
            .TakeWhile(p => p <= Math.Sqrt(n))
            .All(p => n % p != 0))
        .Do(n => primes.Add(n));

    return primes;
}

È meno efficiente (perché la radice quadrata viene rivalutata abbastanza spesso) ma è anche un codice più pulito. È possibile riscrivere il codice per enumerare pigramente i numeri primi, ma questo ingombrerà un po 'il codice.


Sono quasi sicuro che il calcolo della radice quadrata sia ottimizzato dal compilatore JIT (quando compilato con l'ottimizzazione abilitata). Dovresti verificarlo esaminando l'assembly generato (l'IL è solo parzialmente ottimizzato e non è neanche lontanamente vicino all'ottimizzazione eseguita dal compilatore JIT. I giorni del loop hoisting e di altre micro ottimizzazioni sono finiti. In effetti, a volte si cerca di superare in astuzia il JIT può rallentare il codice.
Dave Black

4

Ecco un'implementazione di Sieve of Eratosthenes in C #:

    IEnumerable<int> GeneratePrimes(int n)
    {
        var values = new Numbers[n];

        values[0] = Numbers.Prime;
        values[1] = Numbers.Prime;

        for (int outer = 2; outer != -1; outer = FirstUnset(values, outer))
        {
            values[outer] = Numbers.Prime;

            for (int inner = outer * 2; inner < values.Length; inner += outer)
                values[inner] = Numbers.Composite;
        }

        for (int i = 2; i < values.Length; i++)
        {
            if (values[i] == Numbers.Prime)
                yield return i;
        }
    }

    int FirstUnset(Numbers[] values, int last)
    {
        for (int i = last; i < values.Length; i++)
            if (values[i] == Numbers.Unset)
                return i;

        return -1;
    }

    enum Numbers
    {
        Unset,
        Prime,
        Composite
    }

lo farei con un bool invece di enum ...
Letterman

3

Usando il tuo stesso algoritmo puoi farlo un po 'più breve:

List<int> primes=new List<int>(new int[]{2,3});
for (int n = 5; primes.Count< numberToGenerate; n+=2)
{
  bool isPrime = true;
  foreach (int prime in primes)
  {
    if (n % prime == 0)
    {
      isPrime = false;
      break;
    }
  }
  if (isPrime)
    primes.Add(n);
}

3

So che hai chiesto una soluzione non Haskell, ma la includo qui in quanto si riferisce alla domanda e anche Haskell è bello per questo tipo di cose.

module Prime where

primes :: [Integer]
primes = 2:3:primes'
  where
    -- Every prime number other than 2 and 3 must be of the form 6k + 1 or 
    -- 6k + 5. Note we exclude 1 from the candidates and mark the next one as
    -- prime (6*0+5 == 5) to start the recursion.
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n) $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0

Sì, anch'io sono un grande fan di Haskell (vorrei solo saperlo meglio)
David Johnstone,

3

Ho scritto una semplice implementazione di Eratostene in c # usando un po 'di LINQ.

Sfortunatamente LINQ non fornisce una sequenza infinita di int, quindi devi usare int.MaxValue :(

Ho dovuto memorizzare nella cache in un tipo anonimo il candidato sqrt per evitare di calcolarlo per ogni primo memorizzato nella cache (sembra un po 'brutto).

Uso un elenco di numeri primi precedenti fino a sqrt del candidato

cache.TakeWhile(c => c <= candidate.Sqrt)

e confronta ogni Int a partire da 2

.Any(cachedPrime => candidate.Current % cachedPrime == 0)

Ecco il codice:

static IEnumerable<int> Primes(int count)
{
    return Primes().Take(count);
}

static IEnumerable<int> Primes()
{
    List<int> cache = new List<int>();

    var primes = Enumerable.Range(2, int.MaxValue - 2).Select(candidate => new 
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

Un'altra ottimizzazione consiste nell'evitare di controllare i numeri pari e restituire solo 2 prima di creare l'elenco. In questo modo, se il metodo chiamante richiede solo 1 primo, eviterà tutto il casino:

static IEnumerable<int> Primes()
{
    yield return 2;
    List<int> cache = new List<int>() { 2 };

    var primes = Enumerable.Range(3, int.MaxValue - 3)
        .Where(candidate => candidate % 2 != 0)
        .Select(candidate => new
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

1
Vorrei conoscere abbastanza LINQ per apprezzare e capire meglio questa risposta :-) Inoltre, ho la sensazione che questa non sia un'implementazione del setaccio di Eratostene e che funzioni concettualmente allo stesso modo della mia funzione originale (trova la prossima numero che non è divisibile per nessuno dei numeri primi trovati in precedenza).
David Johnstone,

Sì, ma "trova il numero successivo che non è divisibile per nessuno dei numeri primi trovati in precedenza (più piccolo del numero)" è concettualmente simile al setaccio di eratostene. Se preferisci, posso rifattorizzarlo un po 'per renderlo più leggibile anche se non hai familiarità con LINQ. Hai familiarità con gli iteratori?
Maghis

La cosa che mi piace di questo approccio è che il numero primo successivo viene calcolato proprio quando il chiamante lo richiede, quindi cose come "prendi i primi n numeri primi" o "prendi i primi che sono più piccoli di n" diventano banali
Maghis

1
Grazie, ma posso capirlo abbastanza per sapere più o meno cosa sta facendo :-) Mi piace la valutazione pigra, ma non la definirei ancora un'implementazione del setaccio di Eratostene.
David Johnstone,

1

Per renderlo più elegante, dovresti rifattorizzare il tuo test IsPrime in un metodo separato e gestire il loop e gli incrementi al di fuori di quello.


1

L'ho fatto in Java utilizzando una libreria funzionale che ho scritto, ma poiché la mia libreria utilizza gli stessi concetti di Enumerazioni, sono sicuro che il codice sia adattabile:

Iterable<Integer> numbers = new Range(1, 100);
Iterable<Integer> primes = numbers.inject(numbers, new Functions.Injecter<Iterable<Integer>, Integer>()
{
    public Iterable<Integer> call(Iterable<Integer> numbers, final Integer number) throws Exception
    {
        // We don't test for 1 which is implicit
        if ( number <= 1 )
        {
            return numbers;
        }
        // Only keep in numbers those that do not divide by number
        return numbers.reject(new Functions.Predicate1<Integer>()
        {
            public Boolean call(Integer n) throws Exception
            {
                return n > number && n % number == 0;
            }
        });
    }
});

1

Questo è il più elegante a cui riesco a pensare con breve preavviso.

ArrayList generatePrimes(int numberToGenerate)
{
    ArrayList rez = new ArrayList();

    rez.Add(2);
    rez.Add(3);

    for(int i = 5; rez.Count <= numberToGenerate; i+=2)
    {
        bool prime = true;
        for (int j = 2; j < Math.Sqrt(i); j++)
        {
            if (i % j == 0)
            {
                    prime = false;
                    break;
            }
        }
        if (prime) rez.Add(i);
    }

    return rez;
}

Spero che questo aiuti a darti un'idea. Sono sicuro che questo può essere ottimizzato, tuttavia dovrebbe darti un'idea di come la tua versione potrebbe essere resa più elegante.

EDIT: Come notato nei commenti, questo algoritmo restituisce effettivamente valori errati per numberToGenerate <2. Voglio solo sottolineare che non stavo cercando di postargli un ottimo metodo per generare numeri primi (guarda la risposta di Henri per questo), Stavo precisando come rendere il suo metodo più elegante.


3
Questo restituisce un risultato sbagliato per numberToGenerate <2
Peter Smit

Questo è vero, tuttavia non stavo progettando un algoritmo, gli stavo solo mostrando come il suo metodo può essere reso più elegante. Quindi questa versione è altrettanto sbagliata di quella nella domanda di apertura.
David Božjak

1
Non mi è venuto in mente che fosse rotto per n = 1. Ho cambiato leggermente la domanda in modo che la funzione debba funzionare solo per n> 1 :-)
David Johnstone,

questo ammette quadrati di numeri primi come numeri primi.
Will Ness

1

Utilizzando la programmazione basata sul flusso in Java funzionale , ho ottenuto quanto segue. Il tipo Naturalè essenzialmente a BigInteger> = 0.

public static Stream<Natural> sieve(final Stream<Natural> xs)
{ return cons(xs.head(), new P1<Stream<Natural>>()
  { public Stream<Natural> _1()
    { return sieve(xs.tail()._1()
                   .filter($(naturalOrd.equal().eq(ZERO))
                           .o(mod.f(xs.head())))); }}); }

public static final Stream<Natural> primes
  = sieve(forever(naturalEnumerator, natural(2).some()));

Ora hai un valore, che puoi portare in giro, che è un flusso infinito di numeri primi. Puoi fare cose come questa:

// Take the first n primes
Stream<Natural> nprimes = primes.take(n);

// Get the millionth prime
Natural mprime = primes.index(1000000);

// Get all primes less than n
Stream<Natural> pltn = primes.takeWhile(naturalOrd.lessThan(n));

Una spiegazione del setaccio:

  1. Supponiamo che il primo numero nel flusso di argomenti sia primo e mettilo all'inizio del flusso di ritorno. Il resto del flusso di ritorno è un calcolo da produrre solo quando richiesto.
  2. Se qualcuno chiede il resto dello stream, chiama sieve sul resto dell'argomento stream, filtrando i numeri divisibili per il primo numero (il resto della divisione è zero).

È necessario disporre delle seguenti importazioni:

import fj.P1;
import static fj.FW.$;
import static fj.data.Enumerator.naturalEnumerator;
import fj.data.Natural;
import static fj.data.Natural.*;
import fj.data.Stream;
import static fj.data.Stream.*;
import static fj.pre.Ord.naturalOrd;

1

Personalmente penso che questa sia un'implementazione abbastanza breve e pulita (Java):

static ArrayList<Integer> getPrimes(int numPrimes) {
    ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
    int n = 2;
    while (primes.size() < numPrimes) {
        while (!isPrime(n)) { n++; }
        primes.add(n);
        n++;
    }
    return primes;
}

static boolean isPrime(int n) {
    if (n < 2) { return false; }
    if (n == 2) { return true; }
    if (n % 2 == 0) { return false; }
    int d = 3;
    while (d * d <= n) {
        if (n % d == 0) { return false; }
        d += 2;
    }
    return true;
}

1

Prova questa query LINQ, genera numeri primi come previsto

        var NoOfPrimes= 5;
        var GeneratedPrime = Enumerable.Range(1, int.MaxValue)
          .Where(x =>
            {
                 return (x==1)? false:
                        !Enumerable.Range(1, (int)Math.Sqrt(x))
                        .Any(z => (x % z == 0 && x != z && z != 1));
            }).Select(no => no).TakeWhile((val, idx) => idx <= NoOfPrimes-1).ToList();

1
// Create a test range
IEnumerable<int> range = Enumerable.Range(3, 50 - 3);

// Sequential prime number generator
var primes_ = from n in range
     let w = (int)Math.Sqrt(n)
     where Enumerable.Range(2, w).All((i) => n % i > 0)
     select n;

// Note sequence of output:
// 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
foreach (var p in primes_)
    Trace.Write(p + ", ");
Trace.WriteLine("");

0

Ecco un esempio di codice Python che stampa la somma di tutti i numeri primi inferiori a due milioni:

from math import *

limit = 2000000
sievebound = (limit - 1) / 2
# sieve only odd numbers to save memory
# the ith element corresponds to the odd number 2*i+1
sieve = [False for n in xrange(1, sievebound + 1)]
crosslimit = (int(ceil(sqrt(limit))) - 1) / 2
for i in xrange(1, crosslimit):
    if not sieve[i]:
        # if p == 2*i + 1, then
        #   p**2 == 4*(i**2) + 4*i + 1
        #        == 2*i * (i + 1)
        for j in xrange(2*i * (i + 1), sievebound, 2*i + 1):
            sieve[j] = True
sum = 2
for i in xrange(1, sievebound):
    if not sieve[i]:
        sum = sum + (2*i+1)
print sum

0

Il metodo più semplice è la prova ed errore: provi se un numero qualsiasi tra 2 en-1 divide il tuo primo candidato n.
Le prime scorciatoie sono ovviamente a) devi solo controllare i numeri dispari eb) devi solo controllare i divisori fino a sqrt (n).

Nel tuo caso, dove generi anche tutti i numeri primi precedenti nel processo, devi solo controllare se qualcuno dei primi nella tua lista, fino a sqrt (n), divide n.
Dovrebbe essere il più veloce che puoi ottenere per i tuoi soldi :-)

modifica
Ok, codice, l'hai chiesto. Ma ti avverto :-), questo è un codice Delphi veloce e sporco di 5 minuti:

procedure TForm1.Button1Click(Sender: TObject);
const
  N = 100;
var
  PrimeList: TList;
  I, J, SqrtP: Integer;
  Divides: Boolean;
begin
  PrimeList := TList.Create;
  for I := 2 to N do begin
    SqrtP := Ceil(Sqrt(I));
    J := 0;
    Divides := False;
    while (not Divides) and (J < PrimeList.Count) 
                        and (Integer(PrimeList[J]) <= SqrtP) do begin
      Divides := ( I mod Integer(PrimeList[J]) = 0 );
      inc(J);
    end;
    if not Divides then
      PrimeList.Add(Pointer(I));
  end;
  // display results
  for I := 0 to PrimeList.Count - 1 do
    ListBox1.Items.Add(IntToStr(Integer(PrimeList[I])));
  PrimeList.Free;
end;

1
E come lo esprimi in codice? :-)
David Johnstone,

0

Per scoprire i primi 100 numeri primi, si può considerare il seguente codice java.

int num = 2;
int i, count;
int nPrimeCount = 0;
int primeCount = 0;

    do
    {

        for (i = 2; i <num; i++)
        {

             int n = num % i;

             if (n == 0) {

             nPrimeCount++;
         //  System.out.println(nPrimeCount + " " + "Non-Prime Number is: " + num);

             num++;
             break;

             }
       }

                if (i == num) {

                    primeCount++;

                    System.out.println(primeCount + " " + "Prime number is: " + num);
                    num++;
                }


     }while (primeCount<100);

0

L'ho ottenuto dalla prima lettura di "Sieve of Atkin" su Wikki oltre a qualche pensiero precedente che ho dato a questo - Trascorro molto tempo a scrivere codice da zero e mi sono completamente azzerato sul fatto che le persone siano critiche nei confronti del mio codice molto denso e simile a un compilatore style + Non ho nemmeno fatto un primo tentativo di eseguire il codice ... molti dei paradigmi che ho imparato a usare sono qui, basta leggere e piangere, ottenere quello che puoi.

Sii assolutamente e totalmente sicuro di testare davvero tutto questo prima di qualsiasi utilizzo, di sicuro non mostrarlo a nessuno - è per leggere e considerare le idee. Devo far funzionare lo strumento di primalità, quindi è da qui che inizio ogni volta che devo far funzionare qualcosa.

Procurati una compilazione pulita, quindi inizia a rimuovere ciò che è difettoso: ho quasi 108 milioni di sequenze di tasti di codice utilizzabile che lo fanno in questo modo, ... usa quello che puoi.

Domani lavorerò alla mia versione.

package demo;
// This code is a discussion of an opinion in a technical forum.
// It's use as a basis for further work is not prohibited.
import java.util.Arrays;
import java.util.HashSet;
import java.util.ArrayList;
import java.security.GeneralSecurityException;

/**
 * May we start by ignores any numbers divisible by two, three, or five
 * and eliminate from algorithm 3, 5, 7, 11, 13, 17, 19 completely - as
 * these may be done by hand. Then, with some thought we can completely
 * prove to certainty that no number larger than square-root the number
 * can possibly be a candidate prime.
 */

public class PrimeGenerator<T>
{
    //
    Integer HOW_MANY;
    HashSet<Integer>hashSet=new HashSet<Integer>();
    static final java.lang.String LINE_SEPARATOR
       =
       new java.lang.String(java.lang.System.getProperty("line.separator"));//
    //
    PrimeGenerator(Integer howMany) throws GeneralSecurityException
    {
        if(howMany.intValue() < 20)
        {
            throw new GeneralSecurityException("I'm insecure.");
        }
        else
        {
            this.HOW_MANY=howMany;
        }
    }
    // Let us then take from the rich literature readily 
    // available on primes and discount
    // time-wasters to the extent possible, utilizing the modulo operator to obtain some
    // faster operations.
    //
    // Numbers with modulo sixty remainder in these lists are known to be composite.
    //
    final HashSet<Integer> fillArray() throws GeneralSecurityException
    {
        // All numbers with modulo-sixty remainder in this list are not prime.
        int[]list1=new int[]{0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,
        32,34,36,38,40,42,44,46,48,50,52,54,56,58};        //
        for(int nextInt:list1)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list1");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are  are
        // divisible by three and not prime.
        int[]list2=new int[]{3,9,15,21,27,33,39,45,51,57};
        //
        for(int nextInt:list2)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list2");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are
        // divisible by five and not prime. not prime.
        int[]list3=new int[]{5,25,35,55};
        //
        for(int nextInt:list3)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list3");//
            }
        }
        // All numbers with modulo-sixty remainder in
        // this list have a modulo-four remainder of 1.
        // What that means, I have neither clue nor guess - I got all this from
        int[]list4=new int[]{1,13,17,29,37,41,49,53};
        //
        for(int nextInt:list4)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list4");//
            }
        }
        Integer lowerBound=new Integer(19);// duh
        Double upperStartingPoint=new Double(Math.ceil(Math.sqrt(Integer.MAX_VALUE)));//
        int upperBound=upperStartingPoint.intValue();//
        HashSet<Integer> resultSet=new HashSet<Integer>();
        // use a loop.
        do
        {
            // One of those one liners, whole program here:
            int aModulo=upperBound % 60;
            if(this.hashSet.contains(new Integer(aModulo)))
            {
                continue;
            }
            else
            {
                resultSet.add(new Integer(aModulo));//
            }
        }
        while(--upperBound > 20);
        // this as an operator here is useful later in your work.
        return resultSet;
    }
    // Test harness ....
    public static void main(java.lang.String[] args)
    {
        return;
    }
}
//eof

0

Prova questo codice.

protected bool isPrimeNubmer(int n)
    {
        if (n % 2 == 0)
            return false;
        else
        {
            int j = 3;
            int k = (n + 1) / 2 ;

            while (j <= k)
            {
                if (n % j == 0)
                    return false;
                j = j + 2;
            }
            return true;
        }
    }
    protected void btn_primeNumbers_Click(object sender, EventArgs e)
    {
        string time = "";
        lbl_message.Text = string.Empty;
        int num;

        StringBuilder builder = new StringBuilder();

        builder.Append("<table><tr>");
        if (int.TryParse(tb_number.Text, out num))
        {
            if (num < 0)
                lbl_message.Text = "Please enter a number greater than or equal to 0.";
            else
            {
                int count = 1;
                int number = 0;
                int cols = 11;

                var watch = Stopwatch.StartNew();

                while (count <= num)
                {
                    if (isPrimeNubmer(number))
                    {
                        if (cols > 0)
                        {
                            builder.Append("<td>" + count + " - " + number + "</td>");
                        }
                        else
                        {
                            builder.Append("</tr><tr><td>" + count + " - " + number + "</td>");
                            cols = 11;
                        }
                        count++;
                        number++;
                        cols--;
                    }
                    else
                        number++;
                }
                builder.Append("</table>");
                watch.Stop();
                var elapsedms = watch.ElapsedMilliseconds;
                double seconds = elapsedms / 1000;
                time = seconds.ToString();
                lbl_message.Text = builder.ToString();
                lbl_time.Text = time;
            }
        }
        else
            lbl_message.Text = "Please enter a numberic number.";

        lbl_time.Text = time;

        tb_number.Text = "";
        tb_number.Focus();
    }

Ecco il codice aspx.

<form id="form1" runat="server">
    <div>
        <p>Please enter a number: <asp:TextBox ID="tb_number" runat="server"></asp:TextBox></p>

        <p><asp:Button ID="btn_primeNumbers" runat="server" Text="Show Prime Numbers" OnClick="btn_primeNumbers_Click" />
        </p>
        <p><asp:Label ID="lbl_time" runat="server"></asp:Label></p>
        <p><asp:Label ID="lbl_message" runat="server"></asp:Label></p>
    </div>
</form>

Risultati: 10000 numeri primi in meno di un secondo

100000 numeri primi in 63 secondi

Screenshot dei primi 100 numeri primi inserisci qui la descrizione dell'immagine


1
Provandolo, ho potuto valutare la sua efficacia e presentazione dei risultati: per favore, argomenta la sua eleganza.
barba grigia

Lo stile del risultato è solo una parte aggiunta. Consentitemi di discutere l'algoritmo per restituire vero / falso come numero primo. n% 2 eliminerà metà dei numeri perché i numeri pari sono sempre divisibili per 2. In altro codice, sto dividendo solo per numeri dispari, aumentando divisibile per due (quindi anche il divisibile successivo è dispari) fino alla metà di quel numero che è primo o no. Perché la metà, per non perdere tempo perché ci darà una risposta in frazione.
riz

log10 (63) ~ = 1,8, ovvero i tuoi dati mostrano un tasso di crescita di n ^ 1,8. Questo è molto lento; il setaccio ottimale delle implementazioni di Eratostene mostra ~ n ^ 1.01..1.05; divisione di prova ottimale ~ n ^ 1.35..1.45. In isPrimeNubmereffetti, implementate la divisione trilaterale non ottimale; le sue asintotiche peggioreranno a circa n ^ 2 (o anche al di sopra) quando proverai a generare ancora più numeri primi.
Will Ness
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.