Maneira mais elegante de gerar números primos [fechado]


84

Qual é a maneira mais elegante de implementar esta função:

ArrayList generatePrimes(int n)

Esta função gera os primeiros nprimos (editar: onde n>1), então generatePrimes(5)retornará um ArrayListcom {2, 3, 5, 7, 11}. (Estou fazendo isso em C #, mas estou feliz com uma implementação Java - ou qualquer outra linguagem semelhante para esse assunto (portanto, não Haskell)).

Eu sei como escrever essa função, mas quando eu fiz isso na noite passada, ela não acabou tão legal quanto eu esperava. Aqui está o que eu descobri:

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;
}

Não estou muito preocupado com a velocidade, embora não queira que seja obviamente ineficiente. Não me importo com qual método é usado (ingênuo ou crivo ou qualquer outra coisa), mas eu quero que seja bastante curto e óbvio como funciona.

Edit : Obrigado a todos que responderam, embora muitos não tenham respondido minha pergunta real. Para reiterar, eu queria um código limpo e agradável que gerasse uma lista de números primos. Já sei como fazer isso de várias maneiras diferentes, mas estou propenso a escrever códigos que não são tão claros quanto poderiam ser. Neste tópico, algumas boas opções foram propostas:

  • Uma versão melhor do que eu tinha originalmente (Peter Smit, jmservera e Rekreativc)
  • Uma implementação muito limpa da peneira de Eratóstenes (azul da estrela)
  • Use Java's BigIntegere nextProbablePrimepara código muito simples, embora eu não possa imaginar que seja particularmente eficiente (dfa)
  • Use o LINQ para gerar preguiçosamente a lista de primos (Maghis)
  • Coloque muitos números primos em um arquivo de texto e leia-os quando necessário (darin)

Edição 2 : implementei em C # alguns dos métodos fornecidos aqui e outro método não mencionado aqui. Todos eles encontram os primeiros n primos de forma eficaz (e eu tenho um método decente de encontrar o limite para fornecer às peneiras).


12
não, e também não é para o Projeto Euler :-)
David Johnstone

1
seria melhor se eu recortasse inumeráveis ​​<int> e cedesse um por um
Felice Pollano

4
O que eu gostaria de saber é qual é a forma menos elegante de gerar números primos. Estou pensando em algo envolvendo um banco de dados Access?
j_random_hacker de

1
Para comparação, um código de 2008 Haskell por BMeph : nubBy (((>1).).gcd) [2..]. Deixa apenas não duplicatas entre os números naturais, começando de 2, enquanto considera como duplicado qualquer número cujo gcdcom qualquer um dos números encontrados anteriormente seja maior que 1. É muito ineficiente, quadrático em número de primos produzidos. Mas é elegante .
Will Ness de

o mais elegante , IMO, é o de 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..]) ) }mas é claro que se baseia inteiramente em opiniões .
Will Ness em

Respostas:


48

Use a estimativa

pi(n) = n / log(n)

para o número de primos até n para encontrar um limite e, em seguida, use uma peneira. A estimativa subestima o número de primos até n de alguma forma, então a peneira será um pouco maior do que o necessário, o que está ok.

Esta é minha peneira Java padrão, calcula o primeiro milhão de números primos em cerca de um segundo em um laptop normal:

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
Essa é uma implementação muito boa da peneira de Eratóstenes
David Johnstone

1
não deveria ser suficiente fazer um loop enquanto i <= Math.sqrt(limit)no loop externo?
Christoph

1
@David Johnstone Não, pi (n) = n / log (n) subestima o número de primos até n, que vai na direção oposta. Fico feliz que você tenha encontrado uma aproximação muito melhor, no entanto.
starblue

1
se você estiver disposto a remover todos os múltiplos de 2 em seu próprio loop, você pode usar j + = 2 * i como seu incremento de loop para economizar algum tempo de execução extra, e você pode calcular isso uma vez usando um pequeno deslocamento
Nick Larsen

5
Ao substituir BitSetpor uma classe que implementa a fatoração de roda para 2, 3 e 5, ele se torna quase 3 vezes mais rápido.
starblue

37

Muito obrigado a todos que deram respostas úteis. Aqui estão minhas implementações de alguns métodos diferentes para localizar os primeiros n primos em C #. Os dois primeiros métodos são praticamente os que foram postados aqui. (Os nomes dos pôsteres estão ao lado do título.) Eu pretendo fazer a peneira de Atkin algum dia, embora eu suspeite que não seja tão simples quanto os métodos aqui atualmente. Se alguém puder ver alguma forma de melhorar algum desses métodos, eu adoraria saber :-)

Método Padrão ( Peter Smit , jmservera , Rekreativc )

