Nos comentários de um dos
posts anteriores sobre a peneira de Eratóstenes, esse pequeno algoritmo da
Wikipedia foi mencionado:
Algoritmo 1:1: i := 2, 3, 4, ..., n: 2: lp[i] = 0: 3: lp[i] := i 4: pr[] += {i} 5: p pr p ≤ lp[i] p*i ≤ n: 6: lp[p*i] := p : lp - n pr - n.
O algoritmo é simples, mas não para todos parecia óbvio. O principal problema é que não há evidências na Wikipedia, e o
link para a fonte (
pdf ) contém um algoritmo bastante diferente do acima.
Neste post, tentarei, espero, ser possível provar que esse algoritmo não apenas funciona, mas também por complexidade linear.
Uma observação sobre a complexidade do algoritmoEmbora esse algoritmo seja assintoticamente mais rápido que o
crivo padrão
de Eratóstenes em O (n log log n), requer muito mais memória. Portanto, para n verdadeiramente grande, sempre que esse algoritmo brilha em toda a sua glória, ele não é aplicável. No entanto, é muito útil mesmo para n pequeno, se você precisar não apenas encontrar todos os números primos, mas também fatorar rapidamente todos os números até n.
Definições
m a t h c a l P - muitos números primos.
l p ( x ) - divisor principal mínimo de um número:
lp (x) = min \ {p \ in \ mathcal {P} \ \ vert \ p \ vert x \}lp (x) = min \ {p \ in \ mathcal {P} \ \ vert \ p \ vert x \}rd(x) - outros fatores:
rd(x)=x/lp(x)Observe que todas as definições acima são para
x ge2 .
Algumas propriedades óbvias das funções apresentadas acima, que serão usadas posteriormente:
lp(a timesb)=min(lp(a),lp(b))rd(x)<xp in mathcalP=>lp(p)=pProva
Lema 1 :
lp(x) lelp(rd(x)), forallx notin mathcalP,x>1Prova : Porque qualquer divisor
rd(x) também um divisor
x e
lp(x) não é superior a qualquer divisor
x então
lp(x) não excede nenhum divisor
rd(x) incluindo o menor deles. O único problema com esta afirmação é se
rd(x) não possui divisores simples, mas isso só é possível se
x in mathcalP , que é excluído na condição do lema.
quadradoVamos
E = \ {(lp (x), rd (x)) \ vert \ forall x \ notin \ mathcal {P}, 2 \ le x \ le n \}Porque
lp(x) vezesrd(x)=x (por definição
rd() ), se nos dessem muito
E então poderíamos calcular
lp() para todos os números compostos até n. Isso é feito, por exemplo, pelo seguinte algoritmo:
Algoritmo 2: 1: (l,r) E: 2: lp[l*r] := l;
Note que
vertE vert len portanto, o algoritmo 2 acima funciona para complexidade linear.
Além disso, vou provar que o algoritmo 1 da Wikipedia na verdade apenas repete todos os elementos desse conjunto, porque pode ser parametrizado de uma maneira diferente.
Vamos
E '= \ {(p, r) \ vert p \ in \ mathcal {P}, 2 \ le r <n, p \ le lp (r), p \ vezes r \ le n \}Lema 2 :
E=E′Prova :
Vamos
(a,b) emE =>
existex nãoem mathcalP, 2 arquivox arquivon meioa=lp(x),b=rd(x) .
Por definição
lp(),rd() :
a in mathcalP ,
a vezesb=x . Pelo lema 1,
a lelp(b) .
porque
rd(x)<x então
b<n .
Desde
x notin mathcalP ,
b ge2 .
Além disso, por definição
E ,
x len portanto
a vezesb len .
Todas as 4 condições
E′ meios cumpridos
(a,b) emE′ =>
E subconjuntoE′ .
Vamos
(a,b) emE′ . Vamos
x=a vezesb .
Por definição
E′ ,
a in mathcalP . Meios
a - divisão simples
x .
lp(x)=lp(a vezesb)=min(lp(a),lp(b))=min(a,lp(b))Porque
a lelp(b) então
lp(x)=a. Meios
b=rd(x).Por definição,
x=a timesb len. Obviamente também
x=a timesb ge2.Todas as condições para
E concluído =>
E′ subconjuntoE.Portanto,
E=E′. quadradoAgora resta escolher os corretos
r e
p da definição do conjunto
E′ e aplique o algoritmo 2. É exatamente isso que o algoritmo 1 faz (apenas a variável i é usada em vez de r). Mas há um problema! Para classificar todos os elementos de um conjunto
E′ calcular
lp, precisamos saber
lp, porque existe uma condição na definição
p lelp(r) .
Felizmente, esse problema contorna a ordem de classificação correta. É necessário resolver
r no loop externo e
p - por dentro. Então
lp(r) já será calculado. Este fato é comprovado pelo seguinte teorema.
Teorema 1:O algoritmo 1 suporta a seguinte invariante: Após a iteração do loop externo para i = k, todas as primas até k inclusive serão destacadas em pr. Também será contado
lp() para todos
x notin mathcalP meiox len, rd(x) lek na matriz lp.
Prova :
Provamos por indução. Para k = 2, a invariante é verificada manualmente. O único primo 2 será adicionado à lista pr, porque a matriz lp [] é inicialmente preenchida com zeros. Além disso, o único número composto em que
rd(x) le2 - este é 4. É fácil verificar se o loop interno será executado exatamente uma vez (para n> 3) e executará corretamente lp [4]: = 2.
Agora, suponha que o invariante seja válido para a iteração i = k-1. Vamos provar que também será válido para a iteração i = k.
Para fazer isso, basta verificar se o número i, se for primo, será adicionado à lista pr e se
lp() será contado para todos os números compostos
x n, incluindo
rd(x)=i. São esses números da invariante para k que ainda não estão cobertos pela invariante para k-1.
Se i for simples, lp [i] será 0, porque a única operação de gravação no array lp, que teoricamente poderia calcular lp [i] (na linha 6), sempre grava em índices compostos, porque p * i (para i> 1) - sempre composto. Portanto, o número i será adicionado à lista de números primos. Além disso, na linha 3, lp [i] será contado.
Se i for composto, no início da iteração lp [i] já será calculado pela invariante para i = k-1, porque
rd(i)<i ou
rd(i) lei−1, portanto, eu cai sob a condição invariável na iteração anterior. Portanto, o número composto i não será adicionado à lista de números primos.
Além disso, tendo o lp [i] correto e todos os números primos até i inclusive, o loop nas linhas 5-6 iterará sobre todos os elementos
(p,i) emE′ em que a segunda parte é i:
- p emP, porque é da lista pr
- p lelp(i), por condição para parar o ciclo
- p vezesi len, por condição para parar o ciclo
- i<n, segue de p vezesi len, p>1
Todos os primos necessários na lista pr são, porque apenas números até
lp(i) lei . Portanto, os valores lp [] serão calculados para todos os números compostos
x para qual
rd(x)=i . Esses são exatamente todos os números que estavam faltando durante a transição da invariante para k-1 para a invariante para k.
Portanto, o invariante vale para qualquer i = 2..n.
quadradoPela invariante do Teorema 1 para i = n, todos os números primos até n e todos os lp [] serão calculados pelo algoritmo 1.
Além disso, como nas linhas 5-6, vários elementos do conjunto são classificados
E , o loop interno total não executará mais
vertE vert<n operações. A operação de verificação no loop será executada sem problemas
vertE vert+n−1<2n vezes (
vertE vert vezes retornará verdadeiro e n-1 vezes, para cada i retornará falso). Todas as operações restantes são aninhadas em um ciclo em i de 2 para n.
Daqui resulta que a complexidade do algoritmo 1 é O (n).