Extracción de un número y volver a insertar todos sus múltiplos (por los números primos en el conjunto) en una cola de prioridad se mal (en el sentido de la pregunta) - es decir, produce la secuencia correcta pero ineficientemente así.
Es ineficiente de dos maneras: primero, produce en exceso la secuencia; segundo, cada operación PriorityQueue incurre en un costo adicional (las operaciones remove_top
y insert
generalmente no son ambas O (1), ciertamente no en ninguna implementación de PriorityQueue basada en la lista o en el árbol).
La eficiente algoritmo O (n) mantiene punteros de nuevo en la propia secuencia, ya que se está produciendo, para encontrar y añadir el siguiente número en O (1). En pseudocódigo:
return array h where
h[0]=1; n=0; ps=[2,3,5, ... ]; // base primes
is=[0 for each p in ps]; // indices back into h
xs=[p for each p in ps] // next multiples: xs[k]==ps[k]*h[is[k]]
repeat:
h[++n] := minimum xs
for each (i,x,p) in (is,xs,ps):
if(x==h[n])
{ x := p*h[++i]; } // advance the minimal multiple/pointer
para cada múltiplo mínimo que avanza su puntero, mientras que al mismo tiempo calculando su próximo valor múltiple.Esto implementa de manera muy efectiva una PriorityQueue pero con distinciones cruciales: es antes de el punto final, no después; no crea ningún almacenamiento adicional a excepción de la secuencia misma; y su tamaño es constante (solo k números, para k primos de base) mientras que el tamaño de PriorityQueue pasa a medida que progresamos a lo largo de la secuencia (en el caso de la secuencia de Hamming, basado en el conjunto de primos, como n 2/3, para n números de la secuencia).
El clásico Hamming sequence in Haskell es esencialmente el mismo algoritmo:
h = 1 : map (2*) h `union` map (3*) h `union` map (5*) h
union [email protected](x:xs) [email protected](y:ys) = case compare x y of LT -> x : union xs b
EQ -> x : union xs ys
GT -> y : union a ys
Podemos generar los smooth numbers para primos de base arbitrarias usando la función foldi
(ver Wikipedia) para plegar las listas en un árbol-como moda para la eficiencia, la creación de un árbol de tamaño fijo de las comparaciones:
smooth base_primes = h where -- strictly increasing base_primes NB!
h = 1 : foldi g [] [map (p*) h | p <- base_primes]
g (x:xs) ys = x : union xs ys
foldi f z [] = z
foldi f z (x:xs) = f x (foldi f z (pairs f xs))
pairs f (x:y:t) = f x y : pairs f t
pairs f t = t
También es posible calcular directamente un rebanada de secuencia de Hamming alrededor de su ésimo miembro n en O (n 2/3) tiempo, por enumeración directa de los triples y la evaluación de sus valores a través de logaritmos, logval(i,j,k) = i*log 2+j*log 3+k*log 5
. Este Ideone.com test entry calcula 1 billionth Hamming number en 1,12 0,05 segundo (08/18/2016: aceleración principal debido al uso de Int
en lugar del predeterminado Integer
cuando sea posible, incluso en 32 bits; adicionales 20% gracias a la Tweak sugeridas por @GordonBGood, bajando la complejidad del tamaño de banda a O (n 1/3)).
Esto se discute un poco más en this answer, donde también se encuentra su plena atribución:
slice hi w = (c, sortBy (compare `on` fst) b) where -- hi is a top log2 value
lb5=logBase 2 5 ; lb3=logBase 2 3 -- w<1 (NB!) is (log2 width)
(Sum c, b) = fold -- total count, the band
[ (Sum (i+1), -- total triples w/this j,k
[ (r,(i,j,k)) | frac < w ]) -- store it, if inside the band
| k <- [ 0 .. floor (hi /lb5) ], let p = fromIntegral k*lb5,
j <- [ 0 .. floor ((hi-p)/lb3) ], let q = fromIntegral j*lb3 + p,
let (i,frac) = pr (hi-q) ; r = hi - frac -- r = i + q
] -- (sum . map fst &&& concat . map snd)
pr = properFraction
Esto se puede generalizar para k base de los primos, así, probablemente se ejecuta en O (n (k -1)/k) tiempo.
ver this SO entry para un desarrollo posterior importante. también, this answer es interesante. y otro related answer.
Mejor preguntar esto en math.stackexchange.com –
@HighPerformanceMark sí, pero en orden creciente – nims
Mira esta [pregunta relacionada] (http://stackoverflow.com/questions/7333657/how-to-generate-numbers -don-sus-factores-primos-pero-con-desconocido-exponentes). La respuesta aceptada da O (n) código Python similar a mi respuesta aquí, que se puede adaptar a "bases" arbitrarias (primos establecidos). –