-
Notifications
You must be signed in to change notification settings - Fork 0
Create Pow.md #9
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?
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,122 @@ | ||
# 50. Pow(x, n) | ||
ブランクが空いたので前回指定した「次に解く問題」ではなく、別の問題の別解として馴染みのある再帰の問題を選びました。 | ||
|
||
## 参考にした方々(Pythonで書かれた直近5名) | ||
- https://github.com/nittoco/leetcode/pull/17/files | ||
- https://github.com/Mike0121/LeetCode/pull/17/files | ||
- https://github.com/Ryotaro25/leetcode_first60/pull/48/file | ||
- https://github.com/Yoshiki-Iwasa/Arai60/pull/38/files | ||
|
||
## Step 1 | ||
### 考えたこと | ||
- まずは再帰で書いてみる。 | ||
- n = 0の場合をベースケースとして、+-で掛け算と割り算を繰り返せば良いと思った。他の方のコードで呼び出し上限について言及してるの見たことあるな、こういう風に困るのか…。 | ||
- が、呼び出し回数に引っかかって通らない。ここでギブアップ。 | ||
|
||
下記、通りません: | ||
```Python | ||
class Solution: | ||
def myPow(self, x: float, n: int) -> float: | ||
if n == 0: | ||
return 1 | ||
elif n <= -1: | ||
return self.myPow(x, n + 1) / x | ||
else: | ||
return x * self.myPow(x, n - 1) | ||
``` | ||
|
||
- ここで答えを見ると、x^n = (x^(n/2))^2の操作を繰り返すと計算回数を減らせると知ったので書き直してみる。 | ||
- 愚直にPowを2回呼び出すと結局再帰呼び出し回数に引っかかってしまうので、temp的な変数(half…)に入れ直す | ||
- nが小数の時とかどうしてるんだろう→先人のgitにPowのプログラムへのリンクがあり、生まれて初めてCPythonのソースコードを読んでみたい気になる。が、手も脚も出ない:https://github.com/python/cpython/blob/d5ba4fc9bc9b2d9eff2a90893e8d500e0c367237/Objects/longobject.c#L4849 | ||
|
||
```Python | ||
class Solution: | ||
def myPow(self, x: float, n: int) -> float: | ||
if n == 0: | ||
return 1 | ||
elif n < 0: | ||
n = -n | ||
x = 1 / x | ||
|
||
if n % 2 == 0: | ||
half_even = self.myPow(x, n / 2) | ||
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. n の型が int のため、割り算の結果の型が整数になるよう、 // 演算子を使ったほうが良いと思います。 |
||
return half_even * half_even | ||
if n % 2 == 1: | ||
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. elif: のほうがシンプルだと思います。 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. ありがとうございます(むしろなぜifにしてたのか思い出せないので、整理途中のコードがそのまま残っていたものと思われます...) 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. else にするか全体を下げるかでしょうかね。 Step 3 あたりでなされているので、選択肢が見えているならば、あとは趣味の範囲かと思います。 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. ありがとうございます。不慣れなだけかもしれませんが、全体を下げるのはifと対称でない様に感じられるのでelseにしそうです。
ありがとうございます、if文に入る前に計算してhalfに入れるなどしようと思います。 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. |
||
half_odd = self.myPow(x, (n - 1) / 2) | ||
return x * half_odd * half_odd | ||
``` | ||
|
||
|
||
## Step 2 | ||
### 学んだこと | ||
- モジュラ逆数?(https://github.com/nittoco/leetcode/pull/17/files) 公式へのリンクが貼ってあって、合同式の中で逆数をどう定義するかの話らしい ¶。例がピンとこないのでネットに落ちてた記事に頼る https://qiita.com/Cper0/items/1dc7e7db1eddcbfd259f。 | ||
- みんな浮動小数点の話をしてる。 → 2^(-n)の扱いや型を変換した際に丸め誤差などが乗ることを恐れているっぽい。確かにうっすら怖かったが、自分の場合それに対処できるほど意識を回せていない。とりあえずこれに目を通してみたところ、精度を改善するための方法がいろいろあるらしい:https://docs.python.org/ja/3.5/tutorial/floatingpoint.html | ||
- 末尾再帰とその最適化という言葉を初めて知った。その関数自身のみを呼び出す形にすることで計算回数を減らす考え方らしい。:https://zenn.dev/kj455/articles/dfa23c8357b274 | ||
- 「再帰は若干認知負荷が高くて読みにくいよね」みたいな話が以前にもあったのでwhile文で書いてみる | ||
- accumulated_prodはわかりやすくて良い。Cpythonは読めなかったけど、accumみたいな名前の付け方をしていた。 | ||
- 時間の都合でギブしたが後でもう一度この方のPRを読む:https://github.com/fhiyo/leetcode/pull/46/files。bit演算するアプローチも追えてないが、計算誤差避けの手段っぽい。 | ||
|
||
```Python | ||
class Solution: | ||
def myPow(self, x: float, n: int) -> float: | ||
if n < 0: | ||
n = -n | ||
x = 1 / x | ||
elif n == 0: | ||
return 1 | ||
Comment on lines
+65
to
+66
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. 個人的には n == 0 の条件を先頭に回したほうが単純に見える気がしますね。(下ではそうしていますね。) 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. 僕もそちらの方が自然に感じます。繰り返し打っているステップ3の方が、なんとなく色んな負荷が軽減されたコードが出力される感じがある(「しっくりくる」「書きやすい」)ので、ご指摘いただいたやり方を採用します。 |
||
|
||
accum_prod = 1 | ||
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. 読むにあたり認知負荷がかかるため、単語から文字を削って略称にするのは避けることをお勧めいたします。 https://google.github.io/styleguide/pyguide.html#316-naming
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. ありがとうございます、accumulated_productなどに変更します |
||
|
||
while n > 0: | ||
if n % 2 == 1: | ||
accum_prod = accum_prod * x | ||
x = x * x | ||
n = n // 2 | ||
print(f"{accum_prod}") | ||
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. 消し忘れですかね。 |
||
|
||
return accum_prod | ||
``` | ||
|
||
## Step 3 | ||
|
||
### コメント | ||
- 先人のPRを見ているとwhileの方が可読性が高いのだろうと思いつつ、苦手だった再帰を書ける様にしておこう&めずらしく再帰の方がしっくり来たので、まずは再帰で3回通してみる。エンジニアリングの趣旨から逸脱するのが怖いので、whileでもその後練習してみる。 | ||
- 1回目:5:36, 2回目:3:42 3回目:2:15 | ||
|
||
### 再帰で書いた方 | ||
```Python | ||
if n == 0: | ||
return 1.0 | ||
|
||
if n < 0: | ||
x = 1 / x | ||
n = -n | ||
|
||
if n % 2 == 0: | ||
half_pow = self.myPow(x, n / 2) | ||
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. 上のコメントと重なりますが、n // 2とした方がint型のままだな、というのが読んでいてはっきりわかるので不安がなさそうです。(この場合、分母が偶数だから、、、という推理が入る) 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. こうですね,ありがとうございます。 class Solution(object):
def myPow(self, x, n):
if n == 0:
return 1.0
if n < 0:
n = -n
x = 1/x
half_pow = self.myPow(x, n//2)
if n % 2 == 0:
return half_pow*half_pow
else:
return x*half_pow*half_pow |
||
return half_pow * half_pow | ||
else: | ||
half_pow = self.myPow(x, (n - 1) / 2) | ||
return x * half_pow * half_pow | ||
``` | ||
|
||
### whileで書いた方 | ||
```Python | ||
class Solution: | ||
def myPow(self, x: float, n: int) -> float: | ||
if n == 0: | ||
return 1 | ||
if n < 0: | ||
n = -n | ||
x = 1 / x | ||
|
||
accum_prod = 1 | ||
|
||
while n > 0: | ||
if n % 2 == 1: | ||
accum_prod *= x | ||
x *= x | ||
n //= 2 | ||
|
||
return accum_prod | ||
``` |
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.
私は上の条件が return ならば、elif よりも if を好みます。
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.
elif以降を気にしながらifに続く処理を読む分の負荷が不要になるためですかね。であれば納得感があるので、自分もifを採用してみようと思います(違ったら教えてください)。