Aqui está uma resposta que roda com memória constante, às custas da CPU. Esta não é uma boa resposta no contexto da pergunta original (ou seja, resposta durante uma entrevista). Mas se a entrevista durar 24 horas, não será tão ruim. ;)
A idéia é que, se eu tiver n, que é uma resposta válida, o próximo na sequência será n vezes alguma potência de dois, dividido por alguma potência de 5. Ou então n vezes uma potência de 5, dividido por um poder de dois. Desde que divida uniformemente. (... ou o divisor pode ser 1;) nesse caso, você está apenas multiplicando por 2 ou 5)
Por exemplo, para ir de 625 a 640, multiplique por 5 ** 4/2 ** 7. Ou, geralmente, multiplique por algum valor de 2 ** m * 5 ** n
para alguns m, n onde um é positivo e outro é negativo ou zero, e o valor multiplicador divide o número uniformemente.
Agora, a parte complicada é encontrar o multiplicador. Mas sabemos que a) o divisor deve dividir o número uniformemente, b) o multiplicador deve ser maior que um (os números continuam aumentando) ec) se escolhermos o multiplicador mais baixo maior que 1 (ie 1 <f <todos os outros f's ), esse é certamente o próximo passo. O passo seguinte será o passo mais baixo.
A parte desagradável é encontrar o valor de m, n. Existem apenas possibilidades de log (n), porque existem apenas 2 ou 5 para desistir, mas eu tive que adicionar um fator de -1 a +1 como uma maneira desleixada de lidar com o arredondamento. Portanto, precisamos iterar através de O (log (n)) a cada passo. Portanto, é O (n log (n)) em geral.
A boa notícia é que, como leva um valor e encontra o próximo valor, você pode começar em qualquer lugar da sequência. Portanto, se você deseja o próximo após 1 bilhão, ele pode ser encontrado através da iteração entre 2/5 ou 5/2 e escolhendo o menor multiplicador maior que 1.
(Pitão)
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
Eu validei os primeiros 10.000 números que isso gera com relação aos primeiros 10.000 gerados pela solução da lista classificada e funciona pelo menos até agora.
Aliás, o próximo, depois de um trilhão, parece ser 1.024.000.000.000.
...
Hum. Posso obter O (n) desempenho - O (1) por valor (!) - e O (log n) uso de memória tratando best()
como uma tabela de pesquisa que eu estendo gradualmente. No momento, ele economiza memória repetindo cada vez, mas está fazendo muitos cálculos redundantes. Mantendo esses valores intermediários - e uma lista de valores mínimos - eu posso evitar o trabalho duplicado e acelerar muito. No entanto, a lista de valores intermediários aumentará com n, daí a memória O (log n).