diff --git a/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README.md b/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README.md index f2ab8b25..1837699c 100644 --- a/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README.md +++ b/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README.md @@ -134,11 +134,12 @@ graph LR **実装比較**: -| 実装方法 | Runtime\* | Memory | 特徴 | -| ---------------------------- | --------- | ------ | ------------------ | -| 基本版(Array.from) | ~158ms | ~69MB | 可読性高、標準的 | -| 最適化版(ビット演算) | ~140ms | ~68MB | ビット演算で高速化 | -| 高速化版(変数キャッシング) | ~125ms | ~67MB | 更なる高速化 | +| 実装方法 | Runtime\* | Memory | 特徴 | +| ---------------------------- | --------- | ------- | ------------------ | +| 基本版(Array.from) | ~158ms | ~69MB | 可読性高、標準的 | +| 最適化版(ビット演算) | 154ms | 69.54MB | ビット演算で高速化 | +| 高速化版(変数キャッシング) | 158ms | 69.10MB | 更なる高速化 | +| メモリ最優先版 | 148ms | 67.10MB | メモリ効率最大化 | _\*数値は特定環境での測定例です_ @@ -337,7 +338,7 @@ const lastRow = rows - 1; 5. **空配列** ```typescript - [].snail(0, 0); // → [] + [].snail(1, 0); // → [] (入力サイズ 0) ``` 6. **標準ケース(偶数列)** @@ -356,6 +357,7 @@ const lastRow = rows - 1; ``` 8. **大きなサイズ** + ```typescript new Array(250) .fill(0) diff --git a/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README_react.html b/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README_react.html index 80de3fae..561c28a3 100644 --- a/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README_react.html +++ b/JavaScript/2624. Snail Traversal/Claude Code Sonnet 4.5/README_react.html @@ -6,7 +6,10 @@ LeetCode: Snail Traversal - 蛇行パターンで1D→2D配列変換 - + @@ -20,14 +23,20 @@ + + +
+ + + + +
+

+ アルゴリズム概要 +

+ +
+
+

問題の説明

+

+ 原始根(Primitive Root)とは、素数 p に対して、g の累乗 + g1, g2, ..., gp-1 を p + で割った余りが、すべて異なる値になるような整数 g のことです。 +

+

+ 例えば p = 7 の場合、g = 3 は以下のように原始根になります: +

+
    +
  • 31 mod 7 = 3
  • +
  • 32 mod 7 = 2
  • +
  • 33 mod 7 = 6
  • +
  • 34 mod 7 = 4
  • +
  • 35 mod 7 = 5
  • +
  • 36 mod 7 = 1
  • +
+

+ これらはすべて異なる値(1, 2, 3, 4, 5, 6)になるため、3 は 7 + の原始根です。 +

+
+ +
+

入出力例

+
+
+ 入力: +
7
+
+
+ 出力: +
3 2
+
+

+ 7 の原始根は 3 と 5 の2つあり、最小は 3 です。 +

+
+
+ +
+

制約条件

+
    +
  • p は素数
  • +
  • 2 ≤ p ≤ 109
  • +
+
+ +
+

解法の戦略

+

+ 原始根を効率的に判定するために、数学的な性質を活用します: +

+
    +
  1. + 原始根の判定条件: g が原始根である ⇔ すべての (p-1) + の素因数 q に対して、g(p-1)/q ≢ 1 (mod p) +
  2. +
  3. + (p-1) の素因数分解: まず (p-1) + を素因数分解します(O(√p) 時間) +
  4. +
  5. + 最小原始根の探索: g = 2 + から順に上記の判定条件をチェック +
  6. +
  7. + 原始根の総数: オイラーのトーシェント関数 φ(p-1) + で計算 +
  8. +
+
+ +
+

主要ポイント

+
+
    +
  • + +
    + 時間計算量: O(√p + k·d·log p) +
    + k = 最小原始根の値、d = (p-1)の素因数の個数 +
    +
  • +
  • + 💾 +
    + 空間計算量: O(d) +
    + 素因数リストの保存のみ +
    +
  • +
  • + 🔧 +
    + 最適化手法: Pythonの組み込み関数 pow(base, + exp, mod) を使用した高速累乗剰余演算 +
    +
  • +
+
+
+
+
+ + +
+

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

+
+
+ + +
+

+ Python実装 +

