Ecco una risposta che funziona con memoria costante, a scapito della CPU. Questa non è una buona risposta nel contesto della domanda originale (ad esempio risposta durante un'intervista). Ma se l'intervista dura 24 ore, allora non è così male. ;)
L'idea è che se ho n che è una risposta valida, allora la prossima nella sequenza sarà n volte una potenza di due, divisa per una potenza di 5. Oppure n volte una potenza di 5, diviso per un potere di due. Purché si divida in modo uniforme. (... o il divisore può essere 1;) nel qual caso stai solo moltiplicando per 2 o 5)
Ad esempio, per passare da 625 a 640, moltiplicare per 5 ** 4/2 ** 7. Oppure, più in generale, moltiplicare per un valore di 2 ** m * 5 ** n
per alcuni m, n dove uno è positivo e uno è negativo o zero e il moltiplicatore divide il numero in modo uniforme.
Ora, la parte difficile è trovare il moltiplicatore. Ma sappiamo a) il divisore deve dividere il numero in modo uniforme, b) il moltiplicatore deve essere maggiore di uno (i numeri continuano ad aumentare) e c) se selezioniamo il più basso moltiplicatore maggiore di 1 (cioè 1 < f < tutte le altre f's), quindi è garantito che sarà il nostro prossimo passo. Il passo successivo sarà il passo più basso.
La parte cattiva è trovare il valore di m, n. Ci sono solo possibilità log (n), perché ci sono solo così tanti 2 o 5 da rinunciare, ma ho dovuto aggiungere un fattore da -1 a +1 come modo sciatto per affrontare il roundoff. Quindi dobbiamo solo ripetere su O (log (n)) ogni passo. Quindi è O (n log (n)) nel complesso.
La buona notizia è che, poiché accetta un valore e trova il valore successivo, puoi iniziare in qualsiasi punto della sequenza. Quindi se vuoi il successivo dopo 1 miliardo, puoi trovarlo semplicemente iterando attraverso i 2/5 o 5/2 e selezionando il moltiplicatore più piccolo maggiore di 1.
(python)
MAX = 30
F = - math.log(2) / math.log(5)
def val(i, j):
return 2 ** i * 5 ** j
def best(i, j):
f = 100
m = 0
n = 0
max_i = (int)(math.log(val(i, j)) / math.log(2) + 1) if i + j else 1
#print((val(i, j), max_i, x))
for mm in range(-i, max_i + 1):
for rr in {-1, 0, 1}:
nn = (int)(mm * F + rr)
if nn < -j: continue
ff = val(mm, nn)
#print(' ' + str((ff, mm, nn, rr)))
if ff > 1 and ff < f:
f = ff
m = mm
n = nn
return m, n
def detSeq():
i = 0
j = 0
got = [val(i, j)]
while len(got) < MAX:
m, n = best(i, j)
i += m
j += n
got.append(val(i, j))
#print('* ' + str((val(i, j), m, n)))
#print('- ' + str((v, i, j)))
return got
Ho convalidato i primi 10.000 numeri che questo genera contro i primi 10.000 generati dalla soluzione elenco ordinato, e funziona almeno fino a quel punto.
BTW il prossimo dopo un trilione sembra essere 1.024.000.000.000.
...
Hm. Posso ottenere prestazioni O (n) - O (1) per valore (!) - e O (log n) utilizzo della memoria trattando best()
come tabella di ricerca che estendo in modo incrementale. In questo momento salva la memoria iterando ogni volta, ma sta facendo molti calcoli ridondanti. Tenendo quei valori intermedi - e una lista di valori minimi - posso evitare il lavoro duplicato & accelerare molto. Tuttavia, l'elenco dei valori intermedi crescerà con n, quindi la memoria O (log n).