Pergunta

Para uma biblioteca, eu preciso armazenar os primeiros números primos até um limite L. Esta coleção deve ter um O (1) tempo de pesquisa (para verificar se um número é primo ou não) e deve ser fácil, dado um número, para encontrar o próximo número primo (assumindo que é menor do que L).

Tendo em conta que L é fixo, uma Eratostene peneira para gerar a lista é muito bem. Agora, eu uso uma matriz booleana embalado para armazenar a lista, que contém apenas entradas para números ímpares entre 3 e L (inclusive). Isso leva (L-2) / 2 bits de memória. Eu gostaria de ser capaz de aumentar estaticamente L sem usar mais memória.

Existe uma estrutura de dados usando menos memória com propriedades semelhantes? Ou pelo menos com o tempo de pesquisa constante? (Números ímpares podem ser enumerados até chegarmos a uma prime)

(a língua que eu escrevi isso em é Fator mas esta questão seria a mesma em qualquer língua que tem built-in ou facilmente programável bit embalado matrizes)

Foi útil?

Solução

Você pode verificar explicitamente números mais privilegiados para a redundância de remoção.

No momento que você faz isso só para dois, verificando divisibilidade por dois explícita e, em seguida, armazenar apenas para números ímpares se eles são primos.

Para 2 e 3 você começa restos de 0 a 5, sendo que apenas 1 e 5 não são divisíveis por dois ou três e pode levar a um número primo, então você está para baixo a 1/3.

Para 2, 3 e 5, você obter 8 números fora de 30, o que é bom para armazenar em um byte.

Isto é explicado em mais detalhes aqui .

Outras dicas

Uma alternativa para os mapas de bits e rodas embalados - mas igualmente eficazes em certos contextos - é armazenar as diferenças entre primos consecutivos. Se você deixar de fora o número 2, como de costume, em seguida, todas as diferenças são ainda. Armazenar diferença / 2 você pode obter até 2 ^ 40ish regiões (pouco antes 1999066711391), utilizando variáveis ??de tamanho de byte.

Os números primos até 2 ^ 32 requerem apenas 194 Mbytes, em comparação com 256 Mbyte para um probabilidades somente bitmap embalado. Iteração sobre primos armazenados-delta é muito mais rápido do que para o armazenamento de rodas, que inclui a roda de módulo-2 conhecido como mapa de bits probabilidades-only.

Para intervalos de 1999066711391 em diante, o tamanho da célula grande ou armazenamento de comprimento variável são necessários. O último pode ser extremamente eficiente, mesmo se esquemas muito simples são utilizados (por exemplo, manter-se adicionar até um byte <255 tenha sido adicionado, como em LZ4 estilo de compressão), por causa da extremamente baixa frequência de lacunas mais de 510/2.

Por razões de eficiência, é melhor dividir a faixa em seções (páginas) e gerenciá-los estilo B-Tree.

Entropy-codificar as diferenças (Huffmann ou aritméticas Coding) corta os requisitos de armazenamento permanente para um pouco menos de metade, que fica perto do ideal teórico e melhor do que listas ou rodas comprimidos usando os melhores packers disponíveis.

Se os dados são armazenados descompactado, em seguida, ainda é muito mais compacto do que arquivos de números binários ou de texto, por uma ordem de magnitude ou mais. Com um índice de estilo B-Tree no lugar, é fácil simplesmente mapear seções na memória quando necessário e interagir sobre eles em uma velocidade impressionante.

No momento você está tratando 2 como caso especial e, em seguida, ter um array onde cada número ímpar é mapeado para um elemento na matriz (com alguns números ímpares sendo prime). Pode-se melhorar este por tratamento 2 e 3 como casos especiais, reconhecendo que o resto dos números primos são na forma 6n + 1 ou 6n-1 (que é para todos os primos p onde p> 3, p mod 6 = 1 ou 5). Isto pode ser mais generalizada - veja Wikipedia . Para todos os números primos p> 5, p mod 30 = 1, 7, 11, 13, 17, 19, 23 ou 29. Você poderia continuar com isso e reduzir a memória necessária à custa do tempo de processamento (embora ainda será O (1), apenas um O mais lento (1)).

Talvez um trie estrutura de dados que contém apenas os números primos é o que você está procurando . Em vez de usar personagens como índices você poderia usar os dígitos inteiros. Uma implementação desta são Judy-matriz s.

Altough, eles não atender às suas O (1) exigência, eles são extremamente eficiente para a memória para chaves semelhantes (como a maioria de partes de números são) e muito rápido para procurar com um O (m) (m = chave- comprimento) no máximo.

Se você olhar para cima para um primo na árvore de pré-gerado, você pode andar a árvore até encontrá-lo ou você já está no nó que está ao lado do precedente e seguinte prime.

Dado que a memória é tão barato, eu não acho que você pode fazer muito melhor a partir de uma perspectiva de velocidade do que o seu esquema existente.

Se há uma solução melhor, então eu diria que ele iria aproveitar a Prime Number Theorem que mostra que, como L se torna maior, o limite de

p (L) / (L / ln (L)) se aproxima de 1.

Talvez uma solução melhor seria ter uma solução de embalagem adaptativa em uma estrutura de dados como uma espécie de lista Skip .

Como sobre algum tipo de tabela hash?

Você precisaria de uma boa função hash (algo como n mod p, onde p não é um múltiplo de qualquer um dos q mais baixos números primos - escolha q suficientemente elevado, a fim de minimizar o número de colisões).

Como sobre uma árvore Intervalo? http://www.geeksforgeeks.org/interval-tree/

Pode não ser O (1), mas é muito rápido. Como talvez O (log (p (n))) em que p (n) é o número de números primos até o número n. Desta forma, você vai a memória que você precisa será proporcional ao número de apenas primos, cortando muito o custo de memória.

Por exemplo, suponha que você encontrar um primo em p1 palavra a dizer e, em seguida, o próximo a p2, Inserir intervalo (p1, p2) e assim por diante e quando você executa uma busca por qualquer número nesse intervalo que vai voltar este intervalo e você pode retornar p2 qual seria a resposta no seu caso.

Se você pode descobrir quais são Mersenne ou outros números primos facilmente representados, você pode ser capaz de salvar alguns pedaços usando a representação com uma bandeira para os números aplicáveis.

Além disso, como sobre o armazenamento dos números como a diferença entre o número anterior? Em seguida, o tamanho não deve subir tão rápido (mas lookup seria lenta). Combinando com a abordagem acima, você pode armazenar números primos de Mersenne e a diferença a partir do último Primo de Mersenne.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top