Conversation
| if n == 0: | ||
| return 1.0 | ||
| if n < 0: | ||
| return 1.0 / self.myPow(x, -n) |
There was a problem hiding this comment.
個人的には return self.myPow(1.0/x, -n) の方が好みです。
x^n = (1/x)^(-n) と書く方が、x^n = 1 / (x^(-n)) より自然だと思ったからです
There was a problem hiding this comment.
そちらを自然だと考える理由はなるほどです。
しかし逆数をとった時に生じる誤差が累積されることも考えて今回はこのままにしておこうと思います。
こういう複数の選択肢を選べるようにしておきたいです。
There was a problem hiding this comment.
オーバーフローを引き起こしているので、ここでは良い選択肢とは言えないかもしれません。
「指数を半分にしつつ、底を毎回 x**2 に置き換える」やりかただとオーバーフローが生じたので、底を変えない形にした
こちらについてですが、オーバーフローした時に、**演算だとエラーを投げますが、*演算だとinfを返すので結果的に治っているという形かと。数値的にはpow(x ** 2, n // 2)も、pow(x, n // 2) ** 2も同じですよね。
There was a problem hiding this comment.
ご指摘の通りで、x*xを渡すようにしたらエラーはなくなりますね。
**演算だとエラーを投げますが、*演算だとinfを返す
これは知らなかったです。勉強になりました
| half = self.myPow(x, n // 2) | ||
| if n % 2 == 1: | ||
| return half * half * x | ||
| return half * half |
There was a problem hiding this comment.
これを用いると末尾最適化ができそうですが、Pythonでは末尾最適化が行われないので、Trampolineという手法があることを知り、これを使って実装してみました
|
|
||
| https://github.com/hroc135/leetcode/pull/43/changes#diff-788d4e4d3d8fc9d0d4ea879b815deba3b2c21e39a44274fe799eaa496c5d2e43 | ||
|
|
||
| 組み込み関数のコードを読んでいる |
There was a problem hiding this comment.
nit: 組み込み関数ではなく、標準ライブラリですね。少なくとも自分が良く扱う言語ではここの区別はできた方がいいと思いました!
|
|
||
| sol2.pyを再帰でかいた。 | ||
|
|
||
| 「指数を半分にしつつ、底を毎回 x**2 に置き換える」やりかただとオーバーフローが生じたので、底を変えない形にした |
There was a problem hiding this comment.
これはもうちょっと深掘りしてほしいですね。x * xで底を渡してやると動くと思います。
There was a problem hiding this comment.
ここでもコメントいただいていましたね、すみません。上で返答させていただきました。
| shifted = abs(n) | ||
| current_pow = x | ||
| while shifted > 0: | ||
| if shifted & 1: |
There was a problem hiding this comment.
これはなんらかの意図で、//や%ではなく、ビット演算を行なっているんですかね?
There was a problem hiding this comment.
ビット演算の方が高速だと思います。また//や%よりも一般的だと個人的には思います
There was a problem hiding this comment.
直感的なのは剰余演算かもしれないですね。Pythonは高速化の恩恵がなさそうなので%で十分かもしれません
There was a problem hiding this comment.
どちらが一般的かは文脈によると思いますが、この問題の場合だと、x^2n = (x^n)^2, x^(2n+1)=x(x^n)^2を素直に表現するなら、//, %かなと。
There was a problem hiding this comment.
ビット演算はCPUの動作に忠実で効率も良いため計算の意図を正確に表す書き方だと思っていました。
どちらも選べるようにしておこうと思います。
There was a problem hiding this comment.
2進数のメンタルモデルで考えているなら、ビット演算で処理をするのも自然だと思います。この問題だとどっちもありに思いました。扱っている対象の抽象度に応じて適切な表現は変わりそうですね。
| shifted = abs(n) | ||
| current_pow = x |
There was a problem hiding this comment.
変数名がイマイチに感じます。下のhalfも同様です。
shifted: the variable I'm going to shift later. I won't tell you what it is, though. No spoiler.
って感じですね。
There was a problem hiding this comment.
shiftedはexponentにしました。halfは個人的にはこれで良いかと思ったので差し当たりこのままにしておきます。
There was a problem hiding this comment.
一応、何が違和感があるのか説明しますと、半分にしたのは、指数部分ですよね。2^2を2^4の半分とは呼びませんよね。
| if n == 0: | ||
| return 1.0 | ||
| if n < 0: | ||
| return 1.0 / self.myPow(x, -n) |
There was a problem hiding this comment.
個人的には、引数を書き換えるために関数を再帰的に呼ぶのは、ややパズルに感じます。引数を直接書き換えてしまったほうがシンプルに感じます。
if n < 0:
x = 1.0 / x
n = -n趣味の範囲かもしれません。
There was a problem hiding this comment.
なるほど、再帰的に呼ばなくても解決するのですね。気づきませんでした。
revisedのファイルで採用させていただきました。
https://leetcode.com/problems/powx-n/submissions/1969061269/