diff --git a/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.html b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.html new file mode 100644 index 00000000..6b914d66 --- /dev/null +++ b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.html @@ -0,0 +1,2238 @@ + + + + + + Best Divisor - √n約数列挙+桁和比較 + + + + + + + + + + + + + + + + + +
+ + + + +
+

+ アルゴリズム概要 +

+ +

問題説明

+

+ Kristenは数字の各桁の和(桁和)で数の良し悪しを判定します。与えられた整数 + n + の約数のうち、以下の基準で「最良」のものを見つけてください: +

+
    +
  1. + 桁和が最大の約数を選ぶ(例:6の桁和は6、12の桁和は1+2=3なので6が優先) +
  2. +
  3. 桁和が同じ場合は値が小さい方を選ぶ
  4. +
+ +

入出力例

+
+

Input:

+
12
+

Output:

+
6
+

+ 説明: 12の約数は {1, 2, 3, 4, 6, 12}。各桁和は {1, 2, 3, 4, + 6, 3}。最大桁和6を持つ約数は 6。 +

+
+ +

制約条件

+ + +

戦略

+
    +
  1. + 効率的な約数列挙: √n まで探索し、i が約数なら i と n/i + の両方を収集 +
  2. +
  3. 桁和計算: 各約数を文字列化し、各桁を合計
  4. +
  5. 最良選択: (桁和が最大, 値が最小) の優先順位で比較
  6. +
+ +

主要ポイント

+ +
+ + +
+

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

+
+
+ + +
+

+ Python実装 +

