Skip to content

Commit 705e474

Browse files
committed
docs: update README.md and add Sqrt(x) solution
1 parent ddc87f6 commit 705e474

8 files changed

Lines changed: 3733 additions & 402 deletions

File tree

Algorithm/BinarySearch/leetcode/69. Sqrt(x)/Claude4.6 extended/README.md

Lines changed: 394 additions & 0 deletions
Large diffs are not rendered by default.

Algorithm/BinarySearch/leetcode/69. Sqrt(x)/Claude4.6 extended/README_react.html

Lines changed: 1263 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
## 1. 問題分析結果
2+
3+
## 競技プログラミング視点
4+
5+
- 制約 `0 ≤ x ≤ 2³¹ - 1` → 探索空間は最大 `~46340`**二分探索で最大31回**のイテレーションで確定
6+
- Python の `int` は任意精度 → オーバーフロー完全ゼロ・キャスト不要(Rust/TSと異なる大きな利点)
7+
- `x >> 1` でビットシフト整数除算 → CPython の `BINARY_OP` 最適化が効く
8+
- `math.sqrt` / `**` / `pow` は使用禁止 → 純粋な整数演算のみで完結
9+
10+
## 業務開発視点
11+
12+
- 入力は `int` だが、`float``str`・負数が混入する可能性 → **実行時型ガード**が必要
13+
- `pylance` 対応: 戻り値型 `int` を明示し、`isinstance` ガードで型を narrowing
14+
- `ValueError` / `TypeError` を使い分けて呼び出し元が例外種別で判断できるように設計
15+
16+
## Python特有分析
17+
18+
- **データ構造**: 変数3つ(`low`, `high`, `mid`)のみ → `list`/`deque` 不要
19+
- **標準ライブラリ活用**: `math` モジュールは使用禁止のため不使用。ビットシフト演算子のみ
20+
- **CPython最適化**: `while` ループ + ビットシフト → 組み込み演算子レベルで最速
21+
22+
---
23+
24+
## 2. アルゴリズムアプローチ比較
25+
26+
| アプローチ | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | 標準ライブラリ活用 | CPython最適化 | 備考 |
27+
| ----------------- | ------------ | ---------- | ---------------- | ------ | ------------------ | ------------- | ----------------------- |
28+
| **線形探索** | O(√n) | O(1) || ★★★ | なし || x=2³¹で~46340回、非効率 |
29+
| **二分探索** | O(log n) | O(1) || ★★★ | なし | ✅ 適 | 整数演算のみ・誤差ゼロ |
30+
| **ニュートン法** | O(log log n) | O(1) || ★★☆ | なし | 不適 | float収束判定が曖昧 |
31+
| **`isqrt()`使用** | O(log n) | O(1) | 最低 | ★★★ | `math.isqrt` || 問題の禁止制約に抵触 |
32+
33+
---
34+
35+
## 3. 採用アルゴリズムと根拠
36+
37+
- **選択**: 二分探索(Binary Search)
38+
- **理由**: O(log n)・整数演算のみ・Python の任意精度 `int` でオーバーフロー皆無・Loop Invariant が明確で保守性最高
39+
- **Python最適化戦略**: `x >> 1`(ビットシフト)+ `(low + high) >> 1`(中点計算)で CPython のバイトコード最適化を最大活用
40+
- **トレードオフ**: ニュートン法は収束が速いが `float` の誤差管理が複雑 → 保守性 vs わずかな速度差で二分探索を選択
41+
42+
---
43+
44+
## 4. 実装コード
45+
46+
```python
47+
# Runtime 3 ms
48+
# Beats 66.79%
49+
# Memory 19.32 MB
50+
# Beats 57.57%
51+
52+
class Solution:
53+
"""
54+
Sqrt(x) — 整数平方根(切り捨て)
55+
math.sqrt / ** 演算子禁止・二分探索で実装
56+
"""
57+
58+
# ------------------------------------------------------------------ #
59+
# 業務開発版(型安全・エラーハンドリング・pylance 対応)
60+
# ------------------------------------------------------------------ #
61+
def mySqrt(self, x: int) -> int:
62+
"""
63+
非負整数 x の平方根を小数点以下切り捨てで返す。
64+
65+
Args:
66+
x: 非負整数 (0 ≤ x ≤ 2^31 - 1)
67+
68+
Returns:
69+
floor(√x) の整数値
70+
71+
Raises:
72+
TypeError: x が int でない場合
73+
ValueError: x が負数または制約超過の場合
74+
75+
Complexity:
76+
Time: O(log n) — 最大 31 回のイテレーション
77+
Space: O(1) — 固定変数のみ、追加アロケーションなし
78+
"""
79+
# ── 実行時型ガード(pylance narrowing 対応) ──────────────────
80+
if not isinstance(x, int) or isinstance(x, bool):
81+
raise TypeError(f"x must be int, got {type(x).__name__!r}")
82+
83+
if x < 0:
84+
raise ValueError(f"x must be non-negative, got {x}")
85+
86+
if x > 2**31 - 1:
87+
raise ValueError(f"x={x} exceeds constraint 2^31 - 1")
88+
89+
return self._binary_search_sqrt(x)
90+
91+
def _binary_search_sqrt(self, x: int) -> int:
92+
"""
93+
二分探索による整数平方根の計算(内部実装)。
94+
95+
Loop Invariant:
96+
low - 1 の二乗は x 以下
97+
high + 1 の二乗は x より大きい
98+
→ ループ終了時: high = floor(√x)
99+
100+
Args:
101+
x: 検証済み非負整数
102+
103+
Returns:
104+
floor(√x)
105+
"""
106+
# ── エッジケース早期リターン ──────────────────────────────────
107+
# x=0 → 0、x=1 → 1(ループを回さず即リターン)
108+
if x < 2:
109+
return x
110+
111+
# ── 二分探索 ─────────────────────────────────────────────────
112+
# 探索範囲: [1, x // 2]
113+
# 根拠: x >= 2 のとき floor(√x) <= x // 2 が常に成立
114+
low: int = 1
115+
high: int = x >> 1 # == x // 2(ビットシフトで整数除算)
116+
117+
while low <= high:
118+
# オーバーフロー不要(Python の int は任意精度)だが
119+
# ビットシフトで中点計算 → CPython バイトコード最適化
120+
mid: int = (low + high) >> 1
121+
square: int = mid * mid
122+
123+
if square == x:
124+
# 完全平方数: mid が答えそのもの
125+
return mid
126+
elif square < x:
127+
# mid が小さすぎる → 下限を引き上げ
128+
low = mid + 1
129+
else:
130+
# mid が大きすぎる → 上限を引き下げ
131+
high = mid - 1
132+
133+
# ループ終了後: high = floor(√x)
134+
# 例: x=8 → mid=2(4<8)→low=3, mid=3(9>8)→high=2
135+
# low(3) > high(2) → return 2 ✓
136+
return high
137+
138+
# ------------------------------------------------------------------ #
139+
# 競技プログラミング版(型チェック・docstring 省略・速度最優先)
140+
# ------------------------------------------------------------------ #
141+
def mySqrt_competitive(self, x: int) -> int:
142+
"""
143+
Competitive: O(log n) / O(1)
144+
エラーハンドリング省略・CPython 最速パターン
145+
"""
146+
if x < 2:
147+
return x
148+
149+
low, high = 1, x >> 1
150+
151+
while low <= high:
152+
mid = (low + high) >> 1
153+
sq = mid * mid
154+
if sq == x:
155+
return mid
156+
elif sq < x:
157+
low = mid + 1
158+
else:
159+
high = mid - 1
160+
161+
return high
162+
```
163+
164+
---
165+
166+
## 5. アルゴリズム動作トレース
167+
168+
```
169+
─── x = 4 ─────────────────────────────────────────────────────
170+
初期値: low=1, high=2
171+
172+
Iter 1: mid=1, sq=1 < 4 → low=2
173+
Iter 2: mid=2, sq=4 == 4 → return 2 ✓
174+
175+
─── x = 8 ─────────────────────────────────────────────────────
176+
初期値: low=1, high=4
177+
178+
Iter 1: mid=2, sq=4 < 8 → low=3
179+
Iter 2: mid=3, sq=9 > 8 → high=2
180+
low(3) > high(2) → return high=2 ✓
181+
182+
─── エッジケース ────────────────────────────────────────────────
183+
x=0 → early return 0
184+
x=1 → early return 1
185+
x=2 → low=1,high=1 → mid=1(1<2)→low=2 → 終了: return 1 ✓
186+
x=2147483647 → return 46340
187+
```
188+
189+
---
190+
191+
## 6. Python固有の設計ポイント整理
192+
193+
| 観点 | 実装での対応 |
194+
| -------------------------- | --------------------------------------------------------------------------- |
195+
| **オーバーフロー完全ゼロ** | Python `int` は任意精度 → `u64` キャスト(Rust)が不要 |
196+
| **型narrowing (pylance)** | `isinstance(x, int) and not isinstance(x, bool)``bool` 混入も排除 |
197+
| **ビットシフト最適化** | `x >> 1` / `(low+high) >> 1` → CPython `BINARY_OP` 命令で最速 |
198+
| **`bool` ガード** | Python では `bool``int` のサブクラス → `isinstance(x, bool)` で明示排除 |
199+
| **2パターン分離** | 業務版(型安全)と競技版(速度最優先)を明確に分離し保守性向上 |
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
## 1. 問題の分析
2+
3+
## 競技プログラミング視点での分析
4+
5+
- 探索空間 `[0, x]` は単調増加 → **二分探索**が最適(最大31回のイテレーション)
6+
- `u32::MAX = 2³¹ - 1 = 2147483647``mid * mid` は最大 `u64``~4.6 × 10¹⁸` に達するため、**`u64` へのキャスト**でオーバーフローを防止
7+
- スタックのみ使用・ヒープアロケーション完全ゼロ → キャッシュに最適
8+
- 全変数が `Copy` 型(`u32`, `u64`) → 借用・所有権の複雑性なし
9+
10+
## 業務開発視点での分析
11+
12+
- LeetCode 制約 `0 ≤ x ≤ 2³¹ - 1``i32` ではなく**`u32` が意味論的に正確**(非負整数)
13+
- `Result<u32, SqrtError>` で不正入力を型レベルで表現
14+
- `Option<u32>` ではなく `Result` を選択 → エラー種別を呼び出し元に伝達可能
15+
16+
## Rust特有の考慮点
17+
18+
- `i32` パラメータ(LeetCode signature)を `u32` に変換する際の**符号安全変換** `u32::try_from()` を活用
19+
- `mid.checked_mul(mid)` で算術オーバーフローを**型安全に**回避する選択肢もあるが、`u64` キャストの方がパフォーマンス上有利
20+
- イテレータ vs 命令型ループ: 二分探索は**収束条件が動的**なため `while` ループが最適(`Iterator` では表現しにくい)
21+
22+
---
23+
24+
## 2. アルゴリズムアプローチ比較
25+
26+
| アプローチ | 時間計算量 | 空間計算量 | Rust実装コスト | 安全性 | 可読性 | 備考 |
27+
| ------------------- | ------------ | ---------- | -------------- | ------ | ------ | ------------------------------ |
28+
| **線形探索** | O(√n) | O(1) |||| x=2³¹では~46340回、非効率 |
29+
| **二分探索** | O(log n) | O(1) |||| ✅ 整数演算のみ・誤差なし |
30+
| **ニュートン法** | O(log log n) | O(1) |||| 浮動小数点誤差・収束判定が複雑 |
31+
| **ビット操作(MSB)** | O(log n) | O(1) |||| 最上位ビットから確定、可読性低 |
32+
33+
---
34+
35+
## 3. 選択したアルゴリズムと理由
36+
37+
- **選択したアプローチ**: 二分探索(Binary Search)
38+
- **理由**:
39+
- O(log n) = **最大31回**のループで解が確定(`log₂(2³¹) ≈ 31`
40+
- 浮動小数点を**一切使わない整数演算**のみ → Rust の `u32`/`u64` 型安全性を最大活用
41+
- Loop Invariant(`high * high <= x`)がコード上で自明 → 保守性・レビュー効率が高い
42+
- **Rust特有の最適化ポイント**:
43+
- `u32` / `u64``Copy` トレイト実装済み → 借用なしで値渡し、ゼロコスト
44+
- 全変数がスタック配置 → ヒープアロケーション完全ゼロ
45+
- モノモーフィゼーション不要(ジェネリクスなし) → コンパイル結果がシンプルで高速
46+
47+
---
48+
49+
## 4. 実装コード
50+
51+
```rust
52+
impl Solution {
53+
// Runtime 0 ms
54+
// Beats 100.00%
55+
// Memory 2.07 MB
56+
// Beats 90.93%
57+
58+
/// 非負整数 x の平方根を小数点以下切り捨てで返す(二分探索)
59+
///
60+
/// # Arguments
61+
/// * `x` - 非負整数 (0 ≤ x ≤ 2^31 - 1)
62+
///
63+
/// # Returns
64+
/// `floor(√x)` を `i32` で返す
65+
///
66+
/// # Panics
67+
/// `x` が負の場合(LeetCode 制約上は発生しない)
68+
///
69+
/// # Complexity
70+
/// - Time: O(log n) — 最大 31 回のイテレーション
71+
/// - Space: O(1) — スタック変数のみ、ヒープアロケーションなし
72+
pub fn my_sqrt(x: i32) -> i32 {
73+
// ── 符号安全変換 ─────────────────────────────────────────────
74+
// LeetCode は i32 で渡すが、問題制約は非負整数 → u32 に変換
75+
// x < 0 は制約上あり得ないが、安全のため assert で明示
76+
debug_assert!(x >= 0, "x must be non-negative, got {x}");
77+
let x = x as u32;
78+
79+
// ── エッジケース早期リターン ──────────────────────────────────
80+
// x = 0 → 0、x = 1 → 1(二分探索を回さず即リターン)
81+
if x < 2 {
82+
return x as i32;
83+
}
84+
85+
// ── 二分探索 ─────────────────────────────────────────────────
86+
// 探索範囲: [1, x / 2]
87+
// 根拠: x >= 2 のとき floor(√x) <= x / 2 が常に成立
88+
let mut low: u32 = 1;
89+
let mut high: u32 = x >> 1; // x / 2(ビットシフトで整数除算)
90+
91+
// Loop Invariant:
92+
// low - 1 の二乗は x 以下
93+
// high + 1 の二乗は x より大きい
94+
// → ループ終了時: high = floor(√x)
95+
while low <= high {
96+
// オーバーフロー安全な中点計算
97+
// u32 同士の加算が u32::MAX を超える可能性があるため
98+
// low + (high - low) / 2 パターンを採用
99+
let mid: u32 = low + (high - low) / 2;
100+
101+
// mid * mid は最大 (2^31/2)^2 ≈ 1.15×10^18 → u64 が必要
102+
let square: u64 = (mid as u64) * (mid as u64);
103+
let target: u64 = x as u64;
104+
105+
match square.cmp(&target) {
106+
// 完全平方数: mid が答えそのもの
107+
std::cmp::Ordering::Equal => return mid as i32,
108+
109+
// mid が小さすぎる: 下限を引き上げ
110+
std::cmp::Ordering::Less => low = mid + 1,
111+
112+
// mid が大きすぎる: 上限を引き下げ
113+
std::cmp::Ordering::Greater => high = mid - 1,
114+
}
115+
}
116+
117+
// ループ終了後、high = floor(√x)
118+
// 例: x=8 → [low=1, high=4]
119+
// mid=2 (4<8) → low=3
120+
// mid=3 (9>8) → high=2
121+
// low(3) > high(2) → 終了: return high=2 ✓
122+
high as i32
123+
}
124+
}
125+
```
126+
127+
---
128+
129+
# 5. アルゴリズム動作トレース
130+
131+
```
132+
─── x = 4 ───────────────────────────────────────────────────
133+
early return なし(x >= 2)
134+
初期値: low=1, high=2
135+
136+
Iter 1: mid=1, square=1 < 4 → low=2
137+
Iter 2: mid=2, square=4 == 4 → return 2 ✓
138+
139+
─── x = 8 ───────────────────────────────────────────────────
140+
初期値: low=1, high=4
141+
142+
Iter 1: mid=2, square=4 < 8 → low=3
143+
Iter 2: mid=3, square=9 > 8 → high=2
144+
low(3) > high(2) → return high=2 ✓
145+
146+
─── x = 2147395600 (46340²) ────────────────────────────────
147+
完全平方数 → Equal 分岐で即 return 46340
148+
最大でも約 30 回のイテレーションで収束
149+
150+
─── エッジケース ────────────────────────────────────────────
151+
x=0 → early return 0
152+
x=1 → early return 1
153+
x=2147483647 (i32::MAX) → return 46340
154+
```
155+
156+
---
157+
158+
## 6. Rust 固有の設計ポイント整理
159+
160+
| 観点 | 実装での対応 |
161+
| ------------------------------ | ---------------------------------------------------------- |
162+
| **オーバーフロー防止(中点)** | `low + (high - low) / 2` でラップアラウンドを回避 |
163+
| **オーバーフロー防止(二乗)** | `mid as u64 * mid as u64``u64` に昇格してから乗算 |
164+
| **符号安全変換** | `x as u32`(非負制約を `debug_assert!` で文書化) |
165+
| **`match` + `Ordering`** | `if/else` より意図が明確・コンパイラの網羅性チェックが効く |
166+
| **ゼロコスト抽象化** | 全変数 `Copy`・スタックのみ・ヒープアロケーション完全ゼロ |
167+
| **`debug_assert!`** | リリースビルドで消滅するため実行時コストゼロ |

0 commit comments

Comments
 (0)