O primeiro número primo é 2. Adicione a uma lista de primos. O próximo primo é o próximo número que não é divisível por nenhum número da lista.

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;
}

Isso foi otimizado apenas testando a divisibilidade até a raiz quadrada do número que está sendo testado; e testando apenas números ímpares. Isto pode ser ainda mais optimizado, testando apenas números da forma 6k+[1, 5], ou de 30k+[1, 7, 11, 13, 17, 19, 23, 29]ou assim por diante .

Peneira de Eratóstenes ( azul estrelado )

Isso encontra todos os primos para k . Para fazer uma lista dos primeiros n primos, primeiro precisamos aproximar o valor do n- ésimo primo. O método a seguir, conforme descrito aqui , faz isso.

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;
}

Peneira de Sundaram

Só descobri essa peneira recentemente, mas ela pode ser implementada de forma bastante simples. Minha implementação não é tão rápida quanto a peneira de Eratóstenes, mas é significativamente mais rápida do que o método ingênuo.

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;
}

Para sua informação - tive que alterar o contador de loop principal para "for (int i = 0; i * i <= limit && i * i> 0; i ++)" para evitar um estouro.
Jacobs Data Solutions

Esta implementação do Sieve of Sundaram é uma das poucas corretas por aí. A maioria deles usa limites errados para i e j ao calcular i+j+2*i*jlevando a uma saída incorreta.
jahackbeth

14

Ressuscitando uma velha questão, mas tropecei nela enquanto brincava com o LINQ.

Este código requer .NET4.0 ou .NET3.5 com extensões paralelas

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
Por que essa não é a resposta aceita? O código aqui é muito mais curto, mais elegante e muito mais rápido do que o código na resposta aceita. Gostaria de poder votar positivamente mais de uma vez!
Avrohom Yisroel

9

Você está no bom caminho.

Alguns comentários

  • primes.Add (3); faz com que esta função não funcione para número = 1

  • Você não precisa testar a divisão com números primários maiores que a raiz quadrada do número a ser testado.

Código sugerido:

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
testar aquele primo * primo <= curTest no loop em vez de pré-calcular a raiz quadrada provavelmente o tornará mais rápido e mais genérico (funcionará para bignums, etc)
yairchu

Por que usar a raiz quadrada? Qual é a base matemática para essa opção? Eu, provavelmente estupidamente, só dividiria por 2.
Luis Filipe

3
Porque se um número tem fatores primos, pelo menos um deles deve ser menor ou igual à raiz quadrada. Se a * b = ce a <= b, então a <= sqrt (c) <= b.
David Johnstone

8

você deve dar uma olhada nos primos prováveis . Em particular, dê uma olhada em Algoritmos Randomizados e teste de primalidade de Miller-Rabin .

Para fins de integridade, você pode apenas usar 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 é muito rápido e o código é muito simples. Fornecer iterações suficientes o torna confiável o suficiente para competir com uma falha aleatória de CPU em termos de probabilidade de um falso positivo. A desvantagem do algoritmo é que entender por que ele realmente funciona é uma tarefa difícil.
Brian

6

De forma alguma eficiente, mas talvez o mais legível:

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

com:

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

Na verdade, apenas uma variação de alguns posts aqui com formatação mais agradável.


5

Copyrights 2009 por St.Wittum 13189 Berlin ALEMANHA sob licença CC-BY-SA https://creativecommons.org/licenses/by-sa/3.0/

A maneira simples, mas mais elegante de calcular TODOS OS PRIMES seria esta, mas desta forma é lenta e os custos de memória são muito mais altos para números mais altos porque usar a função de faculdade (!) ... mas demonstra uma variação do Teorema de Wilson em uma aplicação para gerar todos os primos por algoritmo implementado em 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

Use um primo gerador de números para criar primes.txt e, em seguida:

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);
        }
    }
}

Neste caso, eu uso Int16 na assinatura do método, então meu arquivo primes.txt contém números de 0 a 32767. Se você quiser estender isso para Int32 ou Int64, seu primes.txt pode ser significativamente maior.


3
Citando o OP: "Não me importo qual método é usado (ingênuo ou crivo ou qualquer outra coisa), mas quero que seja bastante curto e óbvio como funciona". Acho que minha resposta é perfeitamente relevante. Também é o método mais rápido.
Darin Dimitrov

14
Mesmo que ele diga "Não me importo com qual método ...", acho que isso não inclui "abrir uma lista de primos". Isso seria como responder à pergunta "como construir um computador" ao "comprar um computador". -1
stevenvh

8
Seria mais rápido se você realmente escrevesse os números primos no próprio código-fonte, em vez de lê-los de um arquivo.
Daniel Daranas

