Prepcastdoom:
二つの数を考えるm
とn
、一手に使用すると、2つの新しいペアを取得することができます:
m+n, n
m, n+m
さんはintially設定してみましょうm = n = 1
移動の最小数数字の対等のように少なくとも一つを見つけますk
解決策は、(すなわちリードが移動の順序がkに存在していること)があります保証されています
たとえば、次の指定されたk = 5
ように、動きの最小数m
に等しいまたはNであるk
3
1, 1
1, 2
3, 2
3, 5
3つの移動の合計。
私はPythonで再帰を使用して解決策を考え出す必要があり、それは、大きな数の上で動作するようには思えない(つまり、10 ^ 6)
def calc(m, n, k):
if n > k or m > k:
return 10**6
elif n == k or m == k:
return 0
else:
return min(1+calc(m+n, n, k), 1+calc(m, m+n, k))
k = int(input())
print(calc(1, 1, k))
それは大きな数字のために働くように、どのようにしてパフォーマンスを向上させることができますか?
DarrylG:
(ヒープを使用して)優先度キューに基づく非再帰的アルゴリズム
State: (sum_, m, n, path) sum_ is current sum (i.e. m + n) m and n are the first and second numbers path is the sequence of (m, n) pairs to get to the current sum
各ステップで二つの可能な移動があります
- 合計で最初の番号を交換してください
- 合計することにより、第2の数を交換してください
したがって、各状態は、2つの新しい状態を生成します。米国はによって優先順位付けされています。
- 移動:状態の低い数字では、より高い優先順位を持っています
- 合計:高合計と国が高い優先順位を持っています
私たちは、優先順位によって、プロセスの状態に優先順位キュー(この場合はヒープ)を使用します。
コード
def calc1(k):
if k < 1:
return None, None # No solution
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves, [(m, n)]
h = [] # Priority queue (heap)
path = [(m, n)]
sum_ = m + n
# Python's heapq acts as a min queue.
# We can order thing by max by using -value rather than value
# Thus Tuple (moves+1, -sum_, ...) prioritizes by 1) min moves, and 2) max sum
heappush(h, (moves+1, -sum_, sum_, n, path))
heappush(h, (moves+1, -sum_, m, sum_, path))
while h:
# Get state with lowest sum
moves, sum_, m, n, path = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves, path # Found solution
if sum_ < k:
sum_ = m + n # new sum
# Replace first number with sum
heappush(h, (moves+1, -sum_, sum_, n, path + [(sum_, n)]))
# Replace second number with sum
heappush(h, (moves+1, -sum_, m, sum_, path + [(m, sum_)]))
# else:
# so just continues since sum_ > k
# Exhausted all options, so no solution
return None, None
テスト
テストコード
for k in [5, 100, 1000]:
moves, path = calc1(k)
print(f'k: {k}, Moves: {moves}, Path: {path}')
出力
k: 5, Moves: 3, Path: [(1, 1), (2, 3), (2, 5)]
k: 100, Moves: 10, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(8, 19), (27, 19), (27, 46), (27, 73), (27, 100)]
k: 1000, Moves: 15, Path: [(1, 1), (2, 3), (5, 3), (8, 3), (8, 11),
(19, 11), (19, 30), (49, 30), (79, 30), (79, 109),
(188, 109), (297, 109), (297, 406), (297, 703), (297, 1000)]
パフォーマンスの向上
パフォーマンスを向上させるために、2つの調整の後
- K = 10,000 3Xの高速化を提供するステップのパスだけの数が(を含めません
- 対称ペアを使用していない(K = 10、000と追加の2X提供
同じ前後に、そのような(1、2)や(2,1)であり、対称対Mの平均対Nで。
彼らは同じソリューションのステップ数を提供しますので、我々は、これらの両方にブランチには必要ありません。
改善されたコード
def calc(k):
if k < 1:
return None, None
m, n, moves = 1, 1, 0
if m == k or n == k:
return moves
h = [] # Priority queue (heap)
sum_ = m + n
heappush(h, (moves+1, -sum_, sum_, n))
while h:
moves, sum_, m, n = heappop(h)
sum_ = - sum_
if sum_ == k:
return moves
if sum_ < k:
sum_ = m + n
steps = [(sum_, n), (m, sum_)]
heappush(h, (moves+1, -sum_, *steps[0]))
if steps[0] != steps[-1]: # not same tuple in reverse (i.e. not symmetric)
heappush(h, (moves+1, -sum_, *steps[1]))
演奏
Tested up to k = 100, 000 which took ~2 minutes.