Skip to content
Open
Show file tree
Hide file tree
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
85 changes: 85 additions & 0 deletions 50/step1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# step1 何も見ずに解く

普通に計算しようと思ったが、n の最大が 2^31 - 1 なので線形時間だと 1 秒以内に間に合わない。

```ruby
# @param {Float} x
# @param {Integer} n
# @return {Float}
def my_pow(x, n)
res = 1
n.abs.times do
if n < 0
res /= x
else
res *= x
end
end
res
end
```

再帰とメモ化で n を半分ずつに分けて計算すれば O(logN)に計算量を落とせるので間に合うのではないかと考えた。
空間計算量も O(logN)になる

```ruby
# @param {Float} base
# @param {Integer} exsp
# @return {Float}
def my_pow(base, exsp)
exsp_to_pow = {}
calculate_my_pow = lambda do |exsp|
return 1 if exsp.zero?
return base if exsp == 1
return exsp_to_pow[exsp] if exsp_to_pow.key?(exsp)

result = 1.0
exsp_abs = exsp.abs
if exsp_abs.even?
exsp_left = exsp_right = exsp_abs / 2
else
exsp_left = exsp_abs / 2
exsp_right = (exsp_abs + 1) / 2
end
if exsp >= 0
result *= calculate_my_pow.call(exsp_left)
result *= calculate_my_pow.call(exsp_right)
else
result /= calculate_my_pow.call(exsp_left)
result /= calculate_my_pow.call(exsp_right)
end

Choose a reason for hiding this comment

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

文脈によっては二項演算子の左右を lhs, rhs と書くと伝わることがあると思いました。その場合は *= ではなく
result = calculate_my_pow.call(exsp_left) * calculate_my_pow.call(exsp_right)
と書いたらわかりやすそうです。

exsp_to_pow[exsp] = result
result
end
calculate_my_pow.call(exsp)
end
```

calculate_my_pow では`exsp`が正の場合だけ計算して、外で exsp が負であれば割るのも良いかも。
Ruby の冪乗を計算するための`Integer#**`メソッドも時間計算量 O(log)になっているか気になったのでコードを見た。
バイナリ法という方法で計算することにより、同じ値を計算する必要がないのでメモ化がいらない。
時間計算量は O(logN)
参考にすると以下のようになる。

```ruby
# @param {Float} base
# @param {Integer} exsp
# @return {Float}
def my_pow(base, exsp)
return 1 if exsp == 0
return base if exsp == 1

exsp = exsp.abs
power = 1
current_base = base
bit = exsp
while bit > 0
if (bit & 1) == 1
power *= current_base
end
current_base *= current_base
bit = bit >> 1
end
exsp < 0 ? 1 / power : power
end
```
68 changes: 68 additions & 0 deletions 50/step2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# step2 他の方の解答を見る

https://github.com/TORUS0818/leetcode/pull/47

step1の再帰の方法であったとしても、子ノードが一つしか生まれないようにすればメモ化が必要なくなるのか。
exponentが偶数のときは、exponentを半分に減らせて子ノードは1つしかできない。
奇数のときは次のノードのexponentが偶数になるようにすれば良い。

```ruby
# @param {Float} base
# @param {Integer} exponent
# @return {Float}
def my_pow(base, exponent)
return 1 if exponent.zero?
return 1.0 / my_pow(base, - exponent) if exponent < 0

if exponent.even?
return my_pow(base, exponent / 2) ** 2
else
return base * my_pow(base, exponent - 1)
end
end
```

こっちはもちろんノードが二つになるので以下だと時間以内に解けない

```ruby
# @param {Float} base
# @param {Integer} exponent
# @return {Float}
def my_pow(base, exponent)
return 1 if exponent.zero?
return 1.0 / my_pow(base, - exponent) if exponent < 0

if exponent.even?
# ノードが二つになる
return my_pow(base, exponent / 2) * my_pow(base, exponent / 2)
else
return base * my_pow(base, exponent - 1)
end
end
```

https://github.com/TORUS0818/leetcode/pull/47#discussion_r2038337006
確かに bit を 1 にして左にシフトしていくことで捜査する方が自然

```ruby
# @param {Float} base
# @param {Integer} exponent
# @return {Float}
def my_pow(base, exponent)
return 1 if exponent == 0

if exponent < 0
base = 1.0 / base
exponent = - exponent
end
pow = 1
current_base = base
bit = 1
while bit <= exponent
pow *= current_base if (exponent & bit) != 0
current_base *= current_base
bit <<= 1
end
pow
end
```
17 changes: 17 additions & 0 deletions 50/step3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# step3 3回続けて10分以内に書いてエラーを出さなければOKとする

```ruby
# @param {Float} base
# @param {Integer} exponent
# @return {Float}
def my_pow(base, exponent)
return 1 if exponent.zero?
return 1.0 / my_pow(base, -exponent) if exponent < 0

if exponent.even?
return my_pow(base, exponent / 2) ** 2
else
return base * my_pow(base, exponent - 1)

Choose a reason for hiding this comment

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

趣味の範囲ですが、
return base * my_pow(base, exponent / 2) ** 2
としても良いかなと思いました。

end
end
```
1 change: 1 addition & 0 deletions 50/step4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## step4 レビューを受けて解答を修正