Aquí hay una respuesta que se ejecuta con memoria constante, a expensas de la CPU. Esta no es una buena respuesta en el contexto de la pregunta original (es decir, la respuesta durante una entrevista). Pero si la entrevista dura 24 horas, entonces no está tan mal. ;)
La idea es que si tengo n, que es una respuesta válida, entonces la siguiente en la secuencia será n veces alguna potencia de dos, dividida por alguna potencia de 5. O bien n veces una potencia de 5, dividida por un poder de dos. Siempre que se divida de manera uniforme. (... o el divisor puede ser 1;) en cuyo caso solo está multiplicando por 2 o 5)
Por ejemplo, para pasar de 625 a 640, multiplique por 5 ** 4/2 ** 7. O, más generalmente, multiplique por algún valor de 2 ** m * 5 ** npara algunos m, n donde uno es positivo y uno es negativo o cero, y el multiplicador divide el número de manera uniforme.
Ahora, la parte difícil es encontrar el multiplicador. Pero sabemos que a) el divisor debe dividir el número de manera uniforme, b) el multiplicador debe ser mayor que uno (los números siguen aumentando) yc) si elegimos el multiplicador más bajo mayor que 1 (es decir, 1 <f <todos los demás f) ), entonces ese es nuestro próximo paso. El siguiente paso será el más bajo.
La parte desagradable es encontrar el valor de m, n. Solo hay posibilidades de log (n), porque solo hay que renunciar a 2 o 5, pero tuve que agregar un factor de -1 a +1 como una forma descuidada de lidiar con el redondeo. Entonces solo tenemos que iterar a través de O (log (n)) en cada paso. Entonces es O (n log (n)) en general.
La buena noticia es que, dado que toma un valor y encuentra el siguiente valor, puede comenzar en cualquier parte de la secuencia. Entonces, si desea el siguiente después de mil millones, puede encontrarlo iterando a través de los 2/5 o 5/2 y seleccionando el multiplicador más pequeño mayor que 1.
(pitón)
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
Validé los primeros 10,000 números que esto genera contra los primeros 10,000 generados por la solución de lista ordenada, y funciona al menos hasta ahora.
Por cierto, el siguiente después de un billón parece ser 1,024,000,000,000.
...
Hm. Puedo obtener el rendimiento de O (n) - O (1) por valor (!) - y el uso de memoria O (log n) al tratar best()como una tabla de búsqueda que extiendo de forma incremental. En este momento ahorra memoria al iterar cada vez, pero está haciendo muchos cálculos redundantes. Al mantener esos valores intermedios, y una lista de valores mínimos, puedo evitar el trabajo duplicado y acelerarlo mucho. Sin embargo, la lista de valores intermedios crecerá con n, de ahí la memoria O (log n).