3
Consome muita memória? mais do que ler a lista completa de primos como texto na ... memória? Você sabe como as strings funcionam em .net?
jmservera

6
A lista de primos é uma lista infinita, mas imutável, portanto, faz todo o sentido usar uma lista pré-calculada até o provável limite superior do aplicativo. Por que perder tempo escrevendo código que pode ser problemático quando existe uma lista pública correta disponível que pode ser usada para atender aos requisitos.
AndyM

4

Posso oferecer a seguinte solução C #. Não é nada rápido, mas é muito claro sobre o que faz.

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;
}

Eu deixei de fora quaisquer verificações - se o limite for negativo ou menor que dois (no momento, o método sempre retornará pelo menos dois como primo). Mas tudo isso é fácil de consertar.

ATUALIZAR

Com os dois métodos de extensão a seguir

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;
    }
}

você pode reescrevê-lo da seguinte maneira.

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;
}

É menos eficiente (porque a raiz quadrada é reavaliada com frequência), mas é um código ainda mais limpo. É possível reescrever o código para enumerar preguiçosamente os primos, mas isso bagunçará um pouco o código.


Tenho quase certeza de que o cálculo da raiz quadrada é otimizado pelo compilador JIT (quando compilado com a otimização habilitada). Você teria que verificar isso examinando o assembly gerado (o IL é apenas parcialmente otimizado e não está nem perto da otimização realizada pelo compilador JIT. Os dias de levantamento de loop e outras micro otimizações estão no fim. Na verdade, às vezes tentando ser mais esperto que o JIT pode tornar seu código lento .
Dave Black

4

Aqui está uma implementação do Sieve de Eratóstenes em 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
    }

Eu faria isso com um bool em vez de enum ...
Letterman

3

Usando o mesmo algoritmo, você pode fazer um pouco mais curto:

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

Eu sei que você pediu uma solução não-Haskell, mas estou incluindo isso aqui, pois se refere à pergunta e também Haskell é bonito para esse tipo de coisa.

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

Sim, também sou um grande fã de Haskell (gostaria de conhecê-lo melhor)
David Johnstone

3

Escrevi uma implementação simples de Eratóstenes em c # usando LINQ.

Infelizmente o LINQ não fornece uma sequência infinita de ints, então você deve usar int.MaxValue :(

Tive que armazenar em cache em um tipo anônimo o sqrt candidato para evitar calculá-lo para cada primo armazenado em cache (parece um pouco feio).

Eu uso uma lista de primos anteriores até sqrt do candidato

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

e verifique cada Int começando de 2 contra ele

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

Aqui está o código:

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;
    }
}

Outra otimização é evitar a verificação de números pares e retornar apenas 2 antes de criar a Lista. Dessa forma, se o método de chamada pedir apenas 1 primo, ele evitará toda a confusão:

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
Eu gostaria de conhecer o LINQ o suficiente para apreciar e compreender melhor esta resposta :-) Além disso, tenho a sensação de que esta não é uma implementação da peneira de Eratóstenes e que funciona conceitualmente da mesma forma que minha função original (encontre a próxima número que não é divisível por nenhum dos primos encontrados anteriormente).
David Johnstone

Sim, mas "encontre o próximo número que não é divisível por nenhum dos primos encontrados anteriormente (menor que o número)" é conceitualmente semelhante à peneira de eratóstenes. Se você preferir, posso refatorá-lo um pouco para torná-lo mais legível, mesmo se você não estiver familiarizado com o LINQ. Você está familiarizado com iteradores?
Maghis

O que eu gosto dessa abordagem é que o próximo primo é calculado apenas quando o chamador o pede, então coisas como "pegue os primeiros n primos" ou "pegue os primos menores que n" tornam-se triviais
Maghis

1
Obrigado, mas posso entender isso o suficiente para saber mais ou menos o que está fazendo :-) Gosto da avaliação preguiçosa, mas ainda não chamaria isso de uma implementação da peneira de Eratóstenes.
David Johnstone

1

Para torná-lo mais elegante, você deve refatorar seu teste IsPrime em um método separado e lidar com o loop e incrementos fora dele.


1

Fiz isso em Java usando uma biblioteca funcional que escrevi, mas como minha biblioteca usa os mesmos conceitos de Enumerações, tenho certeza de que o código é adaptável:

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

Este é o mais elegante que consigo pensar em pouco tempo.

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;
}

Espero que isso ajude você a ter uma ideia. Tenho certeza de que isso pode ser otimizado, mas deve dar uma ideia de como sua versão poderia ser mais elegante.

EDIT: Conforme observado nos comentários, este algoritmo realmente retorna valores errados para numberToGenerate <2. Eu só quero salientar que não estava tentando postar para ele um ótimo método para gerar números primos (veja a resposta de Henri para isso), Eu estava claramente apontando como seu método poderia ser mais elegante.


