Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions 31.Next-Permutation/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 31. Next Permutation

Next Permutationのアルゴリズムを知っていたので解けた: sol1.

`nums[ascending_index + 1 :] = sorted(nums[ascending_index + 1 :])`
と書くべきところを
`nums[ascending_index + 1 :].sort()`
と書いてしまい一度間違えた。

配列のスライスはコピーを作ることを確認しておく

### コメント集

https://discord.com/channels/1084280443945353267/1237649827240742942/1353878925117227113

> これを見て、first_increasing_order_index の初期化がここで終了したという理解は構造からは得られないと思うんですよね。

なるほど、初期化の意図が分かりづらくなってしまうのは納得した。これは他の処理でも出てきそう。
改善後: sol2.py

ついでに少し処理をまとめる: sol3.py

> C++ 実装を読みましょう。

https://discord.com/channels/1084280443945353267/1201211204547383386/1232011836543467660

https://en.cppreference.com/cpp/algorithm/next_permutation

Pythonより簡潔な印象

https://github.com/olsen-blue/Arai60/pull/59#discussion_r2030531369

> ひっくり返すポイントを見つけるのに、自乗のオーダーがかかっているので、これの最悪計算量は O(n^2) ですね。

> しかし、償却計算量を考えると、そうではなさそうです。
> 長さ k がひっくり返る確率は、k/(k+1) 1/k! (ただし、k=n のときには 1/k!)でしょうか。

> これに k^2 をかけて期待値を計算すると、∑ k^2 / k! で上から抑えられるのですが、これは発散しないので定数時間ですね。

償却計算量という言葉は知らなかった

> 償却計算量は、ある最悪な入力の列に対しての振る舞いのことです。 たとえば、list に append していくと、たまに、メモリーのリアロケーションで配列長かかりますが、大きさが倍々に増えていく場合(倍でなくても等比級数的ならばよい)は、reallocation のコストは定数で平均すると抑えられます。

一連の操作に対する平均の計算量でこの一連の操作が最悪の場合を考える、と理解した

行間を埋めくださっている方がいた

https://github.com/mamo3gr/arai60/blob/31_next-permutation/31_next-permutation/memo.md

> 1文目:for i と for j の二重ループなので、ループ内の早期終了する条件を満たさない場合 O(N^2) になる。 2文目:長さ k のランダムな順列を考えると、完全降順になる確率は 1/k!. k 個は完全降順だが、k+1 個はそうでない確率は、1/k! - 1/(k+1)!. これを変形すると k(k+1) 1/k! になる。 3文目:O(N^2) から、長さ k を処理する計算量=コストを k^2 として、先程の確率に掛け算して期待値を求める。 すると k^2 x k/(k+1) x 1/k! <= k^2/k!. ∵ k/(k+1) < 1. k^2/k! の分母と分子を見ると、k^2よりもk!の方が遥かに速く増える(=発散しない)。 極限を取ると 2e になるらしい。
19 changes: 19 additions & 0 deletions 31.Next-Permutation/sol1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
pivot = len(nums) - 2
while pivot >= 0:
if nums[pivot] < nums[pivot + 1]:
break
pivot -= 1
if pivot < 0:
nums.sort()
return
for index in range(len(nums) - 1, pivot, -1):
if nums[pivot] < nums[index]:
nums[pivot], nums[index] = nums[index], nums[pivot]
break
nums[pivot + 1 :] = sorted(nums[pivot + 1 :])
return
19 changes: 19 additions & 0 deletions 31.Next-Permutation/sol2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
pivot = -1
for pivot in range(len(nums) - 2, -1, -1):
if nums[pivot] < nums[pivot + 1]:
break
pivot -= 1
if pivot < 0:
nums.sort()
return
for index in range(len(nums) - 1, pivot, -1):
if nums[pivot] < nums[index]:
nums[pivot], nums[index] = nums[index], nums[pivot]
break
nums[pivot + 1 :] = sorted(nums[pivot + 1 :])
return
17 changes: 17 additions & 0 deletions 31.Next-Permutation/sol3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
pivot = -1
for pivot in range(len(nums) - 2, -1, -1):
if nums[pivot] < nums[pivot + 1]:
break
pivot -= 1
if pivot >= 0:
for index in range(len(nums) - 1, pivot, -1):
if nums[pivot] < nums[index]:
nums[pivot], nums[index] = nums[index], nums[pivot]
break
nums[pivot + 1 :] = sorted(nums[pivot + 1 :])
return