-
Notifications
You must be signed in to change notification settings - Fork 0
22. Generate Parentheses #53
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?
Conversation
- 時間計算量: O(n * 2^(2n)) (文字列の任意の位置で開き括弧/閉じ括弧が許される仮定の甘い見積もり。厳密には n x カタラン数でO(4^n/(sqrt(n)))? (ref: https://en.wikipedia.org/wiki/Catalan_number)) | ||
- 手元の環境で試すとn=15で約12s, n=16で約46sかかった。CPUの周波数1G/sとして1回の計算が10ステップ、言語定数50とすると | ||
$(4^{16})/\sqrt{16} * 10 * 50 / 10^9 = 536 \text{秒}$ | ||
10倍以上計測とずれてるけど、まあ許容か...? |
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.
n かかっているのが join によるコピーなので、ネイティブコードで走っているでしょうから500クロックはかからない感じがしますね。
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.
たしかに...!
nかかってるところだけ50で割ると、
(16/50) * (4^16 / (16^1.5)) * 10*50 / 10^9 ≒ 10秒
でした。1回の計算で10クロックはかなり少ないと思うので妥当なところな気がします (CPUの周波数ももっと大きいはずですが)
所要時間: 13:30 | ||
|
||
- 時間計算量: O(n * 2^(2n)) (文字列の任意の位置で開き括弧/閉じ括弧が許される仮定の甘い見積もり。厳密には n x カタラン数でO(4^n/(sqrt(n)))? (ref: https://en.wikipedia.org/wiki/Catalan_number)) | ||
- 手元の環境で試すとn=15で約12s, n=16で約46sかかった。CPUの周波数1G/sとして1回の計算が10ステップ、言語定数50とすると |
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.
言語定数って一般的な用語ではないような気がしてます(調べても出てこなかった、、)。
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.
これ書いたときも一般的じゃないとは思いながら何か書いてしまいました 🙏 (Discordかどこかで1回使われてたような気がしたけど多分気のせいです)
Cと比べてその言語が何倍くらい遅いか、というやつですね
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.
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.
https://discord.com/channels/1084280443945353267/1200089668901937312/1235490680592273410
私がここでうっかり「速度定数」という化学用語を使ったのが原因な気がします。
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.
私がここでうっかり「速度定数」という化学用語を使ったのが原因な気がします。
あ、多分これですね。これを間違えて記憶に残しちゃったんだと思います
```py | ||
class Solution: | ||
def generateParenthesis(self, n: int) -> List[str]: | ||
parens = [] |
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.
無理に省略するのではなくparenthesis
とかにしてあげたほうが読みやすいかなと思います。
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.
https://www.merriam-webster.com/dictionary/paren
parenは割とよく使われてるんじゃないかな、という気がします
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.
なるほど、そうなんですね
yield ')' * right_rest_count | ||
return | ||
if left_open_count > 0: | ||
yield from map(lambda s: ')' + s, generate(left_open_count - 1, right_rest_count - 1)) |
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.
自分が読み慣れていないだけだと思うのですが、 yield from を用いたソースコードは、認知負荷が高いように感じました。
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.
コメントの意図は代わりにリストを使ったらどうかという提案なのでしょうか?
もしそうならば、generatorはリストと異なりメモリにその内容を同時にすべて載せないというメモリ効率性のメリットはあると思うので、認知負荷の観点だけで選択する話ではないんじゃないかなと思いました。あと個人的にはあまり認知負荷ほぼ変わらず、場合によってはyieldの方が低いまであるんですよね...もっと複雑なコードだと違うかもしれませんが。
(計算を遅延させられるのも特徴としてありますが、それはまたメリデメありそう)
leetcodeでコレクションを返す問題だと (全部かは知りませんが) listで返すように型が付けられているので、出力は結局最後にリストにしてるのでメモリ効率の意味は薄いですが、実務ではここのインターフェースは変更できる想定で書いています。変更できない場合はまた対応変わるとは思います。
個人的には書き味が良かったり処理の流れが分かりやすいことも多かったりと良い点も多いので、
自分が読み慣れていないだけ
だけが認知負荷が高い理由なのかどうかが問題になるのかもしれません。
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.
私もこの行は読みにくいと思ったのですが、yield fromが悪いというよりは、
map(lambda s: ')' + s, generate(left_open_count - 1, right_rest_count - 1))
の部分との組み合わせが読みにくいのかもしれません。
ほぼ同じ内容でわかりやすくするとしたら、
for s in generate(left_open_count - 1, right_rest_count - 1):
yield ')' + s
とかでしょうか?
sの定義が比較して明示的になり、かつ前の方にやってきたので次の行のyield ')' + sが自然に読めるのかな?という気がします。map()のカッコや引数の解釈も不要になりました。
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.
map(lambda s: ')' + s, generate(left_open_count - 1, right_rest_count - 1))
たしかにこれ自体はyield関係なく読み辛いですね。納得です。
def generateParenthesis(self, n: int) -> List[str]: | ||
parens = [] | ||
|
||
def generate(left: int, right: int) -> Iterator[str]: |
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.
自分が解いたときは、生成中の文字列・開いている括弧の数・n の値を再帰関数に引き回して解きました。本質的には同じだと思います。
if new_length == 2 * n and new_left_unclosed_count > 0: | ||
break |
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.
上から読んでいって、L166でちゃんと'('は全部閉じれているか不安になり、少し経ってL173~L174で大丈夫だと判明する、という流れで、個人的に少し読むのに時間がかかりました。
https://leetcode.com/problems/generate-parentheses/description/