Skip to content
Open
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
107 changes: 107 additions & 0 deletions Python3/50. Pow(x, n).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
## Step 1. Initial Solution

- 素直にやるならx * x^(n-1)とする方法がある
- このまま取りあえず実装してみると再帰の深さがnになるので
• `-2^31 <= n <= 2^31-1`

を見ると微妙

- 自分で計算するときは{x^(n//2)}^2のようにすることが多いのでこれを実装
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰でやるのは思い付きませんでした。勉強になります 👀

- 少し条件分岐が多くなったがひとまずこれで実装

```python
class Solution:
@cache
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

キャッシュがほとんど効かないと思いますので、外してよいと思います。

def myPow(self, x: float, n: int) -> float:
if x == 0:
return 0

if n < 0:
return 1 / self.myPow(x, -n)
if n == 0:
return 1
if n == 1:
return x
if n == 2:
return x * x

if n % 2 == 1:
return x * self.myPow(x, n - 1)
return self.myPow(self.myPow(x, n // 2), 2)
```

### Complexity Analysis

- 時間計算量:O(log n)
- 空間計算量:O(log n)

## Step 2. Alternatives

- https://github.com/tokuhirat/LeetCode/pull/45/files#diff-ce284075b2a3f6e20cd3d4ff805c3cb4ab0540e9ace2b462e43b40955dfd3dc1R32
- 中で x*xにする考え方もある
- 最後のelseは要らなさそう
- 分岐を参考にして修正

```python
class Solution:
@cache
def myPow(self, x: float, n: int) -> float:
if n < 0:
return 1 / self.myPow(x, -n)
if n == 0:
return 1
if n % 2 == 1:
return x * self.myPow(x, n-1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

二項演算子の左右には、適宜スペースを入れることをお勧めします。

参考までに、関連するスタイルガイドを共有いたします。

https://peps.python.org/pep-0008/#other-recommendations

Always surround these binary operators with a single space on either side: assignment (=), augmented assignment (+=, -= etc.), comparisons (==, <, >, !=, <=, >=, in, not in, is, is not), Booleans (and, or, not).

https://google.github.io/styleguide/pyguide.html#36-whitespace

Surround binary operators with a single space on either side for assignment (=), comparisons (==, <, >, !=, <>, <=, >=, in, not in, is, is not), and Booleans (and, or, not). Use your better judgment for the insertion of spaces around arithmetic operators (+, -, *, /, //, %, **, @).

なお、これらのスタイルガイドは唯一絶対のルールではなく、数あるガイドラインの一つに過ぎません。チームによって重視する書き方が異なる場合もあります。
そのため、ご自身の中に基準を持ちつつも、最終的にはチーム内で一般的とされる書き方に寄せていくことをお勧めいたします。

return self.myPow(x, n // 2) * self.myPow(x, n // 2)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

一度変数に格納してから掛けたほうがシンプルだと思います。

p = self.myPow(x, n // 2)
return p * p

```

- https://github.com/TORUS0818/leetcode/pull/47/files#diff-91647df59bb2863e62120bcb064c143cab9cb1c4e78ed9feab30fc009844b5d0R198-R200
- 確かにPythonではあまり気にすることがないが1.0表記の方がfloatであることが明らかなので良さそう
- x=0 かつ n≤0の場合は処理できないのでZeroDivisionErrorか0^0=1が返ることになる
- Step1の実装も正しくはないので微妙
- https://github.com/TORUS0818/leetcode/pull/47/files#r2038337006
- ビットシフトでループを回す方法もあるらしい
- 実際に手作業でこの演算をやることを考えるとあまり直感的ではない

```python
class Solution:
def myPow(self, x: float, n: int) -> float:
assert x != 0 or n > 0
if n < 0:
x = 1.0 / x
n = -n

result = 1.0
bit = 1
exponent = x
while n >= bit:
if n & bit:
result *= exponent
bit <<= 1
exponent *= exponent
return result
```

## Step 3. Final Solution

- 以下でassert x ≠ 0 or n < 0を最初に入れてx==0の場合分けを入れないと答えが倍数精度で0.0表示になるときにエラーが起きる
- 再帰をするときは再帰で呼び出されるケースを網羅的に考える必要がある

```python
class Solution:
@cache
def myPow(self, x: float, n: int) -> float:
assert x != 0 or n > 0

if n == 0:
return 1.0
if x == 0:
return 0
if n < 0:
x = 1.0 / x
n = -n
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好みですがn *= -1としてもいいと思いました。


if n % 2 == 1:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

n % 2, n // 2についてはそれぞれn & 1, n >> 1とも書けるかもしれません。
操作の記述としては直感的ではないかもですが、一般的には整数の割り算に比べてビット演算の方がCPUにとって早いと聞いたことがあるので、一応頭の片隅に入れておくと良いかもしれません。(すみません、実際それぞれどれぐらいのクロック数が必要かとか、pythonでこう書いたときに最適化されてビット演算と同じになるのかとかは電車内にいまして調べられていません…)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

その視点はなかったですが今回のように計算用の関数の場合はそう言った観点も重要そうですね

return x * self.myPow(x, n-1)
return self.myPow(x*x, n//2)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-, //の両隣はスペースを開ける方が自然な気がしました(もしかすると関数の引数としての区切りが明確になるという点でこういう場合は開けない方が好みという方もいるのでしょうか?)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分はまとまっていた方が理解しやすいと思ってしまうタイプですが、一般的なのは開ける方な気はしています。。

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://peps.python.org/pep-0008/#whitespace-in-expressions-and-statements
Python の標準的なスタイル指針の PEP8 はどちらでもといってそうですね。

スタイルは統一されていることが大事なので、あんまり局所的に正誤が決まるものであるというよりは周りを見て合わせるくらいがちょうどいいかと思います。

```