3
Este retorna um resultado errado para numberToGenerate <2
Peter Smit

Isso é verdade, no entanto, eu não estava projetando um algoritmo, estava apenas mostrando a ele como seu método pode ser mais elegante. Portanto, esta versão está igualmente errada como a da pergunta inicial.
David Božjak

1
Não me ocorreu que estava quebrado para n = 1. Mudei um pouco a pergunta para que a função só funcione para n> 1 :-)
David Johnstone

isso admite quadrados de primos como números primos.
Will Ness

1

Usando programação baseada em fluxo em Functional Java , eu vim com o seguinte. O tipo Naturalé essencialmente 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()));

Agora você tem um valor, que pode carregar, que é um fluxo infinito de números primos. Você pode fazer coisas assim:

// 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));

Uma explicação da peneira:

  1. Suponha que o primeiro número no fluxo de argumento seja primo e coloque-o na frente do fluxo de retorno. O resto do fluxo de retorno é um cálculo a ser produzido apenas quando solicitado.
  2. Se alguém perguntar pelo resto do fluxo, chame sieve no resto do fluxo do argumento, filtrando os números divisíveis pelo primeiro número (o resto da divisão é zero).

Você precisa ter as seguintes importações:

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

Pessoalmente, acho que esta é uma implementação bastante curta e limpa (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

Tente esta consulta LINQ, ela gera números primos conforme você esperava

        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

Aqui está um exemplo de código Python que imprime a soma de todos os primos abaixo de dois milhões:

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

O método mais simples é a tentativa e erro: você tenta se qualquer número entre 2 e n-1 divide seu primo candidato n.
Os primeiros atalhos são, obviamente, a) você só precisa verificar os números ímpares eb) você só precisa verificar os divisores até sqrt (n).

No seu caso, em que você também gera todos os primos anteriores no processo, você só precisa verificar se algum dos primos em sua lista, até sqrt (n), divide n.
Deve ser o mais rápido que você pode conseguir com seu dinheiro :-)

editar
Ok, código, você pediu. Mas estou avisando :-), este é um código Delphi de 5 minutos rápidos e sujos:

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 como você expressa isso em código? :-)
David Johnstone

0

Para descobrir os primeiros 100 números primos, considere o seguinte código 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

Eu consegui isso pela primeira vez em que li "Sieve of Atkin" no Wikki, além de algumas reflexões anteriores que dei a isso - eu gasto muito tempo codificando do zero e fico totalmente zerado com as pessoas que criticam minha codificação semelhante a um compilador e muito densa style + Eu nem fiz uma primeira tentativa de rodar o código ... muitos dos paradigmas que aprendi a usar estão aqui, apenas leia e chore, pegue o que puder.

Esteja absolutamente e totalmente certo de realmente testar tudo isso antes de qualquer uso, com certeza não mostre a ninguém - é para ler e considerar as idéias. Preciso fazer a ferramenta de primalidade funcionar, então é aqui que começo sempre que preciso fazer algo funcionar.

Faça uma compilação limpa e comece a remover o que está com defeito - eu tenho quase 108 milhões de pressionamentos de teclas de código utilizável fazendo isso dessa maneira ... use o que puder.

Vou trabalhar na minha versão amanhã.

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

Experimente este código.

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

Aqui está o código 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>

Resultados: 10.000 números primos em menos de um segundo

100.000 números primos em 63 segundos

Captura de tela dos primeiros 100 números primos insira a descrição da imagem aqui


1
Experimentando, pude avaliar sua eficácia e apresentação de resultados: por favor, argumente sua elegância.
barba cinza

O estilo do resultado é apenas uma parte adicionada. Deixe-me discutir o algoritmo para retornar verdadeiro / falso como número primo. n% 2 eliminará metade dos números porque os números pares são sempre divisíveis por 2. No código else, estou dividindo apenas por números ímpares, aumentando a divisão por dois (então o próximo divisível também é ímpar) até a metade desse número que é primo ou não. Por que metade, para não perder tempo porque nos dará resposta em fração.
riz

log10 (63) ~ = 1,8, ou seja, seus dados mostram uma taxa de crescimento de n ^ 1,8. Isso é muito lento; peneira ideal de implementações de Eratóstenes exibit ~ n ^ 1.01..1.05; divisão de teste ideal ~ n ^ 1.35..1.45. Você de isPrimeNubmerfato implementa a divisão tril subótima; suas assimptotípicas irão piorar para cerca de n ^ 2 (ou até acima disso) quando você tentar gerar ainda mais primos.
Will Ness
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.