+
def findBestDivisor(n: int) -> int:
+    """
+    最良の約数を見つける(競技プログラミング最適化版)
+
+    Time Complexity: O(√n)
+    Space Complexity: O(d) where d is number of divisors
+    """
+    divisors = []
+    i = 1
+
+    # √n まで探索して約数をペアで収集
+    while i * i <= n:
+        if n % i == 0:
+            divisors.append(i)
+            # 平方数でない場合のみペアを追加
+            if i != n // i:
+                divisors.append(n // i)
+        i += 1
+
+    # 桁和が最大、同値なら最小値を選択
+    # タプル比較: (桁和大, 値小) = (sum, -divisor)
+    return max(divisors, key=lambda x: (sum(int(d) for d in str(x)), -x))
+
+
+# 使用例
+if __name__ == '__main__':
+    n = int(input().strip())
+    result = findBestDivisor(n)
+    print(result)
+
+ + +
+

+ フローチャート +

+ +
+

📖 フローチャートの見方

+
+
+
+ 開始・終了 +
+
+
+ 処理 +
+
+
+ 条件分岐 +
+
+
+ はい(Yes) +
+
+
+ いいえ(No) +
+
+
+ ループ戻り +
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + 開始 + + + START + + + + + 1 + + + + + + + + + + + 入力: n + + + 整数 n を受け取る + + + + + 2 + + + + + + + + + + + 初期化 + + + divisors = [ ] + + + i = 1 + + + + + 3 + + + + + + + + + + + i × i ≤ n ? + + + ループ継続判定 + + + + + 4 + + + + + + + + はい + + + + + + + + いいえ + + + + ループ終了 → + + + 最良約数を選択 + + + + + + + + n % i == 0 ? + + + 約数判定 + + + + + 5 + + + + + + + + はい + + + + + + + + いいえ + + + + スキップ + + + + + + + + 約数をリストに追加 + + + divisors.append(i) + + + もし i ≠ n ÷ i なら: + + + divisors.append(n//i) + + + + + 6 + + + + + + + + + + + i を増加 + + + i = i + 1 + + + + + 7 + + + + + + + + 🔄 ループ戻り + + + 次の i をチェック + + + + + + + + 最良の約数を選択 + + + max(divisors, key=...) + + + ① 桁和が最大 + + + ② 同点なら値が小さい方 + + + + + 8 + + + + + + + + + + + 終了 + + + END + + + + + 9 + + + + + + 💡 ポイント + + + √n まで探索するので + + + 効率が良い! + + + O(√n) + + +
+ +

+ フローの説明:
+ 1. 入力 n を受け取り、空の約数リスト divisors と i=1 で初期化
+ 2. i×i ≤ n の間ループ:n を i で割り切れるか判定
+ 3. 割り切れる場合、i と n//i を約数リストに追加(i≠n//i の場合のみペア追加)
+ 4. i を1増やしてループ継続
+ 5. ループ終了後、約数リストから桁和が最大(同値なら最小値)の約数を選択
+ 6. 結果を返して終了 +

+
+ + +
+

+ 計算量分析 +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 項目 + + 本実装(√n列挙) + + 代替手法(全探索) +
+ 時間計算量 + + O(√n) +
+ √n までループ + O(d log d) 桁和計算 +
+
+ O(n) +
+ 1 から n まで全探索 +
+
+ 空間計算量 + + O(d) +
+ 約数リスト(d は約数の個数) +
+
+ O(d) +
同じ
+
+ 実装コスト + + +
+ √n 判定とペア追加が必要 +
+
+ +
シンプルなループ
+
+ 制約 n≤105 での推奨度 + + ★★★★★ +
+ 最大√100000 ≈ 316 回のループで済む +
+
+ ★★★☆☆ +
+ 100000回ループは許容範囲だが非効率 +
+
+
+ +

最適化ポイント

+ + +

具体例での計算量

+
+

n = 12 の場合:

+
    +
  • √12 ≈ 3.46 → i=1,2,3 の3回ループ
  • +
  • i=1: 約数 {1, 12}
  • +
  • i=2: 約数 {2, 6} を追加
  • +
  • i=3: 約数 {3, 4} を追加
  • +
  • 合計6個の約数を3回のループで収集(全探索なら12回)
  • +
+
+
+
+ + + + + + + + + + + + + + + + + diff --git a/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.ipynb b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.ipynb new file mode 100644 index 00000000..8baaa645 --- /dev/null +++ b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Best Divisor/BestDivisor.ipynb @@ -0,0 +1,407 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "3e4cb1d9", + "metadata": {}, + "source": [ + "# 問題分析と実装\n", + "\n", + "## 1. 問題分析結果\n", + "\n", + "### 競技プログラミング視点\n", + "- **制約分析**: n ≤ 10^5 → O(√n) の約数列挙で十分\n", + "- **最速手法**: √n までループして約数を収集、桁和を組み込み関数で計算\n", + "- **メモリ最小化**: 約数の数は高々O(√n)、桁和計算は即座に実行\n", + "- **CPython最適化**: `sum()`と文字列変換、`max()`のkey引数を活用\n", + "\n", + "### 業務開発視点\n", + "- **型安全設計**: 入力検証、厳密な型ヒント\n", + "- **エラーハンドリング**: 不正入力の検証\n", + "- **可読性**: 明確な関数分割、詳細なdocstring\n", + "\n", + "### Python特有分析\n", + "- **データ構造選択**: リストで約数を保持(順序不要、重複なし)\n", + "- **標準ライブラリ活用度**: 組み込み関数`sum()`, `max()`を効果的に使用\n", + "- **CPython最適化度**: 文字列変換とジェネレータ式でC実装を活用\n", + "\n", + "## 2. 採用アルゴリズムと根拠\n", + "\n", + "### アルゴリズム比較表\n", + "\n", + "|アプローチ|時間計算量|空間計算量|Python実装コスト|可読性|標準ライブラリ活用|CPython最適化|備考|\n", + "|---------|---------|---------|---------------|------|----------------|------------|-----|\n", + "|√n約数列挙|O(√n)|O(d)|低|★★★|sum(), max()|適|dは約数の個数|\n", + "|全探索|O(n)|O(d)|低|★★☆|同上|適|非効率|\n", + "\n", + "**選択理由**: O(√n)で全約数を列挙可能。桁和計算は定数時間(桁数は高々log n)。Pythonの組み込み関数を最大活用。\n", + "\n", + "**Python最適化戦略**:\n", + "- `sum(int(d) for d in str(num))`: ジェネレータ式とC実装のsumを活用\n", + "- `max(divisors, key=...)`: C実装のmax関数でソート不要\n", + "\n", + "## 3. 実装パターン\n", + "\n", + "```python\n", + "from typing import List, Tuple\n", + "\n", + "class Solution:\n", + " \"\"\"\n", + " 最良の約数を見つける問題の解決クラス\n", + " \n", + " 競技プログラミング向けと業務開発向けの2パターンを提供\n", + " \"\"\"\n", + " \n", + " def findBestDivisor(self, n: int) -> int:\n", + " \"\"\"\n", + " 業務開発向け実装(型安全・エラーハンドリング重視)\n", + " \n", + " Args:\n", + " n: 正の整数(1 ≤ n ≤ 10^5)\n", + " \n", + " Returns:\n", + " 最良の約数(桁和が最大、同じなら最小値)\n", + " \n", + " Raises:\n", + " ValueError: 入力値が制約を満たさない場合\n", + " TypeError: 入力型が不正な場合\n", + " \n", + " Time Complexity: O(√n + d)\n", + " Space Complexity: O(d)\n", + " \"\"\"\n", + " # 1. 入力検証\n", + " self._validate_input(n)\n", + " \n", + " # 2. 約数を列挙\n", + " divisors = self._find_divisors(n)\n", + " \n", + " # 3. 最良の約数を選択\n", + " best_divisor = self._select_best_divisor(divisors)\n", + " \n", + " return best_divisor\n", + " \n", + " def findBestDivisorCompetitive(self, n: int) -> int:\n", + " \"\"\"\n", + " 競技プログラミング向け最適化実装\n", + " \n", + " Time Complexity: O(√n)\n", + " Space Complexity: O(d)\n", + " \"\"\"\n", + " divisors: List[int] = []\n", + " i = 1\n", + " while i * i <= n:\n", + " if n % i == 0:\n", + " divisors.append(i)\n", + " if i != n // i:\n", + " divisors.append(n // i)\n", + " i += 1\n", + " \n", + " # 桁和が最大、同値なら最小値を選択\n", + " return max(divisors, key=lambda x: (sum(int(d) for d in str(x)), -x))\n", + " \n", + " def _validate_input(self, n: int) -> None:\n", + " \"\"\"型安全な入力検証\"\"\"\n", + " if not isinstance(n, int):\n", + " raise TypeError(f\"Input must be integer, got {type(n)}\")\n", + " \n", + " if n < 1 or n > 10**5:\n", + " raise ValueError(f\"Input must be in range [1, 10^5], got {n}\")\n", + " \n", + " def _find_divisors(self, n: int) -> List[int]:\n", + " \"\"\"\n", + " nの全約数を列挙\n", + " \n", + " Args:\n", + " n: 正の整数\n", + " \n", + " Returns:\n", + " 約数のリスト\n", + " \"\"\"\n", + " divisors: List[int] = []\n", + " i = 1\n", + " # √nまでループして約数をペアで収集\n", + " while i * i <= n:\n", + " if n % i == 0:\n", + " divisors.append(i)\n", + " # i ≠ n/i の場合のみ追加(平方数対策)\n", + " if i != n // i:\n", + " divisors.append(n // i)\n", + " i += 1\n", + " \n", + " return divisors\n", + " \n", + " def _digit_sum(self, num: int) -> int:\n", + " \"\"\"\n", + " 数値の桁和を計算\n", + " \n", + " Args:\n", + " num: 正の整数\n", + " \n", + " Returns:\n", + " 各桁の合計\n", + " \"\"\"\n", + " # 文字列変換とsum()でCPython最適化\n", + " return sum(int(digit) for digit in str(num))\n", + " \n", + " def _select_best_divisor(self, divisors: List[int]) -> int:\n", + " \"\"\"\n", + " 最良の約数を選択\n", + " \n", + " Args:\n", + " divisors: 約数のリスト\n", + " \n", + " Returns:\n", + " 最良の約数(桁和最大、同値なら最小値)\n", + " \"\"\"\n", + " # max()のkey引数を使用\n", + " # タプルで優先順位: (桁和が大きい, 値が小さい)\n", + " return max(divisors, key=lambda x: (self._digit_sum(x), -x))\n", + "\n", + "\n", + "# メイン処理\n", + "if __name__ == '__main__':\n", + " n = int(input().strip())\n", + " solution = Solution()\n", + " # 競技プログラミング版を使用(高速)\n", + " result = solution.findBestDivisorCompetitive(n)\n", + " print(result)\n", + "```\n", + "\n", + "## 4. 検証\n", + "\n", + "### 境界値テスト\n", + "- **n = 1**: 約数は1のみ → 出力: 1\n", + "- **n = 12**: 約数 {1,2,3,4,6,12}、桁和 {1,2,3,4,6,3} → 最大6 → 出力: 6 ✓\n", + "- **n = 100**: 約数に10(桁和1), 20(桁和2), 25(桁和7), 50(桁和5)等 → 桁和最大を選択\n", + "\n", + "### Python特有の最適化ポイント\n", + "1. **組み込み関数**: `sum()`, `max()` はC実装で高速\n", + "2. **ジェネレータ式**: `sum(int(d) for d in str(x))` はメモリ効率的\n", + "3. **文字列変換**: 桁和計算で算術演算より簡潔かつ高速\n", + "4. **タプル比較**: `max(key=lambda x: (a, -b))` で複数条件ソート" + ] + }, + { + "cell_type": "markdown", + "id": "9086447b", + "metadata": {}, + "source": [ + "# コード全体の目的\n", + "\n", + "**入力:** 整数 `n`\n", + "**出力:**\n", + "\n", + "* 約数の中で **桁和(各桁の合計)が最大のもの**\n", + "* 桁和が同じなら **数値が小さい方**\n", + "\n", + "---\n", + "\n", + "# コード本体\n", + "\n", + "```python\n", + "def findBestDivisor(n: int) -> int:\n", + "```\n", + "\n", + "`n` の「最良の約数」を求める関数です。\n", + "\n", + "---\n", + "\n", + "# 計算量の意図(docstring)\n", + "\n", + "```python\n", + "\"\"\"\n", + "Time Complexity: O(√n)\n", + "Space Complexity: O(d) where d is number of divisors\n", + "\"\"\"\n", + "```\n", + "\n", + "### ✔ 時間計算量 `O(√n)`\n", + "\n", + "約数探索を √n までに制限して高速化。\n", + "\n", + "### ✔ 空間計算量 `O(d)`\n", + "\n", + "約数の個数 `d` 分だけメモリ使用。\n", + "\n", + "---\n", + "\n", + "# 約数収集ロジック(√n 最適化)\n", + "\n", + "```python\n", + "divisors = []\n", + "i = 1\n", + "```\n", + "\n", + "---\n", + "\n", + "```python\n", + "while i * i <= n:\n", + "```\n", + "\n", + "### なぜ √n までで良い?\n", + "\n", + "約数は **ペア(i, n//i)** で現れるため、√n まで探索すれば十分。\n", + "\n", + "---\n", + "\n", + "```python\n", + "if n % i == 0:\n", + " divisors.append(i)\n", + "```\n", + "\n", + "`i` が約数なら追加。\n", + "\n", + "---\n", + "\n", + "```python\n", + "if i != n // i:\n", + " divisors.append(n // i)\n", + "```\n", + "\n", + "### ✔ 平方数対策\n", + "\n", + "* `n = 36` のとき `i = 6` → `6 * 6`\n", + "* 同じ約数を **2回追加しない** ように防止\n", + "\n", + "---\n", + "\n", + "### ✔ 結果として\n", + "\n", + "**全ての約数を漏れなく収集**\n", + "\n", + "---\n", + "\n", + "# 約数の選択ルール(最重要)\n", + "\n", + "```python\n", + "return max(divisors, key=lambda x: (sum(int(d) for d in str(x)), -x))\n", + "```\n", + "\n", + "---\n", + "\n", + "## 評価基準(タプル比較)\n", + "\n", + "```python\n", + "(sum_of_digits(x), -x)\n", + "```\n", + "\n", + "### ① 桁和が大きいものを優先\n", + "\n", + "```python\n", + "sum(int(d) for d in str(x))\n", + "```\n", + "\n", + "例:\n", + "\n", + "* 84 → 8+4 = 12\n", + "* 96 → 9+6 = 15(勝ち)\n", + "\n", + "---\n", + "\n", + "### ② 桁和が同じなら「値が小さい方」\n", + "\n", + "```python\n", + "-x\n", + "```\n", + "\n", + "**max() なので、値を反転することで「小さい数を優先」**\n", + "\n", + "例:\n", + "\n", + "* 39 (3+9=12)\n", + "* 48 (4+8=12)\n", + "\n", + "同じ桁和 → **39 を選ぶ**\n", + "\n", + "---\n", + "\n", + "# 実行例\n", + "\n", + "### 入力\n", + "\n", + "```\n", + "n = 100\n", + "```\n", + "\n", + "### 約数\n", + "\n", + "```\n", + "1, 2, 4, 5, 10, 20, 25, 50, 100\n", + "```\n", + "\n", + "### 桁和\n", + "\n", + "| 約数 | 桁和 |\n", + "| --- | -- |\n", + "| 1 | 1 |\n", + "| 2 | 2 |\n", + "| 4 | 4 |\n", + "| 5 | 5 |\n", + "| 10 | 1 |\n", + "| 20 | 2 |\n", + "| 25 | 7 |\n", + "| 50 | 5 |\n", + "| 100 | 1 |\n", + "\n", + "### ✔ 最大は `25`(7)\n", + "\n", + "→ 出力 `25`\n", + "\n", + "---\n", + "\n", + "# main 部分\n", + "\n", + "```python\n", + "if __name__ == '__main__':\n", + " n = int(input().strip())\n", + " result = findBestDivisor(n)\n", + " print(result)\n", + "```\n", + "\n", + "標準入力から整数を受け取り、結果を出力。\n", + "\n", + "---\n", + "\n", + "# アルゴリズムの強み\n", + "\n", + "| 項目 | 評価 |\n", + "| -------- | ------------------ |\n", + "| 高速 | √n で約数探索 |\n", + "| 無駄がない | 約数ペア回収 |\n", + "| Pythonic | `max(key=...)` で簡潔 |\n", + "| 競プロ向き | 大きな n にも対応 |\n", + "\n", + "---\n", + "\n", + "# 改善・代替案(さらに最適化)\n", + "\n", + "## メモリを使わず直接最大を更新(O(1) space)\n", + "\n", + "```python\n", + "def findBestDivisor(n):\n", + " best = 1\n", + " best_sum = 1\n", + " \n", + " i = 1\n", + " while i * i <= n:\n", + " if n % i == 0:\n", + " for d in (i, n // i):\n", + " s = sum(map(int, str(d)))\n", + " if s > best_sum or (s == best_sum and d < best):\n", + " best = d\n", + " best_sum = s\n", + " i += 1\n", + " \n", + " return best\n", + "```" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}