From 59b46b0e7a2b8cac8fa929e364e4fe8b01692f44 Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Sat, 8 Nov 2025 17:03:07 +0900 Subject: [PATCH] Algorithm: leetcode 7. Reverse Integer --- .../7. Reverse Integer/claude/README.html | 1919 +++++++++++++++++ .../7. Reverse Integer/claude/README.md | 246 +++ .../7. Reverse Integer/{ => gpt}/README.md | 0 .../7. Reverse Integer/gpt/README_python.md | 172 ++ .../{ => gpt}/ReverseInteger.ts | 0 .../gpt/Reverse_Integer_js.ipynb | 263 +++ .../gpt/Reverse_Integer_py.ipynb | 229 ++ .../gpt/Reverse_Integer_ts.ipynb | 254 +++ 8 files changed, 3083 insertions(+) create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/claude/README.html create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/claude/README.md rename Algorithm/Other/leetcode/7. Reverse Integer/{ => gpt}/README.md (100%) create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/gpt/README_python.md rename Algorithm/Other/leetcode/7. Reverse Integer/{ => gpt}/ReverseInteger.ts (100%) create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_js.ipynb create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_py.ipynb create mode 100644 Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_ts.ipynb diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.html b/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.html new file mode 100644 index 00000000..40030fe3 --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.html @@ -0,0 +1,1919 @@ + + + + + + LeetCode 7: Reverse Integer - 文字列反転法 + + + + + + + + + + + +
+ + +
+

+ アルゴリズム概要 +

+ +

問題

+

+ 32bit符号付き整数 + x の桁を反転し、結果が + [-2³¹, 2³¹-1] + の範囲外になる場合は + 0 + を返します。64bit整数の使用は禁止されています。 +

+ +

入出力例

+
+
Input: x = 123
+Output: 321
+
+Input: x = -123
+Output: -321
+
+Input: x = 120
+Output: 21
+
+Input: x = 2147483647
+Output: 0 (範囲外)
+
+ +

制約

+
    +
  • -2³¹ ≤ x ≤ 2³¹-1
  • +
  • 64bit整数は使用不可
  • +
+ +

戦略

+
    +
  • 文字列反転法: Pythonの高速な組み込み文字列処理を活用
  • +
  • 符号分離: 絶対値を反転後に符号を掛け戻し
  • +
  • 範囲チェック: 最後に32bit境界と比較
  • +
  • 早期リターン: 1桁の場合は処理不要
  • +
+ +

主要ポイント

+
+

+ ✓ 時間計算量: O(d) — d は桁数(最大10) +

+

+ ✓ 空間計算量: O(d) — 文字列スライスで一時領域 +

+

+ CPythonの str(), スライス + [::-1], + int() + はC実装で最適化されており、数値逐次処理より高速です。 +

+
+
+ +
+

+ ステップバイステップ解説 +

+
+
+ +
+

+ Python実装 +

+
from __future__ import annotations
+
+class Solution:
+    """
+    Reverse Integer (LeetCode #7)
+    32-bit 符号付き整数 x の数字を反転。範囲外は 0 を返す。
+    """
+
+    # 32bit境界定数(クラス定数として明示)
+    INT_MAX: int = 2_147_483_647   #  2^31 - 1
+    INT_MIN: int = -2_147_483_648  # -2^31
+
+    def reverse(self, x: int) -> int:
+        """
+        文字列反転法による最速実装
+
+        Args:
+            x: 32-bit signed integer
+
+        Returns:
+            反転後の整数(範囲外は 0)
+
+        Time: O(d), Space: O(d) — d は桁数(最大10)
+        """
+        # 基底条件: 1桁はそのまま返す(早期リターン)
+        if -9 <= x <= 9:
+            return x
+
+        # 符号を分離して絶対値の文字列を反転
+        sign: int = -1 if x < 0 else 1
+        abs_x: int = -x if x < 0 else x
+
+        # C実装の高速な文字列処理を活用
+        # [::-1] スライスで反転、int() で整数化(先頭ゼロは自動削除)
+        rev_abs: int = int(str(abs_x)[::-1])
+
+        # 符号を適用
+        result: int = rev_abs * sign
+
+        # 32bit範囲チェック(Pythonの任意精度intを手動で拘束)
+        if result < self.INT_MIN or result > self.INT_MAX:
+            return 0
+
+        return result
+
+ +
+

+ フローチャート +

+
+ + + + + + + + + + + + + + + + + 開始 + + + + + + + -9 <= x <= 9 + 1桁判定 + + + + + + はい + + + + x をそのまま返す + 早期リターン + + + + + + いいえ + + + + 符号を分離 + sign, abs_x + + + + + + + str(abs_x)[::-1] + 文字列反転 + + + + + + + int() で整数化 + 先頭ゼロ自動削除 + + + + + + + result = rev * sign + 符号適用 + + + + + + + INT_MIN <= result + <= INT_MAX + + + + + + いいえ + + + + 0 を返す + オーバーフロー + + + + + + はい + + + + result を返す + 成功 + + +
+ +

+ フローの説明:
+ 1. 1桁判定で早期リターン(-9 ≤ x ≤ 9)
+ 2. 符号を分離し、絶対値を文字列に変換
+ 3. [::-1] で反転し、int() で整数化
+ 4. 符号を適用して元の符号を復元
+ 5. 32bit範囲チェックで範囲外なら 0 を返す
+ 6. 範囲内なら result を返す +

+
+ +
+

+ 計算量分析 +

+ +

時間計算量: O(d)

+
    +
  • d は入力整数の桁数(最大10)
  • +
  • + str(), スライス + [::-1], + int() はすべて O(d) +
  • +
  • CPythonのC実装により、ループより高速
  • +
+ +

空間計算量: O(d)

+
    +
  • 文字列スライスで一時的に O(d) のメモリを使用
  • +
  • 最終結果は整数1つ
  • +
+ +

手法比較

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
手法時間空間 + Python実装 + 備考
+ 文字列反転法 + O(d)O(d)最速C実装の恩恵で高速
+ 数値のみ(divmod) + O(d)O(1)中速ループで逐次処理
負数のまま処理O(d)O(1)中速分岐が増えがち
+
+
+
+ + + + + + + + + + diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.md b/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.md new file mode 100644 index 00000000..568dea74 --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/claude/README.md @@ -0,0 +1,246 @@ +# Reverse Integer - 32bit符号付き整数の桁反転 + +

目次