+
#!/bin/python3
+
+import math
+import os
+import random
+import re
+import sys
+
+from typing import List
+
+def prime_factors(n: int) -> List[int]:
+    """
+    nの素因数をリストで返す(重複なし)
+
+    Time Complexity: O(√n)
+    """
+    factors = []
+    # 2で割り切れる場合
+    if n % 2 == 0:
+        factors.append(2)
+        while n % 2 == 0:
+            n //= 2
+
+    # 3以降の奇数でチェック
+    i = 3
+    while i * i <= n:
+        if n % i == 0:
+            factors.append(i)
+            while n % i == 0:
+                n //= i
+        i += 2
+
+    # nが1より大きければ、それ自体が素数
+    if n > 1:
+        factors.append(n)
+
+    return factors
+
+def is_primitive_root(g: int, p: int, prime_divisors: List[int]) -> bool:
+    """
+    gがpの原始根かどうかを判定
+
+    Args:
+        g: 判定対象の整数
+        p: 素数
+        prime_divisors: (p-1)の素因数リスト
+
+    Returns:
+        gが原始根ならTrue
+
+    Time Complexity: O(d·log p) where d = len(prime_divisors)
+    """
+    phi = p - 1
+
+    # 各素因数qについて、g^((p-1)/q) ≢ 1 (mod p) を確認
+    for q in prime_divisors:
+        if pow(g, phi // q, p) == 1:
+            return False
+
+    return True
+
+def euler_phi(n: int) -> int:
+    """
+    オイラーのトーシェント関数 φ(n) を計算
+
+    Time Complexity: O(√n)
+    """
+    result = n
+    p = 2
+    while p * p <= n:
+        if n % p == 0:
+            while n % p == 0:
+                n //= p
+            result -= result // p
+        p += 1
+
+    if n > 1:
+        result -= result // n
+
+    return result
+
+def solve_competitive(p: int) -> tuple:
+    """
+    競技プログラミング向け実装
+
+    Args:
+        p: 素数
+
+    Returns:
+        (最小原始根, 原始根の総数)
+
+    Time Complexity: O(√p + k·d·log p)
+        where k = 最小原始根の値, d = (p-1)の素因数の個数
+    Space Complexity: O(d)
+    """
+    # (p-1)の素因数を求める
+    prime_divisors = prime_factors(p - 1)
+
+    # 最小原始根を探索
+    smallest_root = 0
+    for g in range(2, p):
+        if is_primitive_root(g, p, prime_divisors):
+            smallest_root = g
+            break
+
+    # 原始根の総数 = φ(p-1)
+    total_count = euler_phi(p - 1)
+
+    return smallest_root, total_count
+
+if __name__ == '__main__':
+    p = int(input().strip())
+
+    smallest, total = solve_competitive(p)
+    print(f"{smallest} {total}")
+
+
+ + +
+

+ フローチャート +

+
+ + + + + + + + + + + + + + + + + + + + + 1 + + + + + 2 + + + + + 3 + + + + + 4 + + + + + 5 + + + + + 6 + + + + + 7 + + + + + 8 + + + + + + + 開始 + + + + + + + 入力: 素数 p + + + 素数 p を読み込む + + + + + + + (p-1) の素因数分解 + + + 関数: prime_factors(p-1) + + + 結果: [q₁, q₂, ..., qₐ] の素因数リスト + + + + + + + g = 2 に初期化 + + + 原始根の候補を2から開始 + + + + + + + + + 【ループA: 候補gをチェック】 + + + + + 原始根判定 + + + 【ループB: 各素因数qで確認】 + + + すべてのqについて + + + g^((p-1)/q) mod p ≠ 1 をチェック + + + + + + + 原始根? + + + すべてのqで≠1 + + + + + + はい + + + + + 最小原始根発見! + + + smallest_root = g + + + + + + いいえ + + + + + g = g + 1 + + + 次の候補に進む + + + + + + ループバック + + + + + + + φ(p-1) を計算 + + + 関数: euler_phi(p-1) + + + 結果: 原始根の総数 + + + + + + + 結果出力 + + + (smallest_root, total_count) + + + + + + + 終了 + + +
+ +
+

+ 📋 ステップバイステップの流れ: +

+
+
+ 1 + 開始 - アルゴリズムの実行を開始 +
+
+ 2 + 入力 - 素数 p を読み込む +
+
+ 3 + 素因数分解 - (p-1) の素因数を求める → [q₁, q₂, + ..., qₐ] +
+
+ 4 + 初期化 - 候補 g を 2 に初期化 +
+
+ 5 + 原始根判定 - 各素因数 q について g^((p-1)/q) mod p + ≠ 1 をチェック(ループB) +
+
+ 6 + 判定結果 - 原始根なら発見して次へ、そうでなければ + g++ してループA に戻る +
+
+ 7 + 総数計算 - φ(p-1) + を計算して原始根の総数を求める +
+
+ 8 + 出力と終了 - (最小原始根, 総数) + を出力してアルゴリズム終了 +
+
+
+

+ 💡 初学者向けポイント:
+ • ループAは候補 g を順番にチェックする外側のループ
+ • ループBは各素因数 q で判定する内側のループ
+ • ループバックの矢印は g++ 後に判定ステップに戻ることを示す
+ • ステップ番号で処理の順序が一目でわかる +

+
+
+
+ + +
+

+ 計算量分析 +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 項目 + + 本実装 + + 全探索(素朴) + + 備考 +
+ 時間計算量 + + O(√p + k·d·log p) + + O(p²·log p) + + k = 最小原始根(通常小さい)
+ d = (p-1)の素因数個数 +
+ 空間計算量 + + O(d) + + O(p) + + 素因数リストのみ保存 +
+ 実装コスト + + + + + + 素因数分解が必要 +
+ p=10⁹での実用性 + + ◎ 高速 + + × TLE + + 制約の上限で性能差が顕著 +
+
+ +
+

最適化のポイント

+
    +
  • + + 素因数分解の活用: (p-1) + の素因数だけをチェックすることで、判定回数を大幅削減 +
  • +
  • + + 高速累乗剰余: Python の組み込み関数 pow(g, exp, p) + を使用(C実装で高速) +
  • +
  • + + 早期終了: + 最小原始根が見つかった時点で探索を終了 +
  • +
  • + + 数学的性質: オイラーのトーシェント関数で総数を + O(√p) で計算 +
  • +
+
+
+ + + +
+ + + + + + + + + + + + diff --git a/Mathematics/Number Theory/HuckerRank/Easy/Primitive_Problem.ipynb b/Mathematics/Number Theory/HuckerRank/Easy/Primitive_Problem.ipynb new file mode 100644 index 00000000..b69c2fc8 --- /dev/null +++ b/Mathematics/Number Theory/HuckerRank/Easy/Primitive_Problem.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "15b9a983", + "metadata": {}, + "source": [ + "## 1. 問題分析結果\n", + "\n", + "### 競技プログラミング視点\n", + "- **原始根(Primitive Root)** の定義: 素数pに対して、g^1, g^2, ..., g^(p-1) (mod p) がすべて異なる値になる整数g\n", + "- 制約: p ≤ 10^9 の素数\n", + "- 必要な出力: 最小の原始根と原始根の総数\n", + "\n", + "### 数学的考察\n", + "1. 素数pの原始根の個数は **オイラーのトーシェント関数 φ(p-1)** に等しい\n", + "2. gが原始根 ⇔ g^((p-1)/q) ≢ 1 (mod p) for all prime divisors q of (p-1)\n", + "3. 最小原始根を見つけるには、2から順に確認\n", + "\n", + "### Python特有最適化ポイント\n", + "- `pow(base, exp, mod)` の組み込み関数を使用(高速な累乗剰余演算)\n", + "- 素因数分解の効率化\n", + "- オイラーのトーシェント関数の効率的計算\n", + "\n", + "## 2. アルゴリズム比較表\n", + "\n", + "| アプローチ | 時間計算量 | 空間計算量 | Python実装コスト | 可読性 | 備考 |\n", + "|---------|---------|---------|--------------|-------|------|\n", + "| 全探索(全gをチェック) | O(p² log p) | O(1) | 低 | ★★★ | 制約上TLE |\n", + "| 素因数分解活用 | O(√(p-1) + k·d·log p) | O(d) | 中 | ★★☆ | k:候補数, d:素因数個数 |\n", + "\n", + "## 3. 採用アルゴリズム\n", + "\n", + "**素因数分解を利用した原始根判定**\n", + "- (p-1)の素因数を求める\n", + "- 各候補gについて、g^((p-1)/q) ≢ 1 (mod p) を全素因数qで確認\n", + "- φ(p-1)で総数を計算\n", + "\n", + "## 4. 実装" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24c5e9a1", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import os\n", + "import random\n", + "import re\n", + "import sys\n", + "\n", + "from typing import List, Set\n", + "\n", + "def prime_factors(n: int) -> List[int]:\n", + " \"\"\"\n", + " nの素因数をリストで返す(重複なし)\n", + " \n", + " Time Complexity: O(√n)\n", + " \"\"\"\n", + " factors = []\n", + " # 2で割り切れる場合\n", + " if n % 2 == 0:\n", + " factors.append(2)\n", + " while n % 2 == 0:\n", + " n //= 2\n", + " \n", + " # 3以降の奇数でチェック\n", + " i = 3\n", + " while i * i <= n:\n", + " if n % i == 0:\n", + " factors.append(i)\n", + " while n % i == 0:\n", + " n //= i\n", + " i += 2\n", + " \n", + " # nが1より大きければ、それ自体が素数\n", + " if n > 1:\n", + " factors.append(n)\n", + " \n", + " return factors\n", + "\n", + "def is_primitive_root(g: int, p: int, prime_divisors: List[int]) -> bool:\n", + " \"\"\"\n", + " gがpの原始根かどうかを判定\n", + " \n", + " Args:\n", + " g: 判定対象の整数\n", + " p: 素数\n", + " prime_divisors: (p-1)の素因数リスト\n", + " \n", + " Returns:\n", + " gが原始根ならTrue\n", + " \n", + " Time Complexity: O(d·log p) where d = len(prime_divisors)\n", + " \"\"\"\n", + " phi = p - 1\n", + " \n", + " # 各素因数qについて、g^((p-1)/q) ≢ 1 (mod p) を確認\n", + " for q in prime_divisors:\n", + " if pow(g, phi // q, p) == 1:\n", + " return False\n", + " \n", + " return True\n", + "\n", + "def euler_phi(n: int) -> int:\n", + " \"\"\"\n", + " オイラーのトーシェント関数 φ(n) を計算\n", + " \n", + " Time Complexity: O(√n)\n", + " \"\"\"\n", + " result = n\n", + " \n", + " # 2で割り切れる場合\n", + " if n % 2 == 0:\n", + " while n % 2 == 0:\n", + " n //= 2\n", + " result -= result // 2\n", + " \n", + " # 3以降の奇数でチェック\n", + " p = 3\n", + " while p * p <= n:\n", + " if n % p == 0:\n", + " while n % p == 0:\n", + " n //= p\n", + " result -= result // p\n", + " p += 2\n", + " \n", + " if n > 1:\n", + " result -= result // n\n", + " \n", + " return result\n", + "\n", + "def solve_competitive(p: int) -> tuple:\n", + " \"\"\"\n", + " 競技プログラミング向け実装\n", + " \n", + " Args:\n", + " p: 素数\n", + " \n", + " Returns:\n", + " (最小原始根, 原始根の総数)\n", + " \n", + " Time Complexity: O(√p + k·d·log p) \n", + " where k = 最小原始根の値, d = (p-1)の素因数の個数\n", + " Space Complexity: O(d)\n", + " \"\"\"\n", + " # (p-1)の素因数を求める\n", + " prime_divisors = prime_factors(p - 1)\n", + " \n", + " # 最小原始根を探索\n", + " smallest_root = 0\n", + " for g in range(2, p):\n", + " if is_primitive_root(g, p, prime_divisors):\n", + " smallest_root = g\n", + " break\n", + " \n", + " # 原始根の総数 = φ(p-1)\n", + " total_count = euler_phi(p - 1)\n", + " \n", + " return smallest_root, total_count\n", + "\n", + "if __name__ == '__main__':\n", + " # p = int(input().strip())\n", + " # テスト用入力\n", + " p = 7\n", + " \n", + " smallest, total = solve_competitive(p)\n", + " print(f\"{smallest} {total}\")" + ] + }, + { + "cell_type": "markdown", + "id": "15b9a984", + "metadata": {}, + "source": [ + "## 5. 検証\n", + "\n", + "### サンプル入力0: p = 7\n", + "- p-1 = 6 の素因数: [2, 3]\n", + "- g=2: 2^3 mod 7 = 1 → 原始根でない\n", + "- g=3: 3^3 mod 7 = 6, 3^2 mod 7 = 2 → 原始根 ✓\n", + "- φ(6) = 2\n", + "- 出力: `3 2` ✓\n", + "\n", + "### 計算量分析\n", + "- 素因数分解: O(√p)\n", + "- 原始根判定: 最悪でもO(p·d·log p)だが、実際は最小原始根は小さいことが多い\n", + "- φ(p-1)の計算: O(√p)\n", + "- 全体: p ≤ 10^9 でも実用的な時間で動作" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "3.12.4", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}