-
Notifications
You must be signed in to change notification settings - Fork 0
46. Permutations #44
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
46. Permutations #44
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # step1 何も見ずに解く | ||
| [1,2,3]の場合、 | ||
| [[]] | ||
| [[1], [2], [3]] | ||
| [[1, 2], [1, 3], [2, 3],[2, 1], [3, 1], [3, 2]] | ||
| といった形で増やしていけば良さそう。 | ||
| つまり、配列の各permutationにまだ追加していない値があれば追加したものを加えるというループを繰り返してpermutationの長さがnumsのサイズになれば終了すれば良い。 | ||
|
|
||
| ```ruby | ||
| # @param {Integer[]} nums | ||
| # @return {Integer[][]} | ||
| def permute(nums) | ||
| permutations = [[]] | ||
| while permutations.first.size < nums.size | ||
| next_permutations = [] | ||
| permutations.each do |permutation| | ||
| next_permutations.concat((nums - permutation).map { |num| (permutation + [num]) }) | ||
| end | ||
| permutations = next_permutations | ||
| end | ||
| permutations | ||
| end | ||
| ``` | ||
|
|
||
| ある時点でのpermutationの長さをkとすると、 | ||
| ``` | ||
| (nums - permutation).map { |num| (permutation + [num]) } | ||
| ``` | ||
| の部分で(n - k) * kの計算量がかかる。 | ||
| これを各階層のノードの数とかけて0 - nのkについて合計すると、計算量はO(n * n!)になる。 | ||
| 空間計算量は最終的に作られるpermutationの数 * 各permutationの長さなのでO(n * n!) | ||
| nの最大値が6なので1秒以内で間に合うが、少しでも大きくなったらどうしようもなさそう。 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # step2 他の方の解答を見る | ||
|
|
||
| ## 使用中のnumsをSetにいれるかどうか | ||
| https://github.com/Ryotaro25/leetcode_first60/pull/54#discussion_r1986035628 | ||
|
|
||
| numsの中でpermutationで使われていないものを取り出す時に線形に検索するとnが乗ってしまう。 | ||
| これは(Intersection of Two Arrays)[https://leetcode.com/problems/intersection-of-two-arrays/editorial/]と同じ話かと思った。 | ||
|
|
||
| https://github.com/tokuhirat/LeetCode/pull/50/files#r2278840164 | ||
| 一方で、問題の制限はnが6以下であるし、オーバーヘッドを加味すると配列のまま計算する方が速いかもしれない。 | ||
| ハッシュテーブルにした方が速くなるくらいのサイズだとそもそも現実的な計算時間で終わらなくなっている気もするな… | ||
|
|
||
| と思ってRubyのコードを読んだところ、特定の長さの配列まではハッシュテーブルを使わずに線形探索していた。 | ||
| そういえば積集合をとる`&`も同じことをしていたんだった。 | ||
| ref: https://github.com/ruby/ruby/blob/5257e1298c4dc4e854eaa0a9fe5e6dc5c1495c91/array.c#L5558-L5583 | ||
|
|
||
| ## 再帰で解く | ||
| https://github.com/ryosuketc/leetcode_arai60/pull/39 | ||
|
|
||
| ```ruby | ||
| # @param {Integer[]} nums | ||
| # @return {Integer[][]} | ||
| def permute(nums) | ||
| permute_helper = lambda do |nums| | ||
| permutes = [] | ||
| append_permutes = lambda do |permute, nums| | ||
| if nums.empty? | ||
| permutes << permute | ||
| return | ||
| end | ||
| nums.each_with_index do |num, i| | ||
| new_permute = permute + [num] | ||
| new_nums = nums[0...i] + nums[(i + 1)..-1] | ||
| append_permutes.call(new_permute, new_nums) | ||
| end | ||
| end | ||
| append_permutes.call([], nums) | ||
| permutes | ||
| end | ||
| permute_helper.call(nums) | ||
| end | ||
| ``` | ||
|
|
||
| ## step1の改善 | ||
|
|
||
| ```ruby | ||
| # @param {Integer[]} nums | ||
| # @return {Integer[][]} | ||
| def permute(nums) | ||
| permutes = [[]] | ||
| while permutes.first.size != nums.size | ||
| permutes = permutes.each_with_object([]) do |permute, next_permutes| | ||
| next_permutes.concat((nums - permute).map { |num| permute + [num] }) | ||
| end | ||
| end | ||
| permutes | ||
| end | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # step3 3回続けて10分以内に書いてエラーを出さなければOKとする | ||
|
|
||
| ```ruby | ||
| # @param {Integer[]} nums | ||
| # @return {Integer[][]} | ||
| def permute(nums) | ||
| permutes = [[]] | ||
| while permutes.first.size != nums.size | ||
| permutes = permutes.each_with_object([]) do |permute, next_permutes| | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 細かいですが、permuteは動詞の印象があるので、この辺の変数の名前はpermutationsやpermsにしたいかもしれません。 |
||
| next_permutes.concat((nums - permute).map { |num| permute + [num] }) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. こちらのやり方はこちらのやり方で読みやすいと思いますが、割と広く知られている手法としては、次のようなバックトラッキングを用いたやり方で順列を生成するアルゴリズムがあると思います。 def permute_inplace(nums)
res = []
backtrack = ->(first) do
if first == nums.length
res << nums.dup
return
end
(first...nums.length).each do |i|
nums[first], nums[i] = nums[i], nums[first]
backtrack.call(first + 1)
nums[first], nums[i] = nums[i], nums[first] # 元に戻す
end
end
backtrack.call(0)
res
endどこまで作ったかのindexを渡すことで、途中まで作った順列を表す配列それ自体に
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます! |
||
| end | ||
| end | ||
| permutes | ||
| end | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ## step4 レビューを受けて解答を修正 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
はじめに読んだ時、lambda がネストされていて難しく感じました。nums はブロックパラメータに書かなくても読めるはずなので書かれているのはなんでだろうと思いました。処理を読むと append_permutes の nums はまだ追加していない nums であり、ブロックパラメータに書く必要があるとわかりました。また、permute_helper は入出力が permute と同じなのでまとめられないかなと思いました。自分だったら以下のように書くと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
コメントありがとうございます。
おっしゃる通りlambdaの引数として利用している
numsが関数の引数の名前と同じでかなり読みにくいですね。あと外側のlambdaには引数は不要ですね。
個人的には
resultsをlambdaの外のものを変更するのがあまり好きではないので以下のように書こうと思います。There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
少し考えたのですが、この程度であればlambdaはネストさせなくても良い気がしてきました。
permute内に処理が追加されるようなら検討しても良いかもしれません。(その時はlamdaをネストさせるより別のメソッドに切り出すかもしれないですが)