CPUを犠牲にして、一定のメモリで実行する答えは次のとおりです。これは、元の質問(つまり、インタビュー中の回答)のコンテキストでは適切な回答ではありません。しかし、面接が24時間であれば、それほど悪くはありません。;)
アイデアは、有効な答えであるnがある場合、シーケンスの次は、2の累乗のn倍、5の累乗で除算する、または5の累乗をn倍して、 2のべき乗。均等に分割することを条件とします。(...または除数は1にすることができます;)この場合、2または5を掛けるだけです)
たとえば、625から640に移動するには、5 ** 4/2 ** 7を掛けます。または、より一般的には2 ** m * 5 ** n
、あるm、nの値を掛けます。ここで、1は正で、1は負またはゼロです。乗数は、数値を均等に割ります。
今、トリッキーな部分は乗数を見つけることです。ただし、a)除数は数値を均等に除算する必要があります。b)乗数は1より大きくなければなりません(数値は増加し続けます)。c)1より大きい最小の乗数を選択した場合(つまり、1 <f <他のすべてのf) )、それが次のステップになることが保証されています。その次のステップが最低のステップになります。
厄介なのは、m、nの値を見つけることです。あきらめるのは2または5の数が非常に多いため、log(n)の可能性のみがありますが、丸めを処理するためのずさんな方法として、-1から+1の係数を追加する必要がありました。したがって、各ステップでO(log(n))を繰り返すだけで済みます。つまり、全体としてO(n log(n))です。
良いニュースは、値を取得して次の値を見つけるため、シーケンスのどこからでも開始できるということです。したがって、10億の次の1が必要な場合は、2/5または5/2を反復処理して、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
これにより生成される最初の10,000の数値を、並べ替えられたリストソリューションによって生成される最初の10,000と比較して検証しましたが、少なくともそれは機能します。
ところで、1兆の次は1,024,000,000,000のようです。
...
うーん。O(n)パフォーマンス-値ごとのO(1)(!)-およびO(log n)のメモリ使用量を、best()
増分的に拡張するルックアップテーブルとして扱うことで取得できます。現在は毎回繰り返すことでメモリを節約していますが、多くの冗長な計算を行っています。これらの中間値(および最小値のリスト)を保持することで、重複する作業を回避し、大幅に高速化できます。ただし、中間値のリストはnとともに増加するため、O(log n)メモリが増加します。