From 9aa9c27a9ae0b29fbb5691402c34a48c0bd3d7fc Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sun, 5 Apr 2026 10:39:51 +0900 Subject: [PATCH 1/5] 50. Pow --- 50/__pycache__/sol2.cpython-310.pyc | Bin 0 -> 571 bytes 50/memo.md | 55 ++++++++++++++++++++++++++++ 50/sol1.py | 11 ++++++ 50/sol1_revised.py | 11 ++++++ 50/sol2.py | 10 +++++ 5 files changed, 87 insertions(+) create mode 100644 50/__pycache__/sol2.cpython-310.pyc create mode 100644 50/memo.md create mode 100644 50/sol1.py create mode 100644 50/sol1_revised.py create mode 100644 50/sol2.py diff --git a/50/__pycache__/sol2.cpython-310.pyc b/50/__pycache__/sol2.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35f0a48a642b968173d23170d5bde85984b6a360 GIT binary patch literal 571 zcmY*Vy-ve05I+ATM9`wDR19STA)zb{LSjRJ)Pb!+s$j?xML30sBnCSb6om;M1V-M0 zkr!a~%G8aSiF0VBa@P0x{(N^n*=#NY%E$X>x=8@Otg$EzjbnsvBb|T%L7ov$Gz3A1 z;Ij+#{}UIjV}#y9N+2WxLMlkY1WmYL2^+bB3+I`Kj&QNUlJSNz8<(jp47oR?Fs@3< zu_`WX@40YP97G?X&ygyuc}J#MYl2qr0u`y~3cS({z{t!Ntc#vUhM8;~1jm;< z6{}bWvpY7g)^QqVFva3pqA^3Lam>m(Z~8&Y=2kYB~w=na(J z<hFx3{&EhLX{;*mCKkp>=*Jgm-F z(yLnzeAjqURK$4_MP@mQaw*1{)f-WCJC3ti#2IBWE)7eIvTjfhv_LD^OWDs@XuDGn cx}^3D{J(o4+x|w`C&oMYYj{|{vtzg67qet>$N&HU literal 0 HcmV?d00001 diff --git a/50/memo.md b/50/memo.md new file mode 100644 index 0000000..2fed575 --- /dev/null +++ b/50/memo.md @@ -0,0 +1,55 @@ +# 50. Pow(x, n) + +https://leetcode.com/problems/powx-n/ + +sol1.pyを迷わずかけた。n<0のケースでエラーが出たが、絶対値を取ることで処理し、それ以外ではつまらなかった。冪乗と聞いてビット演算を用いることを思いついた。 + +実行時間は0ms + +計算量は時間計算量 O(log(n)) 、空間計算量 O(1) + + +> 1/2 乗って要するにルートのことですよね。 + +https://discord.com/channels/1084280443945353267/1262688866326941718/1351739946360111126 + +https://github.com/hroc135/leetcode/pull/43/changes#diff-788d4e4d3d8fc9d0d4ea879b815deba3b2c21e39a44274fe799eaa496c5d2e43 + +組み込み関数のコードを読んでいる + +pythonのpowのドキュメント + +https://docs.python.org/ja/3/library/functions.html#pow + +powがmodの引数を取れることは知らなかった + +https://github.com/TORUS0818/leetcode/pull/47/changes +再帰で書く + +ループがかければ再帰は簡単に書けるように思う。 +再帰をはじめに思いついたときにループに直せるようにしておきたい。 + +sol2.pyを再帰でかいた。 + +「指数を半分にしつつ、底を毎回 x**2 に置き換える」やりかただとオーバーフローが生じたので、底を変えない形にした + + +> IEEE-754の内部ビットの数も覚えておくと、面接でよく分かっている風が醸せることがあります。 +> exponent が8ビットと11ビットです。符号が1ビットで残りが23ビットと52ビットです。 + +https://github.com/hroc135/leetcode/pull/43#discussion_r2002298814 + +(-1)^{符号} * 1.{仮数} * 2**{指数-bias} +bias = 2^{指数部のbit} -1 + +符号/指数/仮数 + +単精度 (float) 32 bits 1 / 8 / 23 +倍精度 (double) 64 bits 1 / 11 / 52 + +指数部の8と11だけ覚えれば良い + +この辺りの知識、頭から抜けてしまっているので復習したい + + + diff --git a/50/sol1.py b/50/sol1.py new file mode 100644 index 0000000..81ce809 --- /dev/null +++ b/50/sol1.py @@ -0,0 +1,11 @@ +class Solution: + def myPow(self, x: float, n: int) -> float: + result = 1 + shifted = abs(n) + current_pow = x + while shifted > 0: + if shifted & 1: + result *= current_pow + shifted >>= 1 + current_pow *= current_pow + return result if n >= 0 else 1 / result diff --git a/50/sol1_revised.py b/50/sol1_revised.py new file mode 100644 index 0000000..fc76616 --- /dev/null +++ b/50/sol1_revised.py @@ -0,0 +1,11 @@ +class Solution: + def myPow(self, x: float, n: int) -> float: + result = 1.0 + shifted = abs(n) + current_pow = x + while shifted > 0: + if shifted & 1: + result *= current_pow + shifted >>= 1 + current_pow *= current_pow + return result if n >= 0 else 1 / result diff --git a/50/sol2.py b/50/sol2.py new file mode 100644 index 0000000..6703b92 --- /dev/null +++ b/50/sol2.py @@ -0,0 +1,10 @@ +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1.0 + if n < 0: + return 1.0 / self.myPow(x, -n) + half = self.myPow(x, n // 2) + if n % 2 == 1: + return half * half * x + return half * half From 5838c334d938b422e1b8c6f9cfc422277011ecc6 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sun, 5 Apr 2026 19:36:13 +0900 Subject: [PATCH 2/5] Add tail_recursive --- 50/__pycache__/sol2.cpython-310.pyc | Bin 571 -> 0 bytes 50/memo.md | 24 ++++++++++++ 50/sol3.py | 55 ++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) delete mode 100644 50/__pycache__/sol2.cpython-310.pyc create mode 100644 50/sol3.py diff --git a/50/__pycache__/sol2.cpython-310.pyc b/50/__pycache__/sol2.cpython-310.pyc deleted file mode 100644 index 35f0a48a642b968173d23170d5bde85984b6a360..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 571 zcmY*Vy-ve05I+ATM9`wDR19STA)zb{LSjRJ)Pb!+s$j?xML30sBnCSb6om;M1V-M0 zkr!a~%G8aSiF0VBa@P0x{(N^n*=#NY%E$X>x=8@Otg$EzjbnsvBb|T%L7ov$Gz3A1 z;Ij+#{}UIjV}#y9N+2WxLMlkY1WmYL2^+bB3+I`Kj&QNUlJSNz8<(jp47oR?Fs@3< zu_`WX@40YP97G?X&ygyuc}J#MYl2qr0u`y~3cS({z{t!Ntc#vUhM8;~1jm;< z6{}bWvpY7g)^QqVFva3pqA^3Lam>m(Z~8&Y=2kYB~w=na(J z<hFx3{&EhLX{;*mCKkp>=*Jgm-F z(yLnzeAjqURK$4_MP@mQaw*1{)f-WCJC3ti#2IBWE)7eIvTjfhv_LD^OWDs@XuDGn cx}^3D{J(o4+x|w`C&oMYYj{|{vtzg67qet>$N&HU diff --git a/50/memo.md b/50/memo.md index 2fed575..8f0558a 100644 --- a/50/memo.md +++ b/50/memo.md @@ -52,4 +52,28 @@ bias = 2^{指数部のbit} -1 この辺りの知識、頭から抜けてしまっているので復習したい +追記: +helper 関数を用いる場合を追記 +このhelper関数は末尾再帰最適化を行うことで真価が発揮される。 +しかし、pythonではこれが行われないらしく、追加の処理が必要になる +(末尾再帰最適化が実装されていないこと) +https://docs.python.org/3.15/whatsnew/3.14.html#whatsnew314-tail-call +しかし、Pythonは末尾再帰最適化を実装しておらず、これにはTrampolineという手法を用いるのが良いらしい。 +自分で実装してみる +https://note.com/_ikb_/n/nc67f3e541f20 + + +Geminiや記事を参考に自分なりに言語化すると、 +最初に末尾再帰関数が呼ばれるとfirstcall=Trueなのでwhile文に入る。 +nonlocalを使用しているため、tail_recursiveのlocal変数は全ての呼び出しで共通である。 +そこで次の関数が呼ばれるがすでにfirstcall=Falseになっているので、引数だけを更新してfuncを返し、すぐに終了する。 +これが続いて引数を更新し続け、最後に値が返ってwhileを抜ける。 + +実際にスタックのメモリ使用量に差が出ることを確認できた。 +なお、実行時間はやや遅くなる + +functools.wrapper +関数のメタデータを新しい関数に引き継ぐ + +https://docs.python.org/ja/3/library/functools.html#functools.update_wrapper diff --git a/50/sol3.py b/50/sol3.py new file mode 100644 index 0000000..f5c57a4 --- /dev/null +++ b/50/sol3.py @@ -0,0 +1,55 @@ +from functools import wraps + + +def tail_recursive(func): + firstcall = True + params = ((), {}) + result = func + + @wraps(func) + def wrapper(*args, **kwd): + nonlocal firstcall, params, result + params = args, kwd + if firstcall: + firstcall = False + try: + result = func + while result is func: + result = func(*args, **kwd) # call fact + args, kwd = params + finally: + firstcall = True + return result + else: + return func + + return wrapper + + +class Solution: + @tail_recursive + def myPowHelper(self, base: float, exp: int, acc: float) -> float: + if exp == 0: + return acc + if exp % 2 == 1: + return self.myPowHelper(base, exp - 1, acc * base) + return self.myPowHelper(base * base, exp // 2, acc) + + def myPow(self, x: float, n: int) -> float: + if n >= 0: + return self.myPowHelper(x, n, 1.0) + return 1 / self.myPowHelper(x, -n, 1.0) + + +if __name__ == "__main__": + import sys + + _saved_limit = sys.getrecursionlimit() + + try: + sys.setrecursionlimit(10) + x, n = 1.0001, 2**31 - 1 + sol = Solution() + sol.myPow(x, n) + finally: + sys.setrecursionlimit(_saved_limit) From dd1d0dde059238ac9a398c6582e0e2eb72585a85 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sun, 5 Apr 2026 19:40:09 +0900 Subject: [PATCH 3/5] Change shifted to exponent --- 50/sol1_revised.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/50/sol1_revised.py b/50/sol1_revised.py index fc76616..8854683 100644 --- a/50/sol1_revised.py +++ b/50/sol1_revised.py @@ -1,11 +1,11 @@ class Solution: def myPow(self, x: float, n: int) -> float: result = 1.0 - shifted = abs(n) + exponent = abs(n) current_pow = x - while shifted > 0: - if shifted & 1: + while exponent > 0: + if exponent & 1: result *= current_pow - shifted >>= 1 + exponent >>= 1 current_pow *= current_pow return result if n >= 0 else 1 / result From bf38e66dc1e72145cce01bef4723675abe8c9499 Mon Sep 17 00:00:00 2001 From: tom4649 Date: Mon, 6 Apr 2026 06:14:01 +0900 Subject: [PATCH 4/5] Add suggested changes --- 50/memo.md | 5 ++++- 50/sol2_revised.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 50/sol2_revised.py diff --git a/50/memo.md b/50/memo.md index 8f0558a..0bdc9ac 100644 --- a/50/memo.md +++ b/50/memo.md @@ -31,7 +31,10 @@ https://github.com/TORUS0818/leetcode/pull/47/changes sol2.pyを再帰でかいた。 -「指数を半分にしつつ、底を毎回 x**2 に置き換える」やりかただとオーバーフローが生じたので、底を変えない形にした +「指数を半分にしつつ、底を毎回 x**2 に置き換える」やりかただとオーバーフローが生じたので、底を変えない形にした。 + +追記: +これは x`*`xとすれば解決する。`**`演算だとエラーを投げますが、*演算だとinfを返すためである。 > IEEE-754の内部ビットの数も覚えておくと、面接でよく分かっている風が醸せることがあります。 diff --git a/50/sol2_revised.py b/50/sol2_revised.py new file mode 100644 index 0000000..eab80b0 --- /dev/null +++ b/50/sol2_revised.py @@ -0,0 +1,10 @@ +class Solution: + def myPow(self, x: float, n: int) -> float: + if n == 0: + return 1.0 + if n < 0: + return 1.0 / self.myPow(x, -n) + sub_pow = self.myPow(x, n // 2) + if n % 2 == 1: + return sub_pow * sub_pow * x + return sub_pow * sub_pow From 2ab6bef9c9a4c6818d7aa610fadb86f8a2aeba6d Mon Sep 17 00:00:00 2001 From: tom4649 Date: Sun, 12 Apr 2026 19:01:12 +0900 Subject: [PATCH 5/5] Add suggested changes --- 50/sol2_revised.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/50/sol2_revised.py b/50/sol2_revised.py index eab80b0..fe2917e 100644 --- a/50/sol2_revised.py +++ b/50/sol2_revised.py @@ -3,7 +3,8 @@ def myPow(self, x: float, n: int) -> float: if n == 0: return 1.0 if n < 0: - return 1.0 / self.myPow(x, -n) + x = 1.0 / x + n = -n sub_pow = self.myPow(x, n // 2) if n % 2 == 1: return sub_pow * sub_pow * x