Skip to content

50. Pow(x, n) #46

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

50. Pow(x, n) #46

wants to merge 1 commit into from

Conversation

fhiyo
Copy link
Owner

@fhiyo fhiyo commented Jul 19, 2024

Copy link

@hayashi-ay hayashi-ay left a comment

Choose a reason for hiding this comment

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

まあ良いのではないでしょうか。3rdの解法はヘルパー関数用意しなくてもとは思いますが。

- https://discord.com/channels/1084280443945353267/1233603535862628432/1252973832822853642
- https://github.com/goto-untrapped/Arai60/pull/29

再帰は①よりこっちの方がキレイな気がした (現状これで末尾再帰最適化はされないと思うが)。
Copy link

Choose a reason for hiding this comment

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

if n == 0:
return result
if n % 2 == 0:
return self._pow(x * x, n // 2, result)
Copy link

Choose a reason for hiding this comment

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

n % 2 == 1でearly returnしたり、n & 1で判定したり色々変えてますね。
どれが好みとかあります?
early returnは特別なときにするイメージなので、この上のやつはは個人的にあんまりスッキリしなかったです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

n % 2 == 1 では判定してないですね。nが負のときに言語によって結果が-1になったりして事故の元だなと思いなるべく避けてます。

どれが好みとかあります?

うーん今回はbitが立ってるときに処理するイメージがついたのでbit演算ですかね...


### ①

対数オーダーしかスタックに積まれないので再帰で実装。PythonだとC++などと違い値の範囲を気にする場面が減って楽ではある。C++だと n = -2^31 のときに符号を逆にするとsigned intではオーバーフローするのに注意しないといけない。
Copy link

Choose a reason for hiding this comment

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

同じ処理を書くならループと再帰のどちらが認知負荷が低いかアンケートを取ったところ、ループのほうが認知負荷が低いと答えた人のほうが多かったです。
https://x.com/nodchip/status/1765579286646530148

同じ処理をループと再帰で書ける場合は、チームの平均的なメンバーにとって認知負荷が低い方を選ぶことをお勧めいたします。

class Solution:
def myPow(self, x: float, n: int) -> float:
if n < 0:
return 1 / self.myPow(x, -n)
Copy link

Choose a reason for hiding this comment

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

n = -n
x = 1 / x

としても良いと思います。

if n == 0:
return 1
ans = 1
for i in range(floor(log2(n)) + 1):
Copy link

Choose a reason for hiding this comment

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

float を使って計算するときに、計算誤差が出ないか不安になりました。計算誤差が出ないことを保証できますか?

Copy link
Owner Author

Choose a reason for hiding this comment

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

誤差が出ないかというと出るんですが (例: floor(log2(2**63 - 1)) == 63)、証明じゃなくアレですが多分問題ないと思っています...

まず誤差が大きい側に倒れても n & (1 << i) でiがnの最上位bitより大きいときは0にしかならないので、余分にループが回るだけで計算結果には影響しないです。なので、 floor(log2(2**x - α)) (α>0で小さい) の値がx-1じゃなくxになる誤差は問題ないです。
問題は floor(log2(2**x + α)) の値がxじゃなくx-1になるような事態が起こるかという話ですが、log2の実装次第 (https://github.com/python/cpython/blob/d66b06107b0104af513f664d9a5763216639018b/Modules/mathmodule.c#L709 を見るとCのlog2を使っている) ではありますが、 x == log2(2**x) <= log2(2**x + α) になるようには流石に作ってるだろう...という見立てで大丈夫だと判断しました

Copy link

Choose a reason for hiding this comment

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

IEEE754 の log の仕様見ますかねえ。

Copy link
Owner Author

Choose a reason for hiding this comment

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

https://ieeexplore.ieee.org/document/8766229 pdfダウンロードできないです...
ググると落ちてる (見ていいのか分からないですが) んですが、中身見てもあまり分かりませんでした...p.32の 5.3.3 logBFormat operations が怪しいのかな?とは思いましたが...

Copy link

Choose a reason for hiding this comment

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

あ、仕様有料でしたね。llvm とか見てみましょうか。
https://libc.llvm.org/math/log.html
なんかよく分からないですが、これ本当に大丈夫なのか不安になってきました。
しかし、試してみたところ2.0**1023までは大丈夫そうです。
これだけ考えて自信がもてなかったら安全な方に倒すんでしょうかねえ。

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど見るべき箇所参考になります、ありがとうございます。

しかし、試してみたところ2.0**1023までは大丈夫そうです。

倍精度浮動小数点の範囲なら多分大丈夫って感じですかね。

これだけ考えて自信がもてなかったら安全な方に倒すんでしょうかねえ。

そうですね、+1回余分にループ回しておけば良いでしょうからコメント書いてそうするかもです

if n == 0:
return 1
ans = 1
for i in range(floor(log2(n)) + 1):
Copy link

Choose a reason for hiding this comment

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

whlie (1 << i) <= n: として、ループの中で i += 1 しても良いと思います。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants