Voici une réponse qui s'exécute avec une mémoire constante, au détriment du CPU. Ce n'est pas une bonne réponse dans le contexte de la question d'origine (c'est-à-dire une réponse lors d'un entretien). Mais si l'interview dure 24 heures, ce n'est pas si mal. ;)
L'idée est que si j'ai n qui est une réponse valide, alors la suivante dans la séquence va être n fois une puissance de deux, divisée par une puissance de 5. Ou bien n fois une puissance de 5, divisée par un puissance de deux. Pourvu qu'il se divise également. (... ou le diviseur peut être 1;) auquel cas vous multipliez simplement par 2 ou 5)
Par exemple, pour passer de 625 à 640, multipliez par 5 ** 4/2 ** 7. Ou, plus généralement, multipliez par une valeur de 2 ** m * 5 ** n
pour m, n où l'on est positif et l'autre négatif ou nul, et le multiplicateur divise le nombre également.
Maintenant, la partie délicate est de trouver le multiplicateur. Mais nous savons a) le diviseur doit diviser le nombre également, b) le multiplicateur doit être supérieur à un (les nombres continuent d'augmenter), et c) si nous choisissons le plus petit multiplicateur supérieur à 1 (c'est-à-dire 1 <f <tous les autres f ), alors c'est garanti d'être notre prochaine étape. L'étape qui suivra sera son étape la plus basse.
La partie désagréable est de trouver la valeur de m, n. Il n'y a que des possibilités de log (n), car il n'y a que tant de 2 ou de 5 à abandonner, mais j'ai dû ajouter un facteur de -1 à +1 comme manière bâclée pour gérer l'arrondi. Il suffit donc d'itérer à travers O (log (n)) à chaque étape. C'est donc O (n log (n)) dans l'ensemble.
La bonne nouvelle est que, comme il prend une valeur et trouve la valeur suivante, vous pouvez commencer n'importe où dans la séquence. Donc, si vous voulez le suivant après 1 milliard, il peut simplement le trouver en itérant à travers les 2/5 ou 5/2 et en choisissant le plus petit multiplicateur supérieur à 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
J'ai validé les 10 000 premiers numéros générés par rapport aux 10 000 premiers générés par la solution de liste triée, et cela fonctionne au moins jusque-là.
BTW le suivant après un billion semble être 1 024 000 000 000.
...
Hm. Je peux obtenir des performances O (n) - O (1) par valeur (!) - et l'utilisation de la mémoire O (log n) en traitant best()
comme une table de recherche que j'étends de manière incrémentielle. À l'heure actuelle, il économise de la mémoire en itérant à chaque fois, mais il fait beaucoup de calculs redondants. En maintenant ces valeurs intermédiaires - et une liste de valeurs min - je peux éviter le travail en double et l'accélérer beaucoup. Cependant, la liste des valeurs intermédiaires augmentera avec n, d'où la mémoire O (log n).