Ecco una risposta che funziona con memoria costante, a spese della CPU. Questa non è una buona risposta nel contesto della domanda originale (ovvero la risposta durante un'intervista). Ma se l'intervista dura 24 ore, non è poi così male. ;)
L'idea è che se ho n che è una risposta valida, allora il prossimo nella sequenza sarà n volte un potere di due, diviso per un potere di 5. Oppure n volte un potere di 5, diviso per un potenza di due. A condizione che divida uniformemente. (... o il divisore può essere 1;) nel qual caso stai solo moltiplicando per 2 o 5)
Ad esempio, per andare da 625 a 640, moltiplicare per 5 ** 4/2 ** 7. O, 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 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) ec) se scegliamo il moltiplicatore più basso maggiore di 1 (cioè 1 <f <tutti gli altri f ), questo è garantito per essere il nostro prossimo passo. Il passo successivo sarà il passo più basso.
La parte cattiva sta trovando il valore di m, n. Ci sono solo possibilità di log (n), perché ci sono solo tanti 2 o 5 da rinunciare, ma ho dovuto aggiungere un fattore da -1 a +1 come un modo sciatto per gestire il roundoff. Quindi dobbiamo solo scorrere attraverso O (log (n)) ogni passaggio. Quindi è O (n log (n)) in generale.
La buona notizia è che, poiché prende un valore e trova il valore successivo, puoi iniziare ovunque nella sequenza. Quindi, se vuoi il prossimo dopo 1 miliardo, puoi semplicemente trovarlo scorrendo i 2/5 o i 5/2 e selezionando il moltiplicatore più piccolo maggiore di 1.
(pitone)
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 generati da questo rispetto ai primi 10.000 generati dalla soluzione dell'elenco ordinato e funziona almeno fino a quel punto.
A proposito il prossimo dopo un trilione sembra essere 1.024.000.000.000.
...
Hm. Posso ottenere O (n) prestazioni - O (1) per valore (!) - e O (log n) utilizzo della memoria trattando best()
come una tabella di ricerca che estendo in modo incrementale. In questo momento risparmia memoria ripetendo ogni volta, ma sta facendo molti calcoli ridondanti. Mantenendo quei valori intermedi - e un elenco di valori minimi - posso evitare il lavoro duplicato e accelerarlo molto. Tuttavia, l'elenco dei valori intermedi crescerà con n, quindi la memoria O (log n).