From 5f7c82323caa165ddf410f3d5df1378e7a2ceabd Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 17 Aug 2025 17:09:38 +0900 Subject: [PATCH 1/3] Solve 50_pow_x_n_medium --- 50_pow_x_n_medium/README.md | 106 +++++++++++++++++++++++++++ 50_pow_x_n_medium/step1.py | 31 ++++++++ 50_pow_x_n_medium/step1_iterative.py | 46 ++++++++++++ 50_pow_x_n_medium/step2.py | 38 ++++++++++ 4 files changed, 221 insertions(+) create mode 100644 50_pow_x_n_medium/README.md create mode 100644 50_pow_x_n_medium/step1.py create mode 100644 50_pow_x_n_medium/step1_iterative.py create mode 100644 50_pow_x_n_medium/step2.py diff --git a/50_pow_x_n_medium/README.md b/50_pow_x_n_medium/README.md new file mode 100644 index 0000000..d5ab3ef --- /dev/null +++ b/50_pow_x_n_medium/README.md @@ -0,0 +1,106 @@ +# 問題へのリンク +[50. Pow(x, n)](https://leetcode.com/problems/powx-n/) + +# 言語 +Python + +# 問題の概要 +浮動小数点数 `x` と整数 `n` が与えられたとき、`x` の `n` 乗を計算する。 + +# 自分の解法 + +## step1 +まず、繰り返し二乗法を再帰関数を用いて実装した。 + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + Compute x to the power of n based on these four facts; + - x^(-n) = (1/x)^n (n>0) + - x^0 = 1 + - x^1 = x + - if n = 2*q + r, then x^(n) = (x^q)^2 * (x)^r + + """ + if n < 0: + return self.myPow(1 / x, -n) + if n == 0: + return 1.0 + if n == 1: + return x + quotient, remainder = n // 2, n % 2 + + return self.myPow(x, quotient) ** 2 * self.myPow(x, remainder) +``` + +- `n`が偶数の場合は `x^n = (x^(n/2))^2`、奇数の場合は `x^n = x * (x^((n-1)/2))^2` となる性質を利用した再帰的な解法である。 +- `n`を2で割っていくため、再帰の深さは`log(n)`となる。 +- 時間計算量:`O(log(n))`。各`n`に対する計算は一度しか行われないためである。 + - `remainder`は常に0または1であり、この計算はたかだか定数時間であるため、全体の計算量に大きな影響はない。(メモ化は不要) + - (個人的には)`n%2`が1か0かで場合分けするよりも、`remainder`をそのまま`myPow`に渡す方がわかりやすいと感じた。数学的に必ず`n=2*q+r`なら`x^n=(x^q)^2 * (x)^r`となるため +- 空間計算量:`O(log(n))`。再帰呼び出しのスタックに`log(n)`の空間が必要である。 + +### 発見 +- べき乗演算子`**`の挙動について、`func(x) ** 2` のような式は `func(x)` を1回だけ評価することを確認した。 + +## step2 +step1の再帰実装では空間計算量が`O(log(n))`であったため、これを`O(1)`に改善すべく、反復処理による実装に変更した。 + +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + Compute x to the power of n using doubling. + Example: x=2.0, n = 10 + n = 2^3 + 2^1 + x^n = x^(2^3) * x^(2^1) + + Each x^(2^k) can be computed recursively; + x^(2^(k+1)) = x^(2*2^k) = x^(2^k) * x^(2^k) + """ + if n == 0: + return 1.0 + if x == 0.0: + return 0.0 + if n < 0: + # x^(-n) = (1/x)^n = 1/(x^n) (n>0) + return 1 / self.myPow(x, -n) + x_to_power_of_two = x # x^(2^0) + x_to_n = 1.0 # x^n + while n > 0: + if n % 2 == 1: + x_to_n *= x_to_power_of_two + + # x^(2^(k+1)) = x^(2*2^k) = x^(2^k) * x^(2^k) + x_to_power_of_two = x_to_power_of_two * x_to_power_of_two + n //= 2 + return x_to_n +``` + +- このアプローチは、`n`を2進数として捉えるものである。例えば `n=10` (2進数で`1010`) の場合、`x^10 = x^8 * x^2` となる。 +- ループ内で`n`を右にシフト(`n //= 2`)しながら、最下位ビットが1かどうか(`n % 2 == 1`)を確認する。 +- ビットが1であれば、その位置に対応する`x`のべき乗(`x`, `x^2`, `x^4`, `x^8`, ...)を結果に乗算していく。 +- `x`のべき乗は、ループごとに自身を2乗することで効率的に計算される。 +- 時間計算量:`O(log(n))`。ループ回数が`n`のビット数に比例するためである。 +- 空間計算量:`O(1)`。変数の使用量が`n`の大きさに依存しないためである。 + +## step3 +```python + +``` + +## step4 (FB) +```python + +``` + +# 別解・模範解答 +```python + +``` + +# 次に解く問題の予告 +- [Coin Change - LeetCode](https://leetcode.com/problems/coin-change/) +- [Binary Tree Level Order Traversal - LeetCode](https://leetcode.com/problems/binary-tree-level-order-traversal/) +- [Unique Paths - LeetCode](https://leetcode.com/problems/unique-paths/description/) diff --git a/50_pow_x_n_medium/step1.py b/50_pow_x_n_medium/step1.py new file mode 100644 index 0000000..8b16e17 --- /dev/null +++ b/50_pow_x_n_medium/step1.py @@ -0,0 +1,31 @@ +# +# @lc app=leetcode id=50 lang=python3 +# +# [50] Pow(x, n) +# + +# @lc code=start + + +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + Compute x to the power of n based on these four facts; + - x^(-n) = (1/x)^n (n>0) + - x^0 = 1 + - x^1 = x + - if n = 2*q + r, then x^(n) = (x^q)^2 * (x)^r + + """ + if n < 0: + return self.myPow(1 / x, -n) + if n == 0: + return 1.0 + if n == 1: + return x + quotient, remainder = n // 2, n % 2 + + return self.myPow(x, quotient) ** 2 * self.myPow(x, remainder) + + +# @lc code=end diff --git a/50_pow_x_n_medium/step1_iterative.py b/50_pow_x_n_medium/step1_iterative.py new file mode 100644 index 0000000..43a04aa --- /dev/null +++ b/50_pow_x_n_medium/step1_iterative.py @@ -0,0 +1,46 @@ +# +# @lc app=leetcode id=50 lang=python3 +# +# [50] Pow(x, n) +# + +# @lc code=start + + +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + Compute x to the power of n by using iterative method. + Example: x=2.0 and n = 10 + n = 2^3 + 2^1, so + x^n = x^(2^3) * x^(2^1) + x^1, ..., x^(2^k) can be computed in O(k) by using this formula; x^(2^(k+1)) = x^(2*2^k) = x^(2^k) * x^(2^k) + Therefore, x^n can be also computed in O(log(n)) + + Time Complexity: O(log(n)) + Space Complexity: O(1) + """ + if x == 0.0: + return 0.0 + if n == 0: + return 1.0 + if n < 0: + return 1 / self.myPow(x, -n) + if n == 1: + return x + + x_to_n = 1.0 # x^n + bit_position = 0 # k + power_of_two = 1 # 2^k + x_to_power_of_two = x # x^(2^k) + while power_of_two <= n: + if (n >> bit_position) & 1: + x_to_n *= x_to_power_of_two + bit_position += 1 + x_to_power_of_two = x_to_power_of_two * x_to_power_of_two + power_of_two = 2 * power_of_two + + return x_to_n + + +# @lc code=end diff --git a/50_pow_x_n_medium/step2.py b/50_pow_x_n_medium/step2.py new file mode 100644 index 0000000..785559e --- /dev/null +++ b/50_pow_x_n_medium/step2.py @@ -0,0 +1,38 @@ +# +# @lc app=leetcode id=50 lang=python3 +# +# [50] Pow(x, n) +# + +# @lc code=start +class Solution: + def myPow(self, x: float, n: int) -> float: + """ + Compute x to the power of n using doubling. + Example: x=2.0, n = 10 + n = 2^3 + 2^1 + x^n = x^(2^3) * x^(2^1) + + Each x^(2^k) can be computed recursively; + x^(2^(k+1)) = x^(2*2^k) = x^(2^k) * x^(2^k) + """ + if n == 0: + return 1.0 + if x == 0.0: + return 0.0 + if n < 0: + # x^(-n) = (1/x)^n = 1/(x^n) (n>0) + return 1 / self.myPow(x, -n) + x_to_power_of_two = x # x^(2^0) + x_to_n = 1.0 # x^n + while n > 0: + if n % 2 == 1: + x_to_n *= x_to_power_of_two + + # x^(2^(k+1)) = x^(2*2^k) = x^(2^k) * x^(2^k) + x_to_power_of_two = x_to_power_of_two * x_to_power_of_two + n //= 2 + return x_to_n + + +# @lc code=end From 94d0c3f492e7c63b1669e327afbdca0a3a39c9b1 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 24 Aug 2025 15:27:25 +0900 Subject: [PATCH 2/3] Add step3 and update README --- 50_pow_x_n_medium/README.md | 39 ++++++++++++++++++++++++++++ 50_pow_x_n_medium/step3.py | 27 +++++++++++++++++++ 50_pow_x_n_medium/step3_iterative.py | 27 +++++++++++++++++++ 3 files changed, 93 insertions(+) create mode 100644 50_pow_x_n_medium/step3.py create mode 100644 50_pow_x_n_medium/step3_iterative.py diff --git a/50_pow_x_n_medium/README.md b/50_pow_x_n_medium/README.md index d5ab3ef..c1afc0f 100644 --- a/50_pow_x_n_medium/README.md +++ b/50_pow_x_n_medium/README.md @@ -86,8 +86,47 @@ class Solution: - 空間計算量:`O(1)`。変数の使用量が`n`の大きさに依存しないためである。 ## step3 + +### 反復処理による実装 +```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1 + if n < 0: + return self.myPow(1 / x, -n) + power_of_x = x + i = 0 + two_to_i = 1 + x_to_n = 1.0 + while two_to_i <= n: + if n >> i & 1: + x_to_n *= power_of_x + i += 1 + two_to_i *= 2 + power_of_x = power_of_x * power_of_x + return x_to_n +``` + +### 再帰関数による実装 ```python +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1.0 + if x == 0.0: + return 0.0 + if n == 1: + return x + if n < 0: + n = -n + x = 1 / x + return self.myPow(x, n) + + half_n = n // 2 + remainder = n % 2 + return self.myPow(x, half_n) ** 2 * self.myPow(x, remainder) ``` ## step4 (FB) diff --git a/50_pow_x_n_medium/step3.py b/50_pow_x_n_medium/step3.py new file mode 100644 index 0000000..a19af48 --- /dev/null +++ b/50_pow_x_n_medium/step3.py @@ -0,0 +1,27 @@ +# +# @lc app=leetcode id=50 lang=python3 +# +# [50] Pow(x, n) +# + +# @lc code=start +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1.0 + if x == 0.0: + return 0.0 + if n == 1: + return x + if n < 0: + n = -n + x = 1 / x + return self.myPow(x, n) + + half_n = n // 2 + remainder = n % 2 + + return self.myPow(x, half_n) ** 2 * self.myPow(x, remainder) + + +# @lc code=end diff --git a/50_pow_x_n_medium/step3_iterative.py b/50_pow_x_n_medium/step3_iterative.py new file mode 100644 index 0000000..9dced38 --- /dev/null +++ b/50_pow_x_n_medium/step3_iterative.py @@ -0,0 +1,27 @@ +# +# @lc app=leetcode id=50 lang=python3 +# +# [50] Pow(x, n) +# + +# @lc code=start +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1 + if n < 0: + return self.myPow(1 / x, -n) + power_of_x = x + i = 0 + two_to_i = 1 + x_to_n = 1.0 + while two_to_i <= n: + if n >> i & 1: + x_to_n *= power_of_x + i += 1 + two_to_i *= 2 + power_of_x = power_of_x * power_of_x + return x_to_n + + +# @lc code=end From 3ed48221662aed75a2de339c9d6f0d60d9684ac1 Mon Sep 17 00:00:00 2001 From: Kaichi-Irie Date: Sun, 24 Aug 2025 22:38:35 +0900 Subject: [PATCH 3/3] Add explanation for bit manipulation in iterative implementation --- 50_pow_x_n_medium/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/50_pow_x_n_medium/README.md b/50_pow_x_n_medium/README.md index c1afc0f..8ad12cd 100644 --- a/50_pow_x_n_medium/README.md +++ b/50_pow_x_n_medium/README.md @@ -80,6 +80,7 @@ class Solution: - このアプローチは、`n`を2進数として捉えるものである。例えば `n=10` (2進数で`1010`) の場合、`x^10 = x^8 * x^2` となる。 - ループ内で`n`を右にシフト(`n //= 2`)しながら、最下位ビットが1かどうか(`n % 2 == 1`)を確認する。 + - 「2で割る」操作を「右にビットシフトする」操作に、「2で割った余りを調べる」操作を「最下位ビットを調べる」操作にそれぞれ置き換えて考えることで、`n`の各ビットを順に処理できることが納得できる。 - ビットが1であれば、その位置に対応する`x`のべき乗(`x`, `x^2`, `x^4`, `x^8`, ...)を結果に乗算していく。 - `x`のべき乗は、ループごとに自身を2乗することで効率的に計算される。 - 時間計算量:`O(log(n))`。ループ回数が`n`のビット数に比例するためである。