Conversation
| # モジュールを使用 | ||
| class Solution: | ||
| def permute(self, nums: List[int]) -> List[List[int]]: | ||
| return list(itertools.permutations(nums, len(nums))) |
There was a problem hiding this comment.
https://github.com/python/cpython/blob/main/Modules/itertoolsmodule.c
そろそろ読みます? hayashi さん手伝ってあげてください。
There was a problem hiding this comment.
pythonで書かれたコメントが何やっているかすら理解できませんでした...
cyclesの意図が汲み取れないです。
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
if r > n:
return
indices = list(range(n))
cycles = list(range(n, n-r, -1))
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
if cycles[i] == 0:
indices[i:] = indices[i+1:] + indices[i:i+1]
cycles[i] = n - i
else:
j = cycles[i]
indices[i], indices[-j] = indices[-j], indices[i]
yield tuple(pool[i] for i in indices[:r])
break
else:
returnThere was a problem hiding this comment.
自分も見てみたんですけど、難しいですね。1時間ほど手元で動かしたりしたのですが、あまり分からなかったです。
C++のnext_permutationsとも違った動きをしてそうですね。
一応cyclesについては操作に必要な回数を管理していそうです。
末尾からみてx個についての順列を作成したらx+1個目に移るみたなのを管理しています。
There was a problem hiding this comment.
わざわざ見ていただきありがとうございます。
一旦寝かせて、他の問題を解いてからまた戻ってきたいと思います。
There was a problem hiding this comment.
とりあえず、上のコードについて。
その前に、yield はご存知ですかを一応確認。
indices は、オブジェクトを直接動かす代わりに、その位置を示すインデックスを示していて、これを入れ替えています。
cycles は、繰り上がりの周期を示しています。
たとえば、n=6, r=3 だと、[6,5,4] になるんですが、
"012345"から3つを取ると"012", "013",...となり、最後の桁は4周期、次の桁は(繰り上がりを1回と数えて)5周期、一番上の桁は6周期です。
for i in reversed(range(r)):
これは、後ろから数を増やす作業をします、ということです。
そして、else を見ると yield したあとに break しているので、出力したらループが終わることが分かります。
では、
for i in reversed(range(r)):
の内容を精査していきましょう。
cycles には、0桁目から r - 1 桁目の周期が並んでいますね。
今回、cycles[i+1] == 0 になったときを考えましょう。
i + 1 の桁まではちょうど一周したはずです。
だから、indices[i+1:] はソート済みです。
indices[i:] = indices[i+1:] + indices[i:i+1]
とすると、indices[i] の順番が入れ替わることになります。
こういうときには、printf debug が一番です。
def permutations(iterable, r=None):
# permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
# permutations(range(3)) --> 012 021 102 120 201 210
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
if r > n:
return
indices = list(range(n))
cycles = list(range(n, n-r, -1))
yield tuple(pool[i] for i in indices[:r])
while n:
for i in reversed(range(r)):
cycles[i] -= 1
if cycles[i] == 0:
print("before", cycles, indices)
indices[i:] = indices[i+1:] + indices[i:i+1]
cycles[i] = n - i
print("after", cycles, indices)
else:
j = cycles[i]
indices[i], indices[-j] = indices[-j], indices[i]
print("out", cycles, indices)
yield tuple(pool[i] for i in indices[:r])
break
else:
returnとして、実行すると、次のような出力が得られました。
out [5, 1, 1, 1, 1] [0, 4, 3, 2, 1]
before [5, 1, 1, 1, 0] [0, 4, 3, 2, 1]
after [5, 1, 1, 1, 1] [0, 4, 3, 2, 1]
before [5, 1, 1, 0, 1] [0, 4, 3, 2, 1]
after [5, 1, 1, 2, 1] [0, 4, 3, 1, 2]
before [5, 1, 0, 2, 1] [0, 4, 3, 1, 2]
after [5, 1, 3, 2, 1] [0, 4, 1, 2, 3]
before [5, 0, 3, 2, 1] [0, 4, 1, 2, 3]
after [5, 4, 3, 2, 1] [0, 1, 2, 3, 4]
out [4, 4, 3, 2, 1] [1, 0, 2, 3, 4]
これでこれは読めるかしら。
There was a problem hiding this comment.
その前に、yield はご存知ですかを一応確認。
関数の処理を中断して戻り値を返すものという理解で、仕組みは理解してないです。
大きなファイルの読み出しの時に使うくらいの認識です。
"012345"から3つを取ると"012", "013",...となり、最後の桁は4周期、次の桁は(繰り上がりを1回と数えて)5周期、一番上の桁は6周期です。
cyclesの意味をこちらのコメントで理解できて、読むことができました。
あとは、桁の移動の部分の判定も読み取れてなかったのですが、debugの結果を見て分かりました。
if cycles[i] == 0:の条件下ではbreakがないので次の桁に進めるelseの条件下ではbreakで最初の桁(r桁目)に戻る
breakでiの値がリセットされることを見落としていて苦戦しました...
| def permute(self, nums: List[int]) -> List[List[int]]: | ||
|
|
||
| def make_next_permutation(nums): | ||
| for left in range(len(nums) - 2, -1, -1): |
There was a problem hiding this comment.
Pythonの言語仕様的には問題ないですけど、for文のiteratorで使用しているleftを後続の処理でも使っているのが少し分かりにくいかなと思いました。C言語とかだとfor文の中でしかleftを使うことができないので。
There was a problem hiding this comment.
while でleftの位置を動かすような感覚で使っていましたが、確かにfor文で定義した変数をそのまま使うのは読みづらくなりそうですね。
| min_right_num = nums[i] | ||
| right = i | ||
| nums[left], nums[right] = nums[right], nums[left] | ||
| nums[left + 1 :] = sorted(nums[left + 1 :]) |
There was a problem hiding this comment.
このタイミングでleftの右側は降順になっていると思うので、reverseで良いです。
| nums[left + 1 :] = sorted(nums[left + 1 :]) | ||
| return True | ||
|
|
||
| nums_copy = nums[:] |
There was a problem hiding this comment.
nums_copyよりいい命名があると思います。current_pamutationとか?もっと良いのがあるかも。
|
|
||
| nums_copy = nums[:] | ||
| nums_copy.sort() | ||
| permutations = [nums_copy[:]] |
There was a problem hiding this comment.
自分ならこれもwhileの中でやっちゃいます。終了条件とかも合わせて変えないとですが。
問題
https://leetcode.com/problems/permutations/