+ +- [概要](#overview) +- [アルゴリズム要点(TL;DR)](#tldr) +- [図解](#figures) +- [正しさのスケッチ](#correctness) +- [計算量](#complexity) +- [Python実装](#impl) +- [CPython最適化ポイント](#cpython) +- [エッジケースと検証観点](#edgecases) +- [FAQ](#faq) + +--- + +

概要

+ +**問題**: 32bit符号付き整数 `x` の桁を反転し、結果が `[-2³¹, 2³¹-1]` の範囲外になる場合は `0` を返す。64bit整数の使用は禁止。 + +**要件**: + +- 正当性: 桁を正しく反転し、オーバーフロー時は `0` を返す +- 制約: 空間 O(1) が理想だが、Python では文字列法が実測で最速 +- 安定性: 符号を正しく保持し、先頭ゼロを自然に削除 + +--- + +

アルゴリズム要点(TL;DR)

+ +**戦略**: Pythonの高速な組み込み文字列処理を活用 + +- **文字列反転法**: `str()` → スライス `[::-1]` → `int()` で一括処理 +- **符号分離**: 絶対値を反転後に符号を掛け戻し +- **範囲チェック**: 最後に32bit境界と比較 + +**データ構造**: 文字列(一時)、整数定数 +**時間計算量**: O(d) — d は桁数(最大10) +**空間計算量**: O(d) — 文字列スライスで一時領域 + +--- + +

図解

+ +## フローチャート + +```mermaid +flowchart TD + Start[Start reverse] --> OneDigit{-9 <= x <= 9} + OneDigit -- Yes --> ReturnX[Return x] + OneDigit -- No --> ExtractSign[Extract sign and abs] + ExtractSign --> StrReverse[String reverse via slice] + StrReverse --> ToInt[Convert back to int] + ToInt --> ApplySign[Apply original sign] + ApplySign --> RangeCheck{INT_MIN <= res <= INT_MAX} + RangeCheck -- No --> ReturnZero[Return 0] + RangeCheck -- Yes --> ReturnRes[Return res] +``` + +**説明**: 1桁の場合は即座に返し、それ以外は符号を分離して絶対値の文字列を反転。整数化後に符号を戻し、32bit範囲外なら `0` を返す。 + +### データフロー図 + +```mermaid +graph LR + subgraph Input_Phase + A[Input x] --> B[Early return check] + end + subgraph Processing + B --> C[Sign extraction] + C --> D[Absolute value to string] + D --> E[Slice reverse] + E --> F[Parse to int] + end + subgraph Output_Phase + F --> G[Apply sign] + G --> H[Range validation] + H --> I[Return result or 0] + end +``` + +**説明**: 入力を早期チェック後、符号分離・文字列反転・整数化・符号適用・範囲検証の順で処理し、結果を出力。 + +--- + +

正しさのスケッチ

+ +**不変条件**: + +- 符号は分離保存され、最後に正しく適用される +- 文字列スライスは先頭ゼロを自動削除(`int()` が処理) + +**網羅性**: + +- 1桁: 早期リターンで正しく返す +- 複数桁: 文字列反転で全桁を逆順に処理 +- 負数: 符号を分離して絶対値処理後に `-1` を掛ける + +**基底条件**: `-9 ≤ x ≤ 9` の場合、桁反転は自身なので即座に返す + +**終了性**: 文字列操作は有限桁(最大10桁)で必ず終了 + +--- + +

計算量

+ +**時間計算量**: **O(d)** + +- d は入力整数の桁数(最大10) +- `str()`, スライス `[::-1]`, `int()` はすべて O(d) +- CPythonのC実装により、ループより高速 + +**空間計算量**: **O(d)** + +- 文字列スライスで一時的に O(d) のメモリを使用 +- 最終結果は整数1つ + +**比較**: 数値のみの O(1) 空間版も可能だが、Pythonでは文字列法の方が実測で速い + +--- + +

Python実装

+ +```python +from __future__ import annotations + +class Solution: + """ + Reverse Integer (LeetCode #7) + 32-bit 符号付き整数 x の数字を反転。範囲外は 0 を返す。 + """ + + # 32bit境界定数(クラス定数として明示) + INT_MAX: int = 2_147_483_647 # 2^31 - 1 + INT_MIN: int = -2_147_483_648 # -2^31 + + def reverse(self, x: int) -> int: + """ + 文字列反転法による最速実装 + + Args: + x: 32-bit signed integer + + Returns: + 反転後の整数(範囲外は 0) + + Time: O(d), Space: O(d) — d は桁数(最大10) + """ + # 基底条件: 1桁はそのまま返す(早期リターン) + if -9 <= x <= 9: + return x + + # 符号を分離して絶対値の文字列を反転 + sign: int = -1 if x < 0 else 1 + abs_x: int = -x if x < 0 else x + + # C実装の高速な文字列処理を活用 + # [::-1] スライスで反転、int() で整数化(先頭ゼロは自動削除) + rev_abs: int = int(str(abs_x)[::-1]) + + # 符号を適用 + result: int = rev_abs * sign + + # 32bit範囲チェック(Pythonの任意精度intを手動で拘束) + if result < self.INT_MIN or result > self.INT_MAX: + return 0 + + return result +``` + +**主要ステップ**: + +1. **早期リターン**: 1桁の場合は処理不要 +2. **符号分離**: 負数を絶対値化して `sign` に記録 +3. **文字列反転**: `str()[::-1]` でC実装の高速処理 +4. **整数化**: `int()` で先頭ゼロを自動削除 +5. **符号適用**: `sign` を掛けて元の符号を復元 +6. **範囲検証**: 32bit境界外なら `0` を返す + +--- + +

CPython最適化ポイント

+ +**採用した最適化**: + +- **文字列処理の活用**: CPythonの `str()`, スライス, `int()` はC実装で高速 +- **早期リターン**: 1桁の場合は条件分岐を避けて即座に返す +- **符号分離**: 負数の `%` 演算を回避し、分岐を削減 + +**追加の最適化余地**: + +- **定数のローカル化**: `INT_MAX` 等を関数内ローカル変数に束縛すると属性参照を削減できる(マイクロ最適化) + +```python +int_max = self.INT_MAX +int_min = self.INT_MIN +``` + +- **数値のみ版**: O(1) 空間が必須なら `divmod` を使った桁処理も可能だが、Pythonでは文字列法が速い + +**GIL影響**: 単一スレッドのCPU計算のみで影響なし + +--- + +

エッジケースと検証観点

+ +| ケース | 入力例 | 期待出力 | 検証ポイント | +| ------------ | ------------- | -------- | ----------------------- | +| 1桁正数 | `5` | `5` | 早期リターン | +| 1桁負数 | `-5` | `-5` | 符号保持 | +| 末尾ゼロ | `120` | `21` | 先頭ゼロ削除 | +| 負数末尾ゼロ | `-120` | `-21` | 符号+ゼロ削除 | +| 正の境界 | `2147483647` | `0` | 上限オーバーフロー | +| 負の境界 | `-2147483648` | `0` | 下限オーバーフロー | +| 境界直前 | `1534236469` | `0` | `9646324351` は上限超え | +| ゼロ | `0` | `0` | 基底条件 | +| 複数桁正数 | `123` | `321` | 通常の反転 | +| 複数桁負数 | `-123` | `-321` | 符号保持 | + +**検証方法**: + +- 単体テスト: 上記の全ケースをアサーション +- 境界値テスト: `±2³¹` 付近の値 +- ランダムテスト: `-2³¹` から `2³¹-1` の範囲でランダム生成 + +--- + +

FAQ

+ +**Q1: なぜ数値のみの O(1) 空間版ではなく文字列法を採用したのか?** +A: CPythonでは `str()`, スライス, `int()` がC実装で最適化されており、桁数が最大10と小さいため、文字列法の方が実測で速いケースが多い。競技プログラミングでは実行時間が優先される。 + +**Q2: 64bit整数を使えば簡単では?** +A: 問題制約で64bit整数の使用は禁止されている。Pythonは任意精度intだが、32bit範囲を手動でチェックする必要がある。 + +**Q3: 負数の剰余演算を避ける理由は?** +A: Pythonの `%` は負数で数学的定義(floor division)に従うため、C言語的な「toward-zero」と異なる。符号分離して絶対値で処理する方が分岐が少なく、読みやすい。 + +**Q4: 先頭ゼロはどう処理されるのか?** +A: `int("021")` は自動的に `21` になるため、特別な処理は不要。 + +**Q5: オーバーフロー検査を事前に行わない理由は?** +A: Pythonの任意精度intでは反転後の値を一旦計算してから範囲チェックする方がシンプル。数値のみ版では事前検査が必要だが、文字列法では最後に一発で判定できる。 + +**Q6: 業務開発で使う場合の注意点は?** +A: 型検証(`isinstance(x, int)`)や範囲検証を追加し、例外処理を実装する。LeetCode版は速度優先で検証を省略している。 diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/README.md b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/README.md similarity index 100% rename from Algorithm/Other/leetcode/7. Reverse Integer/README.md rename to Algorithm/Other/leetcode/7. Reverse Integer/gpt/README.md diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/gpt/README_python.md b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/README_python.md new file mode 100644 index 00000000..2fefce21 --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/README_python.md @@ -0,0 +1,172 @@ +# Reverse Integer - 32bit範囲での十進桁反転 + +## Table of Contents + +- [概要](#overview) +- [アルゴリズム要点(TL;DR)](#tldr) +- [図解](#figures) +- [正しさのスケッチ](#correctness) +- [計算量](#complexity) +- [Python 実装](#impl) +- [CPython最適化ポイント](#cpython) +- [エッジケースと検証観点](#edgecases) +- [FAQ](#faq) + +

概要

+ +- **プラットフォーム/ID**: LeetCode 7 +- **問題タイトル**: Reverse Integer +- **要約**: 符号付き 32bit 整数 `x` の十進数の桁を逆順にした整数を返す。結果が 32bit 範囲 `[-2^31, 2^31-1]` を外れる場合は `0` を返す。 +- **入出力仕様(簡潔)**: + - 入力: `x: int` + - 出力: 反転後の `int`(範囲外なら `0`) + +- **関数シグネチャ(LeetCode準拠)**: + - `class Solution: def reverse(self, x: int) -> int:` + +- **想定データ構造**: 整数(十進表現)。補助構造は不要。 +- **代表例**: + - `x = 123` → `321` + - `x = -123` → `-321` + - `x = 120` → `21` + +- **制約**: `-2^31 <= x <= 2^31 - 1`、任意精度 `int` の Python でも 32bit に**制限**して判定する。 + +

アルゴリズム要点(TL;DR)

+ +- **戦略**: Pythonでは C 実装の `str`/スライス/`int` が高速なため、**文字列反転**で最短化し、最後に 32bit 範囲チェック。 +- **手順**: + 1. `x` の符号を分離し、絶対値の十進文字列を `[::-1]` で反転。 + 2. `int(...)` に戻して符号を掛け戻す。 + 3. 32bit 範囲 `[-2^31, 2^31-1]` から外れていれば `0` を返す。 + +- **計算量**: Time **O(d)**、Space **O(d)**(d は桁数、最大 10) +- **安定性**: 例外を投げず、範囲外は必ず `0` を返す。 + +

図解

+ +## **フローチャート** + +```mermaid +flowchart TD + Start[Start] --> Sign[Extract sign] + Sign --> Abs[Take absolute x] + Abs --> RevStr[Reverse decimal string] + RevStr --> Parse[Int cast] + Parse --> Apply[Apply sign] + Apply --> Check{In 32bit range} + Check -- Yes --> Ret[Return result] + Check -- No --> Zero[Return 0] +``` + +短い説明: 符号分離→絶対値化→文字列反転→整数変換→符号適用→32bitチェックの順で処理します。 + +## **データフロー図** + +```mermaid +graph LR + Inp[Input x] --> Sgn[Sign split] + Sgn --> AbsV[Abs value] + AbsV --> StrV[To string] + StrV --> RStr[Reverse slice] + RStr --> IntV[Int parse] + IntV --> ReSign[Multiply sign] + ReSign --> Range[Range check] + Range --> Out[Output] +``` + +短い説明: データは文字列化→反転→整数化と一方向に流れ、最後に範囲でフィルタします。 + +

正しさのスケッチ

+ +- **不変条件**: 反転操作は十進の桁順序を逆転するが、桁集合(各桁の多重集合)は不変。 +- **網羅性**: 正・負・零を別々に扱い、符号は最後に掛け戻すため、すべての整数に対して定義される。 +- **基底条件**: 1桁の整数は反転しても同値(実装ではそのまま返すか、一般手順で同値になる)。 +- **終了性**: 反転は有限桁の文字列処理であり、必ず終了する。 +- **範囲検証**: 出力が `[-2^31, 2^31-1]` を外れる場合に `0` を返すため、問題仕様に合致。 + +

計算量

+ +- **Time**: O(d) — 文字列化・反転・整数化はいずれも桁数に線形。 +- **Space**: O(d) — 反転文字列が一時的に必要。 +- **備考**: d ≤ 10(32bit 整数の最大桁数)なので、実用上のオーバーヘッドは極小。 + +

Python 実装

+ +> 要件: LeetCode クラス形式、pylance で型エラーなし、外部副作用なし(Pure)。 + +```python +from __future__ import annotations +from typing import Final + +class Solution: + """ + Reverse Integer (#7) + 32-bit 符号付き整数 x の桁を反転。範囲外なら 0 を返す。 + """ + + # 32bit 範囲を Final 定数として定義(pylance に優しい明示型) + INT_MAX: Final[int] = 2_147_483_647 # 2^31 - 1 + INT_MIN: Final[int] = -2_147_483_648 # -2^31 + + def reverse(self, x: int) -> int: + """ + 文字列反転で最短ルート + Time: O(d), Space: O(d) d は桁数(最大 10) + + Args: + x: 32-bit signed integer + + Returns: + 反転後の整数(範囲外は 0) + """ + # 1桁は早期リターン + if -9 <= x <= 9: + return x + + # 符号分離 + sign: int = -1 if x < 0 else 1 + abs_x: int = -x if x < 0 else x + + # 十進文字列を反転し整数化(C 実装の高速パス) + rev_abs: int = int(str(abs_x)[::-1]) + + # 符号を掛け戻し + res: int = rev_abs * sign + + # 32bit 範囲チェック + if res < self.INT_MIN or res > self.INT_MAX: + return 0 + return res +``` + +- 実装は**純粋関数的**(引数のみを使用・外部状態変更なし)。 +- 主要ステップ(符号分離→反転→範囲判定)に沿ってコメントを配置。 + +

CPython最適化ポイント

+ +- **C 実装の活用**: `str()`, スライス `[::-1]`, `int()` は C で最適化され、短い整数ではループより速い。 +- **属性アクセス削減**: `INT_MAX/INT_MIN` を `Final` でクラス定数化。局所変数へ束縛してもよいが、桁数が小さいため差は僅少。 +- **例外非使用**: 正常系は例外を投げず直線実行。 +- **代替案(数値のみ)**: 逐次 `divmod` で桁処理+`INT_MAX` のみ事前オーバーフロー判定でも実装可(O(1) 追加メモリ)。Python では実測で文字列法が有利なことが多い。 + +

エッジケースと検証観点

+ +- `0`(そのまま `0`) +- 1桁正負: `5` → `5`, `-8` → `-8` +- 末尾ゼロ: `120` → `21`, `-120` → `-21` +- 符号: `-123` → `-321` +- 上限超過: `1534236469` → 反転 `9646324351` は `INT_MAX` 超過 → `0` +- 境界: `2147483647` → 反転は超過 → `0`、`-2147483648` → 反転は超過 → `0` + +

FAQ

+ +- **Q. 文字列を使うのは反則では?** + **A.** 問題は文字列使用を禁止していません。Python では C 実装の高速化恩恵が大きく、最短経路です。 + +- **Q. 数値演算のみで書くとしたら?** + **A.** `sign = -1 if x < 0 else 1`、`n = abs(x)` として `while n: rev = rev * 10 + n % 10; n //= 10`。 + 最後に `rev * sign`。`INT_MAX` 事前チェックでオーバーフローを防ぎます。 + +- **Q. なぜ 32bit チェックが必要?** + **A.** Python の `int` は任意精度ですが、問題仕様が 32bit 範囲を要求するためです。範囲外は `0` を返します。 diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/ReverseInteger.ts b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/ReverseInteger.ts similarity index 100% rename from Algorithm/Other/leetcode/7. Reverse Integer/ReverseInteger.ts rename to Algorithm/Other/leetcode/7. Reverse Integer/gpt/ReverseInteger.ts diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_js.ipynb b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_js.ipynb new file mode 100644 index 00000000..7bbeb9fa --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_js.ipynb @@ -0,0 +1,263 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b0c66304", + "metadata": {}, + "source": [ + "# 0. 実行環境\n", + "\n", + "* Language/Runtime: **JavaScript (Node.js v22.14.0)**(コードは Node 18+ 互換)\n", + "* Module: **CommonJS**\n", + "* 外部ライブラリ: **禁止(Node 標準のみ)**\n", + "* CI前提: `node solution.js` 実行可能(LeetCode では関数のみ評価)\n", + "\n", + "# 1. 問題の分析\n", + "\n", + "## 競技プログラミング視点\n", + "\n", + "* **目的**: 32bit 符号付き整数 `x` の十進数字を逆順にして返す。範囲外(`[-2^31, 2^31-1]`)に出たら `0`。\n", + "* **鍵ポイント**: 乗算/加算の都度オーバーフローを事前検知(`rev` が境界を超える直前で検査)。\n", + "* **I/O**: LeetCode 形式の関数のみ。副作用なし。\n", + "\n", + "## 業務開発視点\n", + "\n", + "* **保守性**: `INT_MAX/INT_MIN` を定数化。境界ロジックはコメントで明示。\n", + "* **例外/入力検証**: 数値・整数・範囲の軽量チェックを早期に実施(ホットパス外)。不正入力は `TypeError / RangeError` を送出。\n", + "\n", + "## JavaScript特有の考慮点\n", + "\n", + "* **V8 最適化**: 単純な `while` ループ+プリミティブ `number` のみ。hidden class の変動なし。\n", + "* **整数切り捨て**: `x = (x / 10) | 0` でゼロ方向切捨て(32bit 符号演算)を安定化。\n", + "* **GC対策**: 一時オブジェクト生成ゼロ。クロージャ不使用。\n", + "\n", + "---\n", + "\n", + "# 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | JS実装コスト | 可読性 | 備考 |\n", + "| -------------------------- | ----: | ----: | ------: | --- | ----------------- |\n", + "| 方法A(桁ポップ&プッシュ、逐次オーバーフロー検査) | O(d) | O(1) | 低 | 中 | `rev` を都度チェック(推奨) |\n", + "| 方法B(文字列反転→数値化→範囲判定) | O(d) | O(d) | 低 | 高 | 文字列化で一時メモリ増、不要 |\n", + "| 方法C(配列バッファ) | O(d) | O(d) | 中 | 中 | Bの下位互換、利点なし |\n", + "\n", + "`d` は桁数(最大 10)。\n", + "\n", + "---\n", + "\n", + "# 3. 選択したアルゴリズムと理由\n", + "\n", + "* **選択**: 方法A(桁ポップ&プッシュ)\n", + "* **理由**: 追加メモリ不要、毎ステップで安全に 32bit 境界を検査でき、最速・最小。\n", + "* **JS最適化ポイント**:\n", + "\n", + " * `while (x !== 0)` の単純ループ\n", + " * `x % 10` で下位桁抽出、`(x / 10) | 0` でゼロ方向切捨て\n", + " * 事前境界チェック:`rev` が `INT_MAX/10`(または `INT_MIN/10`)を超えるか、等しくて末尾桁条件を満たさない場合は即 `0`\n", + "\n", + "---\n", + "\n", + "# 4. コード実装(solution.js)\n", + "\n", + "```javascript\n", + "'use strict';\n", + "// Module: CommonJS(ローカル実行用に module.exports を併記。LeetCode では無視されます。)\n", + "\n", + "/**\n", + " * Reverse Integer (LeetCode #7) - Pure Function\n", + " *\n", + " * 与えられた 32bit 符号付き整数 x の数字を反転して返す。\n", + " * 反転結果が 32bit 範囲 [-2^31, 2^31-1] を外れたら 0 を返す。\n", + " *\n", + " * 計算量: 時間 O(d), 空間 O(1) (d は桁数 ≤ 10)\n", + " *\n", + " * @param {number} x - 入力整数\n", + " * @returns {number} 反転後の整数(範囲外は 0)\n", + " * @throws {TypeError} 型が number でない / 非有限値 / 整数でない\n", + " * @throws {RangeError} 入力が 32bit 符号付き整数範囲外\n", + " */\n", + "function reverse(x) {\n", + " // ---- 入力検証(ホットパス外の早期チェック)----\n", + " if (typeof x !== 'number' || !Number.isFinite(x)) {\n", + " throw new TypeError('Input must be a finite number');\n", + " }\n", + " if (!Number.isInteger(x)) {\n", + " throw new TypeError('Input must be an integer');\n", + " }\n", + " const INT_MAX = 2147483647; // 2^31 - 1\n", + " const INT_MIN = -2147483648; // -2^31\n", + " if (x < INT_MIN || x > INT_MAX) {\n", + " throw new RangeError('Input out of 32-bit signed integer range');\n", + " }\n", + "\n", + " // ---- 本処理(桁ポップ&プッシュ;逐次オーバーフロー検査)----\n", + " let rev = 0;\n", + " // 以降、x は 32bit 整数範囲内で推移する。ゼロ方向に切り捨てるため (x/10)|0 を使用。\n", + " while (x !== 0) {\n", + " // 下位桁を取り出し\n", + " const pop = x % 10; // JS の % は符号を保つ(負数でもOK)\n", + " x = (x / 10) | 0; // toward-zero truncation(32bit 符号演算)\n", + "\n", + " // オーバーフロー事前検知(10倍+pop の前に境界比較)\n", + " // 上限チェック:rev > INT_MAX/10 なら次で確実に溢れる\n", + " if (rev > 214748364 || (rev === 214748364 && pop > 7)) {\n", + " return 0;\n", + " }\n", + " // 下限チェック:rev < INT_MIN/10 なら次で確実に溢れる\n", + " if (rev < -214748364 || (rev === -214748364 && pop < -8)) {\n", + " return 0;\n", + " }\n", + " rev = rev * 10 + pop;\n", + " }\n", + " return rev;\n", + "}\n", + "\n", + "// LeetCode では関数定義のみで可。ローカル実行やCI向けにエクスポートを同梱。\n", + "module.exports = { reverse };\n", + "\n", + "Analyze Complexity\n", + "Runtime 57 ms\n", + "Beats 43.86%\n", + "Memory 55.49 MB\n", + "Beats 74.57%\n", + "\n", + "```\n", + "\n", + "---\n", + "\n", + "# 5. 追加メモ(JS最適化チェックリスト)\n", + "\n", + "* ループは `while`/`for` の基本形を使用(`map/forEach` 非採用)。\n", + "* 追加オブジェクト・配列の生成ゼロ。\n", + "* 定数は先頭で確定、プロパティの動的追加なし(hidden class 安定)。\n", + "* 数値のみを扱い単型維持、クロージャ不使用で GC 圧を回避。\n", + "* 例外はホットパスの外で早期判定(正常系は分岐最小化)。\n", + "\n", + "---\n", + "\n", + "# 1. 問題の分析\n", + "\n", + "## 競技プログラミング視点での分析\n", + "\n", + "* すでに O(d), O(1) で実装済みならボトルネックは**処理本体の分岐・型変換コスト**。\n", + "* LeetCode 入力は常に妥当(型/範囲チェック不要)なので、**バリデーションを外してホットパスを最短化**する余地があります。\n", + "* `|0` 等の**ビット演算は ToInt32 変換を誘発**し、数値タグ(Smi/HeapNumber)の行き来でデオプトが出ることがあります。\n", + "\n", + "## 業務開発視点での分析\n", + "\n", + "* プロダクションでは検証を残す価値あり。ただし LeetCode では**最速優先で検証を削除**する構成が実利的。\n", + "* 可読性は維持しつつ、**境界定数の外出し**と**分岐の並び順**(ヒット頻度の高いケースを先)で分岐予測を助けます。\n", + "\n", + "## JavaScript特有の考慮点\n", + "\n", + "* `x = (x / 10) | 0` よりも **`x = (x - pop) / 10`** は余計な int32 化を避け、V8 上で安定して速い傾向。\n", + "* `Math.trunc` は関数呼び出しオーバヘッドがあり、短いループでは不利になりがち。\n", + "* 文字列化(`String/Array`)は GC プレッシャ増。数値演算だけで完結させるのが安全。\n", + "\n", + "---\n", + "\n", + "# 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | JS実装コスト | 可読性 | 備考 | |\n", + "| --------------------- | -------: | ----: | ------: | --- | ------------------- | ----------------- |\n", + "| 方法A(数値のみ、事前OF検査)※最適化版 | O(d) | O(1) | 低 | 中 | `x=(x-pop)/10`、検証削除 | |\n", + "| 方法B(従来A:` | 0` で切捨て) | O(d) | O(1) | 低 | 中 | ToInt32 による型変換が発生 |\n", + "| 方法C(文字列反転) | O(d) | O(d) | 低 | 高 | メモリ圧・速度ともに不利 | |\n", + "\n", + "---\n", + "\n", + "# 3. 選択したアルゴリズムと理由\n", + "\n", + "* **選択したアプローチ**: 方法A(数値のみ、事前オーバーフロー検査/入力検証レス)\n", + "* **理由**:\n", + "\n", + " * LeetCode 前提で入力検証を省略し**ホットパス最短化**。\n", + " * **ビット演算排除**で不要な ToInt32 を避け、JIT の最適化を阻害しにくい。\n", + " * 追加メモリ 0、分岐は 2 条件のみで**分岐予測が当たりやすい**。\n", + "* **JavaScript特有の最適化ポイント**:\n", + "\n", + " * `pop = x % 10` → `x = (x - pop) / 10`(toward-zero を保証、型安定)\n", + " * `MAX_DIV10/MIN_DIV10` 等の**定数は関数外に固定**(hidden class 安定・再割当なし)\n", + " * ループは `while (x !== 0)` の単純形。クロージャ・一時配列ゼロ。\n", + "\n", + "---\n", + "\n", + "# 4. コード実装(solution.js)\n", + "\n", + "```javascript\n", + "'use strict';\n", + "// Module: CommonJS(LeetCode は関数のみ評価/ローカル実行やCI向けにexport)\n", + "\n", + "// 32-bit 境界定数(関数外に固定して再生成回避)\n", + "const INT_MAX = 2147483647; // 2^31 - 1\n", + "const INT_MIN = -2147483648; // -2^31\n", + "const MAX_DIV10 = 214748364; // Math.trunc(INT_MAX / 10)\n", + "const MIN_DIV10 = -214748364; // Math.trunc(INT_MIN / 10)\n", + "const MAX_LAST = 7; // INT_MAX % 10\n", + "const MIN_LAST = -8; // INT_MIN % 10\n", + "\n", + "/**\n", + " * Reverse Integer (LeetCode #7) - Judge-Optimized, Pure\n", + " *\n", + " * 計算量: 時間 O(d), 空間 O(1) (d ≤ 10)\n", + " * 入力は LeetCode で保証されるため、検証と例外は省略(速度優先)。\n", + " *\n", + " * @param {number} x\n", + " * @returns {number}\n", + " */\n", + "function reverse(x) {\n", + " let rev = 0;\n", + " while (x !== 0) {\n", + " // 符号付き % は負数でも下位桁を返す\n", + " const pop = x % 10;\n", + "\n", + " // 事前オーバーフローチェック(rev*10 + pop を行う前)\n", + " if (rev > MAX_DIV10 || (rev === MAX_DIV10 && pop > MAX_LAST)) return 0;\n", + " if (rev < MIN_DIV10 || (rev === MIN_DIV10 && pop < MIN_LAST)) return 0;\n", + "\n", + " rev = rev * 10 + pop;\n", + "\n", + " // toward-zero:bit演算を避けて型安定のまま更新\n", + " x = (x - pop) / 10;\n", + " }\n", + " return rev;\n", + "}\n", + "\n", + "module.exports = { reverse };\n", + "\n", + "Analyze Complexity\n", + "Runtime 45 ms\n", + "Beats 90.39%\n", + "Memory 55.13 MB\n", + "Beats 88.26%\n", + "\n", + "```\n", + "\n", + "---\n", + "\n", + "# 5. 追加メモ(JS最適化チェックリスト)\n", + "\n", + "* **入力検証は省略**(LeetCode前提)。業務コードでは別レイヤで実施。\n", + "* **ビット演算を排除**して ToInt32 強制変換を避ける。\n", + "* **定数は関数外**で一度だけ確定。\n", + "* **関数は単純なスカラー演算のみ**。クロージャ・配列・文字列生成なし。\n", + "* **分岐順序**は「正常系が通りやすい」並び(`rev > MAX_DIV10` 先)。\n", + "\n", + "---\n", + "\n", + "## ひとこと\n", + "\n", + "* いまの 57ms / 55.49MB は十分に優秀ですが、上記の**検証削除+`(x - pop)/10`**の置き換えで、環境次第では**数%〜十数%**の微改善が期待できます。\n", + "* さらに詰めるなら、関数定義を**ファイル先頭**に固定・`'use strict'` 維持・他のグローバルを増やさない(IC 汚染回避)といった**周辺ノイズの削減**も有効です。\n", + "\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_py.ipynb b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_py.ipynb new file mode 100644 index 00000000..5cfcba0f --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_py.ipynb @@ -0,0 +1,229 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b0c66304", + "metadata": {}, + "source": [ + "### 1. 問題分析結果\n", + "\n", + "#### 競技プログラミング視点\n", + "\n", + "* **最速手法**: 文字列化を避け、数値のみで桁を「ポップ→プッシュ」。\n", + " `rev = rev * 10 + pop` を行う直前に **上限側のみ**(`INT_MAX`)の事前オーバーフロー検査を実施。\n", + "* **メモリ最小化**: 追加配列・文字列生成なしの **O(1)**。ループは整数演算のみ。\n", + "* **分岐最小化**: 負数の `%` を避けるために **絶対値で処理**し、下限チェックを不要化(分岐削減)。\n", + "\n", + "#### 業務開発視点\n", + "\n", + "* **型安全性・保守性**: `int` 専用。境界定数をクラス定数化し命名で明快に。\n", + "* **エラーハンドリング**: 業務向けには型・範囲検証を備えたメソッドを別途用意(LeetCode 実行では最速版を使用)。\n", + "\n", + "#### Python特有分析\n", + "\n", + "* **CPython特性**: Python の `int` は任意精度だが、**32bit 範囲を手動で拘束**する。\n", + "* **最適化**: ループ内で使う定数・関数参照を**ローカル変数に束縛**して属性ルックアップを削減。`//` と `%` は C 実装で高速。\n", + "* **GIL**: 単一スレッド・CPU計算のみで影響なし。\n", + "\n", + "---\n", + "\n", + "### 2. 採用アルゴリズムと根拠\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | 標準ライブラリ活用 | CPython最適化 | 備考 |\n", + "| ----------------------- | ----: | ----: | ----------: | --- | -------------- | ---------- | ------------------ |\n", + "| 方法A:数値のみ・絶対値化・上限側のみOF検査 | O(d) | O(1) | 低 | 高 | 不要 | 適 | **採用**。分岐最小・GC負荷最小 |\n", + "| 方法B:負数のまま処理(上下限OF検査) | O(d) | O(1) | 低 | 中 | 不要 | 適 | 分岐が増えがち |\n", + "| 方法C:文字列反転→数値化 | O(d) | O(d) | 低 | 高 | `str`/`[::-1]` | 不適 | GC/一時オブジェクトで不利 |\n", + "\n", + "※ `d` は桁数(最大 10)。\n", + "\n", + "---\n", + "\n", + "### 3. 実装パターン\n", + "\n", + "* **競技版(LeetCode用)**: 型/範囲チェックなしで最短径路。\n", + "* **業務版(参考)**: 例外による防御的プログラミングを `_reverse_checked` で提供。\n", + "\n", + "---\n", + "\n", + "### 4. 実装(LeetCode クラス形式・型付き・pylance対応)\n", + "\n", + "```python\n", + "from __future__ import annotations\n", + "\n", + "class Solution:\n", + " \"\"\"\n", + " Reverse Integer (#7)\n", + " 32-bit 符号付き整数 x の数字を反転。範囲外は 0 を返す。\n", + " \"\"\"\n", + "\n", + " # クラス定数(読みやすさと再利用のため)\n", + " INT_MAX: int = 2_147_483_647 # 2^31 - 1\n", + " INT_MIN: int = -2_147_483_648 # -2^31\n", + " _MAX_DIV10: int = 214_748_364 # INT_MAX // 10\n", + " _MAX_LAST: int = 7 # INT_MAX % 10\n", + "\n", + " def reverse(self, x: int) -> int:\n", + " \"\"\"\n", + " 競技向け最速実装(LeetCode用)\n", + " Time: O(d), Space: O(1) — d は桁数(≤10)\n", + "\n", + " Args:\n", + " x: 32-bit signed integer\n", + "\n", + " Returns:\n", + " 反転後の整数(範囲外は 0)\n", + " \"\"\"\n", + " # 符号を外して絶対値で処理(負数の % や下限チェックを回避)\n", + " sign: int = -1 if x < 0 else 1\n", + " n: int = -x if x < 0 else x\n", + "\n", + " rev: int = 0\n", + " # ループ内で使う定数をローカルに束縛(属性参照を避ける)\n", + " max_div10: int = 214_748_364\n", + " max_last: int = 7\n", + "\n", + " # 数値のみでポップ&プッシュ\n", + " while n:\n", + " pop: int = n % 10\n", + "\n", + " # 事前オーバーフロー検査(rev*10 + pop > INT_MAX?)\n", + " if rev > max_div10 or (rev == max_div10 and pop > max_last):\n", + " return 0\n", + "\n", + " rev = rev * 10 + pop\n", + " n //= 10 # toward-zero(n は非負なので // でOK)\n", + "\n", + " return rev * sign\n", + "\n", + " # --- 以下は業務向けの参考実装(LeetCodeでは未使用) ---\n", + "\n", + " def _reverse_checked(self, x: int) -> int:\n", + " \"\"\"\n", + " 業務向け:入力検証付き(型・範囲)。失敗時は例外。\n", + " \"\"\"\n", + " if not isinstance(x, int):\n", + " raise TypeError(\"x must be int\")\n", + " if x < self.INT_MIN or x > self.INT_MAX:\n", + " raise ValueError(\"x out of 32-bit signed integer range\")\n", + " return self.reverse(x)\n", + "\n", + "Analyze Complexity\n", + "Runtime 44 ms\n", + "Beats 23.31%\n", + "Memory 17.72 MB\n", + "Beats 57.29%\n", + "\n", + "```\n", + "\n", + "---\n", + "\n", + "### 5. 検証(思考実験レベル)\n", + "\n", + "* **境界値**: `0`, `5`, `-5`, `120`→`21`, `-120`→`-21`\n", + "* **INT境界**: `1534236469` → `0`(`9646324351` は上限超え)\n", + "* **極値**: `2_147_483_647` → `0`, `-2_147_483_648` → `0`\n", + "* **符号**: `-123` → `-321`, `123` → `321`\n", + "\n", + "> 競技版は例外を投げず、要件どおり範囲外は **0** を返します。\n", + "\n", + "### 1. 多角的問題分析\n", + "\n", + "* **競技プログラミング視点**\n", + " Python では C 実装の `str()`・スライス・`int()` が非常に高速です。数値演算で毎桁 `%` と `//` を回すより、**文字列で反転 → 整数化**の方が実測で速くなることが多いです(桁数最大 10 のためメモリ影響は極小)。よって最速狙いでは**文字列法**を採用します。\n", + "\n", + "* **業務開発視点**\n", + " 境界定数を明示、ロジックを小さく保ち、早期リターンで読みやすさを確保。任意精度 `int` であっても**32bit 範囲チェック**を厳密に実施。\n", + "\n", + "* **Python特有考慮**\n", + " CPython では組み込み処理(文字列変換・スライス・整数化)が C で最適化済み。ループより**一括処理**が有利。GIL の影響はなし。属性参照を減らすために**ローカル変数に束縛**するのが定石ですが、本実装は最小限の行数で十分高速です。\n", + "\n", + "---\n", + "\n", + "### 2. アルゴリズム比較表\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | 標準ライブラリ活用 | CPython最適化 | 備考 |\n", + "| ----------------------- | ----: | ----: | ----------: | --- | ----------- | ---------- | ------------- |\n", + "| 方法A:**文字列反転 → int** | O(d) | O(d) | 低 | 高 | `str`, スライス | 適 | **推奨(最速狙い)** |\n", + "| 方法B:数値のみ(`divmod` で桁処理) | O(d) | O(1) | 低 | 中 | 不要 | 適 | C での一括処理に劣る傾向 |\n", + "| 方法C:負数のまま上下限チェック | O(d) | O(1) | 低 | 中 | 不要 | 適 | 分岐増でわずかに不利 |\n", + "\n", + "※ d は桁数(最大 10)\n", + "\n", + "---\n", + "\n", + "### 3. 採用アルゴリズムと根拠\n", + "\n", + "* **選択**: 方法A(文字列反転)\n", + "* **理由**:\n", + "\n", + " * C 実装の高速化恩恵で **最短実行時間**になりやすい\n", + " * コードが短く**保守性・可読性**が高い\n", + " * 32bit 範囲チェックを最後に一発で判定できる\n", + "* **最適化ポイント**:\n", + "\n", + " * 1桁は**早期リターン**\n", + " * 符号分離して絶対値を反転、最後に符号を掛け戻し\n", + " * 範囲外は 0 を返す仕様に忠実\n", + "\n", + "---\n", + "\n", + "### 4. 実装(LeetCode クラス形式・pylance対応)\n", + "\n", + "```python\n", + "from __future__ import annotations\n", + "\n", + "class Solution:\n", + " \"\"\"\n", + " Reverse Integer (#7)\n", + " 32-bit 符号付き整数 x の数字を反転。範囲外は 0。\n", + " \"\"\"\n", + "\n", + " INT_MAX: int = 2_147_483_647 # 2^31 - 1\n", + " INT_MIN: int = -2_147_483_648 # -2^31\n", + "\n", + " def reverse(self, x: int) -> int:\n", + " \"\"\"\n", + " 競技向け最速(文字列反転)実装\n", + " Time: O(d), Space: O(d) ※ d は桁数(最大 10)\n", + " \"\"\"\n", + " # 1桁はそのまま返す(早期リターン)\n", + " if -9 <= x <= 9:\n", + " return x\n", + "\n", + " # 符号分離して絶対値の文字列を反転(C 実装で高速)\n", + " sign: int = -1 if x < 0 else 1\n", + " rev_abs: int = int(str(-x if x < 0 else x)[::-1])\n", + "\n", + " res: int = rev_abs * sign\n", + "\n", + " # 32bit 範囲チェック(任意精度 int だが仕様に従い拘束)\n", + " return 0 if res < self.INT_MIN or res > self.INT_MAX else res\n", + "\n", + "Analyze Complexity\n", + "Runtime 34 ms\n", + "Beats 84.54%\n", + "Memory 17.92 MB\n", + "Beats 16.77%\n", + "\n", + "```\n", + "\n", + "---\n", + "\n", + "### 5. 追加の改善余地(必要に応じて)\n", + "\n", + "* **数値のみ版が必要**な場合は、`divmod` を使い `while n:` で桁処理+上限側のみオーバーフロー検査(`rev > 214748364 or (rev == 214748364 and pop > 7)`) にするのが最速です。\n", + "* マイクロ最適化として、境界定数をローカル変数へ束縛(`INT_MAX` 等を局所化)すると属性参照を減らせます。\n", + "\n", + "今回の置き換え(文字列反転)は、Python において多くの環境で**数値逐次版より速い**ことが多く、提示の 44ms からのさらなる短縮が期待できます。\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_ts.ipynb b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_ts.ipynb new file mode 100644 index 00000000..8e2520ea --- /dev/null +++ b/Algorithm/Other/leetcode/7. Reverse Integer/gpt/Reverse_Integer_ts.ipynb @@ -0,0 +1,254 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b0c66304", + "metadata": {}, + "source": [ + "### 1. 問題の分析\n", + "\n", + "* **競技プログラミング視点**\n", + "\n", + " * 10 進の各桁を「ポップ → `rev = rev * 10 + pop`」で積み上げ、都度オーバーフローを事前検知。\n", + " * 文字列化は避け、数値演算のみで O(d)・O(1) を維持(d は桁数 ≤ 10)。\n", + " * `rev*10 + pop` 実行前に `rev` を `INT_MAX/10`・`INT_MIN/10` と末尾桁で判定し、超過なら即 0。\n", + "\n", + "* **業務開発視点**\n", + "\n", + " * 型は `number`(TS の静的型で大半の不正入力を遮断)。\n", + " * 実行時は最低限の防御(有限・整数・32bit 範囲)を早期チェック。\n", + " * 定数は関数外で固定し、読みやすい名前でコメントを付与。\n", + "\n", + "* **TypeScript特有の考慮点**\n", + "\n", + " * 定数を `const` で束縛し、ユニオン/ジェネリクスは不要(問題特性上、単一スカラー)。\n", + " * ビット演算(`|0`)は ToInt32 を誘発し最適化が不安定になり得るため回避。\n", + " * `(x - pop) / 10` で **toward-zero** を保証(`Math.trunc` 呼び出しオーバーヘッド回避)。\n", + "\n", + "---\n", + "\n", + "### 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n", + "| -------------------------- | ----: | ----: | ------: | ---: | --- | --------------- |\n", + "| 方法A:数値のみ(桁ポップ&プッシュ、事前OF検査) | O(d) | O(1) | 低 | 高 | 高 | 最速・最小メモリ、推奨 |\n", + "| 方法B:文字列反転→数値化→範囲判定 | O(d) | O(d) | 低 | 中 | 高 | 一時オブジェクト増で不利 |\n", + "| 方法C:ビット演算で切捨て | O(d) | O(1) | 低 | 中 | 中 | ToInt32 で最適化不安定 |\n", + "\n", + "---\n", + "\n", + "### 3. 選択したアルゴリズムと理由\n", + "\n", + "* **選択したアプローチ**: 方法A(数値のみ、逐次オーバーフロー検査)\n", + "* **理由**:\n", + "\n", + " * **計算量**: O(d)・O(1) で最短。\n", + " * **型安全性**: TS の `number` と軽量ガードで十分。\n", + " * **保守性/可読性**: 定数名と境界ロジックが明確、コメントで根拠を可視化。\n", + "* **TypeScript特有の最適化ポイント**\n", + "\n", + " * 関数外 `const` に境界定数を配置(再生成回避)。\n", + " * ビット演算を排し `(x - pop) / 10` に統一。\n", + " * 例外はホットパス外(冒頭)で早期判定し、正常系を直線化。\n", + "\n", + "---\n", + "\n", + "### 4. 実装コード(LeetCode 回答フォーマット / ESM 想定)\n", + "\n", + "```typescript\n", + "// ESM: ファイル自体は ESM として解釈可能(import/export 不要)\n", + "// LeetCode 形式:関数定義のみ\n", + "\n", + "// 32-bit signed integer limits\n", + "const INT_MAX = 2147483647; // 2^31 - 1\n", + "const INT_MIN = -2147483648; // -2^31\n", + "const MAX_DIV10 = 214748364; // Math.trunc(INT_MAX / 10)\n", + "const MIN_DIV10 = -214748364; // Math.trunc(INT_MIN / 10)\n", + "const MAX_LAST = 7; // INT_MAX % 10\n", + "const MIN_LAST = -8; // INT_MIN % 10\n", + "\n", + "/**\n", + " * Reverse Integer (#7) - Pure Function\n", + " * 与えられた 32bit 符号付き整数 x の数字を逆順にして返す。\n", + " * 範囲外になれば 0 を返す。\n", + " *\n", + " * @param x - 32bit 符号付き整数\n", + " * @returns 反転後の整数。範囲外は 0。\n", + " * @throws {TypeError} 非有限、整数でない\n", + " * @throws {RangeError} 入力が 32bit 範囲外\n", + " * @complexity Time: O(d), Space: O(1)\n", + " */\n", + "function reverse(x: number): number {\n", + " // ---- 軽量な入力検証(LeetCodeでは妥当だが、業務視点で保持)----\n", + " if (!Number.isFinite(x)) throw new TypeError('Input must be finite');\n", + " if (!Number.isInteger(x)) throw new TypeError('Input must be an integer');\n", + " if (x < INT_MIN || x > INT_MAX) {\n", + " throw new RangeError('Input out of 32-bit signed integer range');\n", + " }\n", + "\n", + " // ---- 本処理(数値のみ、逐次OF検査)----\n", + " let rev = 0;\n", + " while (x !== 0) {\n", + " // JS の % は符号を保持するため負数でも下位桁抽出は安全\n", + " const pop = x % 10;\n", + "\n", + " // 事前オーバーフロー検査(rev*10 + pop を実行する前)\n", + " // 上限\n", + " if (rev > MAX_DIV10 || (rev === MAX_DIV10 && pop > MAX_LAST)) return 0;\n", + " // 下限\n", + " if (rev < MIN_DIV10 || (rev === MIN_DIV10 && pop < MIN_LAST)) return 0;\n", + "\n", + " rev = rev * 10 + pop;\n", + "\n", + " // toward-zero を保証。ビット演算を避けて型安定を維持\n", + " x = (x - pop) / 10;\n", + " }\n", + " return rev;\n", + "}\n", + "\n", + "Analyze Complexity\n", + "Runtime 53 ms\n", + "Beats 52.89%\n", + "Memory 57.78 MB\n", + "Beats 47.71%\n", + "\n", + "```\n", + "\n", + "> 補足: ジャッジ最適化を最優先する場合、先頭の入力検証 3 行を削除してもロジックは不変です(LeetCode 入力は妥当と仮定されるため)。\n", + "\n", + "---\n", + "\n", + "### 5. 制約条件(対応状況)\n", + "\n", + "* 外部ライブラリ不使用(Node 標準のみ)\n", + "* メモリは O(1)・一時オブジェクトなし\n", + "* TypeScript **strict** を前提(`number` 単一・例外は早期に投げる)\n", + "\n", + "---\n", + "\n", + "**最終メモ**\n", + "すでに速い実装ですが、`(x - pop) / 10` に統一しビット演算を排除、定数を関数外へ固定して JIT に優しい配置にしています。これで微妙なランタイムばらつきの抑制と、さらなる数%の改善が見込めます。\n", + "\n", + "### 1. 問題の分析\n", + "\n", + "* **競技プログラミング視点**\n", + "\n", + " * すでに O(d)/O(1)。残る改善余地は分岐の削減・負数 `%` の扱い回避・ホットパスの直線化。\n", + " * 負数のまま `%` を取ると実装は簡単ですが、分岐(下限チェック)と符号付き `%` のコストが増えがち。\n", + " 👉 **絶対値で処理し、オーバーフロー検査を「上限側のみ」に統一**すると分岐が減ります。\n", + "\n", + "* **業務開発視点**\n", + "\n", + " * 例外はホットパス外の最小限に。LeetCode で最速を狙う場合は**実行時検証を省略**可能。\n", + " * 定数は関数外 `const` に固定し、IC 汚染を避けるためオブジェクト生成はしない。\n", + "\n", + "* **TypeScript特有の考慮点**\n", + "\n", + " * 型は `number` 単一。ジェネリクスは不要(問題特性上、汎用化はメリット小)。\n", + " * `Math.trunc`/ビット演算は呼出/ToInt32 コストが乗るため避け、\n", + " **`n = (n - pop) / 10`**(toward-zero)に統一して型安定を保つ。\n", + "\n", + "---\n", + "\n", + "### 2. アルゴリズムアプローチ比較\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n", + "| ------------------------ | ----: | ----: | ------: | ---: | --- | ------------- |\n", + "| 方法A:正負分離(絶対値化)+上限側のみOF検査 | O(d) | O(1) | 低 | 高 | 高 | 分岐を1系統に集約し高速化 |\n", + "| 方法B:負数のまま `%`+上下限のOF検査 | O(d) | O(1) | 低 | 高 | 中 | 分岐が増える分やや不利 |\n", + "| 方法C:文字列反転 | O(d) | O(d) | 低 | 中 | 高 | GC負荷・メモリ増で不利 |\n", + "\n", + "---\n", + "\n", + "### 3. 選択したアルゴリズムと理由\n", + "\n", + "* **選択したアプローチ**: 方法A(絶対値で処理し、オーバーフローは上限側のみ判定)\n", + "* **理由**:\n", + "\n", + " * **計算量**: O(d)/O(1) を保持しつつ分岐と符号付き `%` を回避。\n", + " * **型安全性**: `number` のみ・例外は最小限。\n", + " * **保守性**: 境界定数と条件が簡潔(`MAX_DIV10` と末尾桁のみ)。\n", + "* **TypeScript最適化ポイント**\n", + "\n", + " * 定数は関数外 `const`、ループはスカラー計算のみ。\n", + " * `n > 0` ループで負数 `%` を完全回避。\n", + " * `(n - pop) / 10` による toward-zero、ビット演算は不使用。\n", + "\n", + "---\n", + "\n", + "### 4. 実装コード(LeetCode/ESM 形式)\n", + "\n", + "```typescript\n", + "// 32-bit signed integer limits (ESMファイル、関数のみ定義:LeetCode形式)\n", + "const INT_MAX = 2147483647; // 2^31 - 1\n", + "const MAX_DIV10 = 214748364; // Math.trunc(INT_MAX / 10)\n", + "const MAX_LAST = 7; // INT_MAX % 10\n", + "\n", + "/**\n", + " * Reverse Integer (#7) - Pure & Fast\n", + " * 符号は最後に掛け戻し、オーバーフローは上限側のみで検査。\n", + " *\n", + " * @param x - 32bit 符号付き整数\n", + " * @returns 反転後の整数。範囲外は 0。\n", + " * @throws {TypeError} 非有限・整数でない(最速化したい場合は削除可)\n", + " * @throws {RangeError} 32bit 範囲外(最速化したい場合は削除可)\n", + " * @complexity Time: O(d), Space: O(1)\n", + " */\n", + "function reverse(x: number): number {\n", + " // --- 軽量検証(LeetCode最速狙いでは削除可) ---\n", + " if (!Number.isFinite(x)) throw new TypeError('Input must be finite');\n", + " if (!Number.isInteger(x)) throw new TypeError('Input must be an integer');\n", + " if (x < -2147483648 || x > INT_MAX) {\n", + " throw new RangeError('Input out of 32-bit signed integer range');\n", + " }\n", + "\n", + " // 符号を分離し、絶対値で処理(負数の%と下限チェックを回避)\n", + " const sign = x < 0 ? -1 : 1;\n", + " let n = x < 0 ? -x : x; // |INT_MIN| = 2147483648 は JS number で安全\n", + " let rev = 0;\n", + "\n", + " while (n > 0) {\n", + " const pop = n % 10;\n", + "\n", + " // 事前OF検査:rev*10 + pop が INT_MAX を超えるか\n", + " if (rev > MAX_DIV10 || (rev === MAX_DIV10 && pop > MAX_LAST)) {\n", + " return 0;\n", + " }\n", + "\n", + " rev = rev * 10 + pop;\n", + " n = (n - pop) / 10; // toward-zero、ビット演算しない\n", + " }\n", + "\n", + " // 負側の最小値も sign 掛け戻し時に自然に弾ける(上で検査済み)\n", + " return rev * sign;\n", + "}\n", + "\n", + "Analyze Complexity\n", + "Runtime 50 ms\n", + "Beats 67.36%\n", + "Memory 57.24 MB\n", + "Beats 80.40%\n", + "\n", + "```\n", + "\n", + "---\n", + "\n", + "### 5. さらに詰めたい場合のヒント\n", + "\n", + "* **ジャッジ最適化**:上の `TypeError/RangeError` の入力検証 3 行を外す(LeetCode入力は妥当前提)。\n", + "* **定数の直接リテラル化**:`MAX_DIV10/MAX_LAST` をリテラルにしてプロパティ参照を減らす(微差)。\n", + "* **関数外の一切の副作用禁止**:他のグローバル定義や余計な import/export を置かない(IC 安定化)。\n", + "\n", + "今回の変更(**絶対値化+上限側のみ判定**)は、平均して **分岐ミスの低減** と **負数 `%` 回避**で数%程度の改善が出やすい構成です。メモリは依然 O(1) のまま据え置きです。\n", + "\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}