●
例によってプログラミング問題です。今回は「直線上の位置0にいる雪子さんが、nの距離にある自宅に帰ろうとしています。雪子さんは酔っ払ってるので前・後ろにランダムで歩きます。ただしn*2歩あるくと疲れ果てて倒れます。雪子さんが自宅に帰れるパターンの総数を求めよ。」先日重複順列を作ろうとしていたのはこのためだったのだ。
●
これが重複順列作成の方針。こういう考え方でいってみる。初めての gif 作成。

上の図ではカーソルが全パターン最深部まで進んじゃってるけれど、本問題では全パターン網羅する必要ない。カーソルが引き返す条件を、「カーソルが2nに達したとき」「+の合計がnに達したとき」「カーソルがnに達したのに+の合計が0未満」としてみる。条件を先日の重複順列関数に足すと……。
def foo(n):
# 家に辿り着いたルート数。
answer = 0
# 雪子現在地。
yukiko = 0
# 雪子現在歩数。
cursor = -1
# 雪子体力限界。
max_cursor = n * 2 - 1
# 歩行履歴リスト。
lis = [0 for i in range(n * 2)]
# 問題の都合で登場する数値。
big_num = pow(10, 9) + 7
# 重複順列関数に雪子条件を足したもの。
def bar(yukiko, cursor, lis):
nonlocal answer
for i in [1,-1]:
# 1歩移動。
cursor += 1
lis[cursor] = i
yukiko += i
# 帰宅。記録してから、時間よ戻れ。
if yukiko == n:
answer += 1
cursor -= 1
yukiko -= i
# 限界の半分まできたのにまだ現在地0を超えてなかったら無理。時間よ戻れ。
# 体力限界。時間よ戻れ。
elif (cursor-1 == n and yukiko <= 0) or (cursor == max_cursor):
cursor -= 1
yukiko -= i
# 次の1歩へ。
else:
yukiko, cursor, lis = bar(yukiko, cursor, lis)
# for が終わったら時間よ戻れ。
yukiko -= lis[cursor]
cursor -= 1
return yukiko, cursor, lis
bar(yukiko, cursor, lis)
# 問題の都合。10^9+7の余りを返却します。
return answer % big_num
できた! とても大変であった。●
テストケースを作って、時間を測ってみる。
# 計測開始!
import time
start = time.time()
# スタート。
import yukiko_node
# テスト。入力値、答えの順番。
test_cases = [
[1, 1],
[2, 3],
[3, 4],
[4, 19],
[5, 26],
[6, 144],
[7, 197],
[8, 1171],
[9, 1597],
[10, 9878],
]
for test_case in test_cases:
answer = yukiko_node.foo(test_case[0])
if answer != test_case[1]:
print(f'はずれー 出題:{test_case} 今出た答え:{answer}')
exit()
margin = time.time() - start
print(f'結果: {margin}秒')
# 結果
結果: 0.46582603454589844秒
ええやん。ただ入力が11を超えると段々1秒を超え、2秒を超え……きつくなっていくけれど。まあやり遂げたぜ!