diff --git a/.markdownlint.json b/.markdownlint.json
index f1d9c561..e0c0a983 100644
--- a/.markdownlint.json
+++ b/.markdownlint.json
@@ -4,7 +4,7 @@
"MD002": true,
"MD003": { "style": "atx" },
"MD004": { "style": "consistent" },
- "MD007": { "indent": 4 },
+ "MD007": { "indent": 2 },
"MD009": { "br_spaces": 2 },
"MD012": true,
"MD013": {
@@ -20,7 +20,7 @@
"MD024": { "siblings_only": true },
"MD025": { "front_matter_title": "" },
"MD026": { "punctuation": ".,;:!。,;:" },
- "MD029": { "style": "ordered" },
+ "MD029": false,
"MD030": {
"ul_single": 1,
"ol_single": 1,
@@ -46,10 +46,12 @@
]
},
"MD034": false,
+ "MD036": false,
"MD038": true,
"MD040": false,
- "MD041": { "level": 1 },
+ "MD041": false,
"MD042": false,
"MD046": { "style": "fenced" },
- "MD048": { "style": "backtick" }
+ "MD048": { "style": "backtick" },
+ "MD058": false
}
diff --git a/README.md b/README.md
index f58f8a48..bf93985b 100644
--- a/README.md
+++ b/README.md
@@ -28,31 +28,32 @@
### マトリックス構造
**2つのAIプロバイダー × 3つのプログラミング言語 × 3つのドキュメントティア = 1問題あたり18成果物**
+
```mermaid
graph TB
Problem[問題
例: 回文判定]
-
+
subgraph AI[AIプロバイダー]
Claude[Claude Sonnet 4.5
競技プログラミング最適化]
GPT[GPT-5.1
本番環境対応]
end
-
+
subgraph Lang[プログラミング言語]
Python[Python 3.11
型ヒント付き]
TypeScript[TypeScript
厳密な型チェック]
JavaScript[JavaScript
ランタイム検証]
end
-
+
subgraph Docs[ドキュメントティア]
T1[Tier 1: 静的
README.md]
T2[Tier 2: インタラクティブ
README.html]
T3[Tier 3: 動的
README_react.html]
end
-
+
Problem --> AI
AI --> Lang
Lang --> Docs
-
+
style Problem fill:#e1f5ff
style Claude fill:#ffe1e1
style GPT fill:#ffe1e1
@@ -68,37 +69,38 @@ graph TB
各問題は、18ファイルを生成する一貫したパターンに従います。
-| AIプロバイダー | 言語 | ドキュメントファイル | 言語ごとの合計 |
-|--------------|------|---------------------|--------------|
-| Claude | Python | `*.py` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| Claude | TypeScript | `*.ts` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| Claude | JavaScript | `*.js` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| GPT | Python | `*.py` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| GPT | TypeScript | `*.ts` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| GPT | JavaScript | `*.js` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
-| **合計** | | | **18成果物** |
+| AIプロバイダー | 言語 | ドキュメントファイル | 言語ごとの合計 |
+| -------------- | ---------- | ---------------------------------------------------------- | -------------- |
+| Claude | Python | `*.py` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| Claude | TypeScript | `*.ts` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| Claude | JavaScript | `*.js` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| GPT | Python | `*.py` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| GPT | TypeScript | `*.ts` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| GPT | JavaScript | `*.js` + `README.md` + `README.html` + `README_react.html` | 4ファイル |
+| **合計** | | | **18成果物** |
---
## 4ドメイン問題分類
リポジトリは、問題を4つの異なるドメインに整理し、それぞれが特定のアルゴリズムパターンとデータ操作技術をターゲットとしています。
+
```mermaid
graph LR
Root[Algorithm-DataStructures
-Math-SQL]
-
+
subgraph Domains[4つのドメイン]
Algo[Algorithm
アルゴリズム
-----
二分探索
動的計画法
Two Pointers]
DS[DataStructures
データ構造
-----
LinkedList
DoublyLinkedList
ポインタ操作]
Math[Mathematics
数学
-----
回文
幾何学
有限状態機械]
SQL[SQL
データベース
-----
JOIN パターン
Window関数
最適化]
end
-
+
Root --> Algo
Root --> DS
Root --> Math
Root --> SQL
-
+
style Root fill:#e1f5ff
style Algo fill:#ffe1e1
style DS fill:#fff4e1
@@ -108,18 +110,19 @@ graph LR
### ドメイン分類表
-| ドメイン | 主要コードエンティティ | コアパターン | ファイルパスの例 |
-|---------|---------------------|------------|----------------|
-| **Algorithm**
アルゴリズム | `Solution.findMedianSortedArrays()`
`numDecodings()`
`minPathSum()` | 二分探索
動的計画法
Two Pointers | `Algorithm/BinarySearch/leetcode/4. Median of Two Sorted Arrays/`
`Algorithm/DynamicProgramming/leetcode/91. Decode Ways/` |
-| **DataStructures**
データ構造 | `Solution.addTwoNumbers()`
`class ListNode`
`class DoublyLinkedList` | In-place操作
ポインタ操作 | `DataStructures/LinkedList/leetcode/2. Add Two Numbers/`
`DataStructures/DoublyLinkedList/` |
-| **Mathematics**
数学 | `isPalindrome()`
`reflectPoint()`
`gameWithCells()` | 有限状態機械
幾何学的変換 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/`
`Mathematics/Geometry/hackerrank/Army Game/` |
-| **SQL**
データベース | `CombineTwoTables_mysql.md`
`combine_two_tables()` (Pandas)
`RisingTemperature.sql` | JOINパターン
Window関数 | `SQL/Leetcode/Basic join/175. Combine Two Tables/`
`SQL/Leetcode/Window function/` |
+| ドメイン | 主要コードエンティティ | コアパターン | ファイルパスの例 |
+| --------------------------------- | ------------------------------------------------------------------------------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
+| **Algorithm**
アルゴリズム | `Solution.findMedianSortedArrays()`
`numDecodings()`
`minPathSum()` | 二分探索
動的計画法
Two Pointers | `Algorithm/BinarySearch/leetcode/4. Median of Two Sorted Arrays/`
`Algorithm/DynamicProgramming/leetcode/91. Decode Ways/` |
+| **DataStructures**
データ構造 | `Solution.addTwoNumbers()`
`class ListNode`
`class DoublyLinkedList` | In-place操作
ポインタ操作 | `DataStructures/LinkedList/leetcode/2. Add Two Numbers/`
`DataStructures/DoublyLinkedList/` |
+| **Mathematics**
数学 | `isPalindrome()`
`reflectPoint()`
`gameWithCells()` | 有限状態機械
幾何学的変換 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/`
`Mathematics/Geometry/hackerrank/Army Game/` |
+| **SQL**
データベース | `CombineTwoTables_mysql.md`
`combine_two_tables()` (Pandas)
`RisingTemperature.sql` | JOINパターン
Window関数 | `SQL/Leetcode/Basic join/175. Combine Two Tables/`
`SQL/Leetcode/Window function/` |
---
## デュアルAI実装哲学
このリポジトリは、各問題に対して2つの根本的に異なるコーディング哲学を実装し、比較学習体験を提供します。
+
```mermaid
graph TB
subgraph Claude[Claude Sonnet 4.5
競技プログラミング最適化]
@@ -128,17 +131,17 @@ graph TB
C3[パフォーマンス優先
10-15%高速]
C4[ユースケース
LeetCode/Codeforces]
end
-
+
subgraph GPT[GPT-5.1 Thinking
本番環境対応]
G1[包括的な検証
TypeError/ValueError]
G2[Float センチネル値
float 'inf']
G3[堅牢性優先
5-10%低速だがより安全]
G4[ユースケース
本番API/エンタープライズ]
end
-
+
Problem[問題] --> Claude
Problem --> GPT
-
+
style Problem fill:#e1f5ff
style Claude fill:#ffe1e1
style GPT fill:#e8f5e9
@@ -147,6 +150,7 @@ graph TB
### コードエンティティ比較
#### Claude実装パターン
+
```python
# Mathematics/Palindrome/leetcode/9. Palindrome Number/claud sonnet 4.5/
# PalindromeNumber.py
@@ -158,16 +162,17 @@ class Solution:
return False
if x < 10:
return True
-
+
rev: int = 0 # 整数センチネル
while x > rev:
rev = rev * 10 + (x % 10)
x //= 10
-
+
return x == rev or x == rev // 10
```
#### GPT実装パターン
+
```python
# Mathematics/Palindrome/leetcode/9. Palindrome Number/gpt 5.1 thinking customized/
# PalindromeNumber.py
@@ -177,39 +182,40 @@ class Solution:
# 包括的な検証
if type(x) is not int:
raise TypeError("x must be an int")
-
+
INT_MIN, INT_MAX = -2**31, 2**31 - 1
if x < INT_MIN or x > INT_MAX:
raise ValueError("x out of 32-bit range")
-
+
# 検証付きコアロジック
if x < 0 or (x % 10 == 0 and x != 0):
return False
-
+
rev: int = 0
while x > rev:
rev = rev * 10 + (x % 10)
x //= 10
-
+
return x == rev or x == rev // 10
```
### 実装の違い表
-| 側面 | Claude実装 | GPT実装 |
-|-----|-----------|---------|
-| **Pythonメソッドシグネチャ** | `isPalindrome(self, x: int) -> bool` | `isPalindrome(self, x: int) -> bool`
+ `is_palindrome_production(self, x: int) -> bool` |
-| **センチネル値** | 整数: `NEG = -10_000_007`
`POS = +10_000_007` | Float: `float("inf")`
`-float("inf")` |
-| **検証戦略** | 問題制約を信頼
最小限のチェック | 包括的なランタイム検証
TypeError、ValueError例外発生 |
-| **TypeScriptシグネチャ** | `function isPalindrome(x: number): boolean` | `function isPalindrome(x: readonly number[]): boolean`
(不変パラメータ) |
-| **パフォーマンス** | 10-15%高速
(Python 6ms, TypeScript 5ms) | 5-10%低速
(Python 8ms)
より堅牢 |
-| **ユースケース** | LeetCode/Codeforces競技 | 本番API
エンタープライズシステム |
+| 側面 | Claude実装 | GPT実装 |
+| ---------------------------- | ------------------------------------------------- | ------------------------------------------------------------------------------------------- |
+| **Pythonメソッドシグネチャ** | `isPalindrome(self, x: int) -> bool` | `isPalindrome(self, x: int) -> bool`
+ `is_palindrome_production(self, x: int) -> bool` |
+| **センチネル値** | 整数: `NEG = -10_000_007`
`POS = +10_000_007` | Float: `float("inf")`
`-float("inf")` |
+| **検証戦略** | 問題制約を信頼
最小限のチェック | 包括的なランタイム検証
TypeError、ValueError例外発生 |
+| **TypeScriptシグネチャ** | `function isPalindrome(x: number): boolean` | `function isPalindrome(x: readonly number[]): boolean`
(不変パラメータ) |
+| **パフォーマンス** | 10-15%高速
(Python 6ms, TypeScript 5ms) | 5-10%低速
(Python 8ms)
より堅牢 |
+| **ユースケース** | LeetCode/Codeforces競技 | 本番API
エンタープライズシステム |
---
## ファイル組織階層
リポジトリは、任意の成果物を数秒で発見可能にする厳格な6レベル階層構造に従っています。
+
```mermaid
graph TD
L1[Level 1: ドメイン
Algorithm / DataStructures /
Mathematics / SQL]
@@ -218,13 +224,13 @@ graph TD
L4[Level 4: 問題
4. Median of Two Sorted Arrays /
9. Palindrome Number]
L5[Level 5: AIプロバイダー
claud sonnet 4.5 /
gpt 5.1 thinking customized]
L6[Level 6: 成果物
*.py / *.ts / *.js
README.md / README.html /
README_react.html]
-
+
L1 --> L2
L2 --> L3
L3 --> L4
L4 --> L5
L5 --> L6
-
+
style L1 fill:#e1f5ff
style L2 fill:#ffe1e1
style L3 fill:#fff4e1
@@ -235,16 +241,17 @@ graph TD
### ファイル命名規則表
-| ファイルタイプ | 命名パターン | 目的 | パスの例 |
-|-------------|------------|-----|---------|
-| **Python実装** | `{ProblemName}.py` | `class Solution`とコア実装を含む | `Mathematics/Palindrome/leetcode/9. Palindrome Number/claud sonnet 4.5/PalindromeNumber.py` |
-| **TypeScript実装** | `{ProblemName}.ts` | 厳密なチェック付き型安全実装 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/gpt 5.1 thinking customized/PalindromeNumber.ts` |
-| **JavaScript実装** | `{ProblemName}.js` | ランタイム検証実装 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/gpt 5.1 thinking customized/PalindromeNumber.js` |
-| **静的ドキュメント** | `README.md` | 5セクション構造ドキュメント
(セクションあたり100行未満) | 各AIプロバイダーフォルダに`README.md`を含む |
-| **インタラクティブHTML** | `README.html` | Prism.jsシンタックスハイライト
Tailwind CSSスタイリング
1000-2000行 | 各AIプロバイダーフォルダに`README.html`を含む |
-| **React可視化** | `README_react.html` | React 18 + Babel Standalone
インタラクティブデモ
カスタマイズ可能な入力 | 各AIプロバイダーフォルダに`README_react.html`を含む |
+| ファイルタイプ | 命名パターン | 目的 | パスの例 |
+| ------------------------ | ------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
+| **Python実装** | `{ProblemName}.py` | `class Solution`とコア実装を含む | `Mathematics/Palindrome/leetcode/9. Palindrome Number/claud sonnet 4.5/PalindromeNumber.py` |
+| **TypeScript実装** | `{ProblemName}.ts` | 厳密なチェック付き型安全実装 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/gpt 5.1 thinking customized/PalindromeNumber.ts` |
+| **JavaScript実装** | `{ProblemName}.js` | ランタイム検証実装 | `Mathematics/Palindrome/leetcode/9. Palindrome Number/gpt 5.1 thinking customized/PalindromeNumber.js` |
+| **静的ドキュメント** | `README.md` | 5セクション構造ドキュメント
(セクションあたり100行未満) | 各AIプロバイダーフォルダに`README.md`を含む |
+| **インタラクティブHTML** | `README.html` | Prism.jsシンタックスハイライト
Tailwind CSSスタイリング
1000-2000行 | 各AIプロバイダーフォルダに`README.html`を含む |
+| **React可視化** | `README_react.html` | React 18 + Babel Standalone
インタラクティブデモ
カスタマイズ可能な入力 | 各AIプロバイダーフォルダに`README_react.html`を含む |
### 標準ディレクトリ構造
+
```
Mathematics/Palindrome/leetcode/9. Palindrome Number/
├── claud sonnet 4.5/
@@ -266,6 +273,7 @@ Mathematics/Palindrome/leetcode/9. Palindrome Number/
### SQLドメインの例外
SQL問題は、単一の`gpt/`ディレクトリ下にグループ化されたプラットフォーム固有のソリューションファイルを持つ異なる構造に従います。
+
```
SQL/Leetcode/Basic join/175. Combine Two Tables/
└── gpt/
@@ -279,15 +287,16 @@ SQL/Leetcode/Basic join/175. Combine Two Tables/
## 3ティアドキュメントシステム
各問題は、異なるスキルレベルと学習目標をターゲットとする3つの段階的な複雑さレベルでドキュメント化されています。
+
```mermaid
graph LR
T1[Tier 1: 静的
README.md
-----
初心者向け
3,000-5,000単語
10-15分読了]
T2[Tier 2: インタラクティブ
README.html
-----
中級者向け
Prism.js + Tailwind
ステップ制御]
T3[Tier 3: 動的
README_react.html
-----
上級者向け
React 18
リアルタイム入力]
-
+
T1 -->|スキル向上| T2
T2 -->|スキル向上| T3
-
+
style T1 fill:#e8f5e9
style T2 fill:#fff4e1
style T3 fill:#ffe1e1
@@ -295,11 +304,11 @@ graph LR
### ティア機能比較
-| ティア | ファイル | 対象読者 | 主要技術 | 主な機能 |
-|-------|---------|---------|---------|---------|
-| **Tier 1
静的** | `README.md` | 初心者
CS基礎学習者 | Markdown | • 問題概要
• アルゴリズム解説
• 複雑度解析 O(n)
• 実装詳細
• 最適化議論
• 3,000-5,000単語
• 10-15分読了 |
-| **Tier 2
インタラクティブ** | `README.html` | 中級者
競技プログラミング参加者 | Prism.js
Tailwind CSS | • シンタックスハイライト
• ステップ制御システム
(再生/一時停止/前/次/リセット)
• 状態可視化
• SVGフローチャートレンダリング
• 1,000-2,000行 |
-| **Tier 3
動的** | `README_react.html` | 上級エンジニア
パフォーマンス重視 | React 18
Babel Standalone | • React Hooks (useState, useEffect)
• リアルタイム入力変更
• エッジケーステスト
• AI実装比較
(Claude vs GPT並列表示)
• パフォーマンスベンチマーク
• 2,000-4,000行 |
+| ティア | ファイル | 対象読者 | 主要技術 | 主な機能 |
+| ------------------------------- | ------------------- | ------------------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| **Tier 1
静的** | `README.md` | 初心者
CS基礎学習者 | Markdown | • 問題概要
• アルゴリズム解説
• 複雑度解析 O(n)
• 実装詳細
• 最適化議論
• 3,000-5,000単語
• 10-15分読了 |
+| **Tier 2
インタラクティブ** | `README.html` | 中級者
競技プログラミング参加者 | Prism.js
Tailwind CSS | • シンタックスハイライト
• ステップ制御システム
(再生/一時停止/前/次/リセット)
• 状態可視化
• SVGフローチャートレンダリング
• 1,000-2,000行 |
+| **Tier 3
動的** | `README_react.html` | 上級エンジニア
パフォーマンス重視 | React 18
Babel Standalone | • React Hooks (useState, useEffect)
• リアルタイム入力変更
• エッジケーステスト
• AI実装比較
(Claude vs GPT並列表示)
• パフォーマンスベンチマーク
• 2,000-4,000行 |
### Tier 1: 静的ドキュメント構造
@@ -314,22 +323,23 @@ graph LR
### Tier 2: インタラクティブHTML機能
HTMLドキュメントには、埋め込まれたインタラクティブ要素が含まれます。
+
```mermaid
graph TB
HTML[README.html]
-
+
subgraph Features[主要機能]
S1[Prism.jsシンタックスハイライト
Python/TypeScript/JavaScript]
S2[ステップバイステップ実行制御
再生/一時停止/前/次/リセット]
S3[状態可視化
変数値の変化を追跡]
S4[SVGフローチャート
アルゴリズムフロー図解]
end
-
+
HTML --> S1
HTML --> S2
HTML --> S3
HTML --> S4
-
+
style HTML fill:#fff4e1
style S1 fill:#e8f5e9
style S2 fill:#e8f5e9
@@ -338,26 +348,27 @@ graph TB
```
### Tier 3: Reactコンポーネントアーキテクチャ
+
```mermaid
graph TB
React[README_react.html]
-
+
subgraph Components[Reactコンポーネント]
C1[入力フォーム
useStateでリアルタイム更新]
C2[実行ボタン
Claude vs GPT比較実行]
C3[結果表示
パフォーマンスベンチマーク]
C4[エッジケーステスト
境界値検証]
end
-
+
React --> C1
React --> C2
React --> C3
React --> C4
-
+
C1 --> C2
C2 --> C3
C2 --> C4
-
+
style React fill:#ffe1e1
style C1 fill:#e1f5ff
style C2 fill:#e1f5ff
@@ -370,20 +381,21 @@ graph TB
## SQLマルチプラットフォーム戦略
SQL問題は、3つの異なる実行環境で解決され、クロスプラットフォーム学習体験を提供します。
+
```mermaid
graph TB
SQL[SQL問題
例: Combine Two Tables]
-
+
subgraph Platforms[3つのプラットフォーム]
MySQL[MySQL
-----
LEFT JOIN
DATE_SUB/ADD
CREATE INDEX
実行時間: ~45ms]
PostgreSQL[PostgreSQL
-----
DISTINCT ON
LATERAL JOIN
Covering Index
実行時間: ~38ms 最速]
Pandas[Pandas
-----
DataFrame.merge
Series.map
set_index
merge: ~120ms
map: ~65ms]
end
-
+
SQL --> MySQL
SQL --> PostgreSQL
SQL --> Pandas
-
+
style SQL fill:#f3e5f5
style MySQL fill:#e1f5ff
style PostgreSQL fill:#e8f5e9
@@ -393,6 +405,7 @@ graph TB
### プラットフォーム固有のSQLクエリパターン
#### MySQL実装例
+
```sql
-- SQL/Leetcode/Basic join/175. Combine Two Tables/gpt/
-- CombineTwoTables_mysql.md
@@ -411,12 +424,14 @@ LEFT JOIN Address AS a ON a.personId = p.personId;
```
**MySQL特性:**
+
- 小文字テーブル名(person、address)
- 標準LEFT JOIN構文
- `DATE_SUB()`、`DATE_ADD()`による日付操作
- シンプルなインデックス戦略
#### PostgreSQL実装例
+
```sql
-- SQL/Leetcode/Basic join/175. Combine Two Tables/gpt/
-- CombineTwoTables_postgre.md
@@ -443,6 +458,7 @@ LEFT JOIN Address a ON a.personId = p.personId;
```
**PostgreSQL特性:**
+
- `DISTINCT ON`: PostgreSQL固有の重複排除
- `LATERAL JOIN`: 相関サブクエリの代替
- 完全なWindow関数サポート: `LAG`、`LEAD`、`DENSE_RANK`、`ROW_NUMBER`
@@ -450,6 +466,7 @@ LEFT JOIN Address a ON a.personId = p.personId;
- 大文字小文字を区別する引用符付き識別子
#### Pandas実装例
+
```python
# SQL/Leetcode/Basic join/175. Combine Two Tables/gpt/
# CombineTwoTables_pandas.md
@@ -477,18 +494,19 @@ def combine_two_tables_optimized(person: pd.DataFrame, address: pd.DataFrame) ->
"""
# 1:1マッピングの場合、mapの方が高速
address_dict = address.set_index('personId')[['city', 'state']].to_dict('index')
-
+
person['city'] = person['personId'].map(
lambda x: address_dict.get(x, {}).get('city', None)
)
person['state'] = person['personId'].map(
lambda x: address_dict.get(x, {}).get('state', None)
)
-
+
return person[["firstName", "lastName", "city", "state"]]
```
**Pandas特性:**
+
- `DataFrame.merge()`: SQL JOIN相当
- `Series.map()`: 1:1マッピングの最適化
- `get_indexer()`: カスタムインデックス操作
@@ -496,15 +514,15 @@ def combine_two_tables_optimized(person: pd.DataFrame, address: pd.DataFrame) ->
### プラットフォーム機能比較
-| 機能 | MySQL | PostgreSQL | Pandas |
-|-----|-------|-----------|--------|
-| **JOIN構文** | `LEFT JOIN` | `LEFT JOIN`
`LATERAL JOIN` | `DataFrame.merge(how="left")` |
-| **重複排除** | `DISTINCT` | `DISTINCT ON (column)` | `drop_duplicates()` |
-| **日付操作** | `DATE_SUB()`
`DATE_ADD()` | `INTERVAL '1 day'`
`LAG()`
`date_trunc()` | `pd.Timedelta()`
`shift()`
`dt.floor()` |
-| **インデックス作成** | `CREATE INDEX` | Covering indexes
Partial indexes
`CREATE INDEX CONCURRENTLY` | `set_index()`
`get_indexer()`
`MultiIndex` |
-| **Window関数** | 限定的サポート
(MySQL 8.0+) | 完全サポート
`LAG`、`LEAD`
`DENSE_RANK`
`ROW_NUMBER` | `groupby().transform()`
`shift()`
`rolling()` |
-| **識別子** | 大文字小文字区別なし
(デフォルト) | 大文字小文字区別あり
(引用符付き) | 大文字小文字区別あり
(列名) |
-| **実行時間
(100K行)** | ~45ms
(インデックス付き) | ~38ms
(Covering Index)
**最速** | merge(): ~120ms
Series.map(): ~65ms |
+| 機能 | MySQL | PostgreSQL | Pandas |
+| ------------------------- | ------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------- |
+| **JOIN構文** | `LEFT JOIN` | `LEFT JOIN`
`LATERAL JOIN` | `DataFrame.merge(how="left")` |
+| **重複排除** | `DISTINCT` | `DISTINCT ON (column)` | `drop_duplicates()` |
+| **日付操作** | `DATE_SUB()`
`DATE_ADD()` | `INTERVAL '1 day'`
`LAG()`
`date_trunc()` | `pd.Timedelta()`
`shift()`
`dt.floor()` |
+| **インデックス作成** | `CREATE INDEX` | Covering indexes
Partial indexes
`CREATE INDEX CONCURRENTLY` | `set_index()`
`get_indexer()`
`MultiIndex` |
+| **Window関数** | 限定的サポート
(MySQL 8.0+) | 完全サポート
`LAG`、`LEAD`
`DENSE_RANK`
`ROW_NUMBER` | `groupby().transform()`
`shift()`
`rolling()` |
+| **識別子** | 大文字小文字区別なし
(デフォルト) | 大文字小文字区別あり
(引用符付き) | 大文字小文字区別あり
(列名) |
+| **実行時間
(100K行)** | ~45ms
(インデックス付き) | ~38ms
(Covering Index)
**最速** | merge(): ~120ms
Series.map(): ~65ms |
---
@@ -519,16 +537,16 @@ graph TB
TS[TypeScript
標準ライブラリのみ
Array, Object, Math]
JS[JavaScript
標準ライブラリのみ
Array, Object, Math]
end
-
+
subgraph Docs[ドキュメント層
外部ライブラリ許可]
Prism[Prism.js
シンタックスハイライト]
Tailwind[Tailwind CSS
スタイリング]
React[React 18
インタラクティブUI]
Babel[Babel Standalone
JSXトランスパイル]
end
-
+
Core -.->|実装| Docs
-
+
style Core fill:#e8f5e9
style Docs fill:#fff4e1
style Python fill:#e1f5ff
@@ -541,31 +559,35 @@ graph TB
リポジトリは、コア実装に対して厳格なルールを適用します。
**許可されるライブラリ:**
+
- Python標準ライブラリ: `typing`、`collections`、`itertools`、`math`
- JavaScript/TypeScript標準ライブラリ: Arrayメソッド、Objectメソッド、Mathオブジェクト
**禁止されるライブラリ:**
+
- Pythonサードパーティ: `numpy`、`scipy`、`pandas`(アルゴリズム実装内)
- JavaScriptサードパーティ: `lodash`、`underscore`、`ramda`
**理由:**
+
- **教育的透明性**: 学習者が内部実装を理解できる
- **面接との整合性**: 面接環境ではライブラリが利用できない
**ドキュメント層の例外:**
+
- Tier 2: Prism.js、Tailwind CSS
- Tier 3: React 18、Babel Standalone
### 開発環境要件
-| コンポーネント | バージョン/設定 | 目的 |
-|-------------|---------------|-----|
-| **Python** | CPython 3.11.10 | 型ヒント付きアルゴリズム実装 |
-| **Node.js** | v18.x (JavaScript)
v22.14.0 (TypeScript) | TS/JS実装のランタイム環境 |
-| **Bun** | Lockfile version 1 | パッケージ管理と決定論的ビルド |
-| **TypeScript** | @types/node ^22.18.10 | Node.js型定義 |
-| **ESLint** | ^9.37.0 | コード品質検証とリント |
-| **live-server** | ^1.2.2 | ライブリロード開発サーバー |
+| コンポーネント | バージョン/設定 | 目的 |
+| --------------- | -------------------------------------------- | ------------------------------ |
+| **Python** | CPython 3.11.10 | 型ヒント付きアルゴリズム実装 |
+| **Node.js** | v18.x (JavaScript)
v22.14.0 (TypeScript) | TS/JS実装のランタイム環境 |
+| **Bun** | Lockfile version 1 | パッケージ管理と決定論的ビルド |
+| **TypeScript** | @types/node ^22.18.10 | Node.js型定義 |
+| **ESLint** | ^9.37.0 | コード品質検証とリント |
+| **live-server** | ^1.2.2 | ライブリロード開発サーバー |
---
@@ -576,7 +598,7 @@ graph TB
```mermaid
graph TB
Repo[Algorithm-DataStructures
-Math-SQL
リポジトリ]
-
+
subgraph Users[ユーザーセグメント]
U1[アルゴリズム学習者
CS学生/独学者]
U2[競技プログラミング準備
LeetCode/HackerRank参加者]
@@ -584,13 +606,13 @@ graph TB
U4[パフォーマンス最適化研究
ソフトウェアエンジニア]
U5[教育指導
講師/チューター/メンター]
end
-
+
Repo --> U1
Repo --> U2
Repo --> U3
Repo --> U4
Repo --> U5
-
+
style Repo fill:#e1f5ff
style U1 fill:#e8f5e9
style U2 fill:#fff4e1
@@ -601,28 +623,28 @@ graph TB
### 主要ユースケースマトリックス
-| ユースケース | 対象ユーザー | 活用機能 | 推奨ティア |
-|------------|------------|---------|-----------|
-| **アルゴリズム学習** | CS学生
独学者 | • 包括的リファレンス
• 複雑度解析
• ステップバイステップ可視化 | Tier 1 → 2 |
-| **競技プログラミング準備** | LeetCode/HackerRank
参加者 | • 最適化されたソリューション
• 最小限の検証
• Claude実装(スピード優先)
• プラットフォーム固有アプローチ | Tier 1 + Claude |
-| **技術面接準備** | 求職者
キャリアチェンジ | • 実装パターン
• ベストプラクティス
• 複数のソリューションアプローチ
• エッジケース処理 | Tier 1 → 3
Claude + GPT |
-| **パフォーマンス最適化研究** | ソフトウェアエンジニア
研究者 | • 言語固有技術
• 並列実装比較
• ベンチマーク
• GPT実装(安全性優先) | Tier 3
GPT |
-| **教育指導** | 講師
チューター
メンター | • 3ティア学習システム
• 段階的複雑さ
• ビジュアルエイド
• インタラクティブデモ | 全ティア |
-| **多言語一貫性研究** | ポリグロット開発者
言語比較研究者 | • Python/TypeScript/JavaScript統一API
• 言語間パフォーマンス比較 | 全言語
Tier 3 |
+| ユースケース | 対象ユーザー | 活用機能 | 推奨ティア |
+| ---------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------- |
+| **アルゴリズム学習** | CS学生
独学者 | • 包括的リファレンス
• 複雑度解析
• ステップバイステップ可視化 | Tier 1 → 2 |
+| **競技プログラミング準備** | LeetCode/HackerRank
参加者 | • 最適化されたソリューション
• 最小限の検証
• Claude実装(スピード優先)
• プラットフォーム固有アプローチ | Tier 1 + Claude |
+| **技術面接準備** | 求職者
キャリアチェンジ | • 実装パターン
• ベストプラクティス
• 複数のソリューションアプローチ
• エッジケース処理 | Tier 1 → 3
Claude + GPT |
+| **パフォーマンス最適化研究** | ソフトウェアエンジニア
研究者 | • 言語固有技術
• 並列実装比較
• ベンチマーク
• GPT実装(安全性優先) | Tier 3
GPT |
+| **教育指導** | 講師
チューター
メンター | • 3ティア学習システム
• 段階的複雑さ
• ビジュアルエイド
• インタラクティブデモ | 全ティア |
+| **多言語一貫性研究** | ポリグロット開発者
言語比較研究者 | • Python/TypeScript/JavaScript統一API
• 言語間パフォーマンス比較 | 全言語
Tier 3 |
### スキルレベル進行
```mermaid
graph LR
Beginner[初心者
-----
CS初心者
競技プログラミング
新規参入者
-----
期間: 1-2ヶ月
目標: アルゴリズム
基礎理解]
-
+
Intermediate[中級者
-----
競技プログラミング
参加者
面接準備
CS専攻学生
-----
期間: 2-4ヶ月
目標: 実装能力と
デバッグスキル]
-
+
Advanced[上級者
-----
ソフトウェア
エンジニア
言語最適化研究者
テックリード
-----
期間: 継続的
目標: 最適化戦略と
アーキテクチャ設計]
-
+
Beginner -->|スキル向上| Intermediate
Intermediate -->|スキル向上| Advanced
-
+
style Beginner fill:#e8f5e9
style Intermediate fill:#fff4e1
style Advanced fill:#ffe1e1
@@ -630,11 +652,11 @@ graph LR
### 学習期間と目標
-| レベル | 対象ユーザー | 推奨アプローチ | 学習期間 | 達成目標 |
-|-------|------------|-------------|---------|---------|
-| **初心者** | • CS初心者
• 競技プログラミング新規参入者
• プログラミング基礎学習者 | • Tier 1静的ドキュメントから開始
• 基本概念を理解
• 複雑度解析を学習
• シンプルな問題から取り組む | 1-2ヶ月 | アルゴリズム基礎理解 |
-| **中級者** | • 競技プログラミング参加者
• 面接準備
• CS専攻学生 | • Tier 2インタラクティブHTMLで実行検証
• 多言語実装を比較
• エッジケースを理解 | 2-4ヶ月 | 実装能力とデバッグスキル |
-| **上級者** | • ソフトウェアエンジニア
• 言語最適化研究者
• テックリード | • Tier 3 React可視化で詳細分析
• 本番vs競技実装を検証
• パフォーマンスチューニング | 継続的 | 最適化戦略とアーキテクチャ設計 |
+| レベル | 対象ユーザー | 推奨アプローチ | 学習期間 | 達成目標 |
+| ---------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------ |
+| **初心者** | • CS初心者
• 競技プログラミング新規参入者
• プログラミング基礎学習者 | • Tier 1静的ドキュメントから開始
• 基本概念を理解
• 複雑度解析を学習
• シンプルな問題から取り組む | 1-2ヶ月 | アルゴリズム基礎理解 |
+| **中級者** | • 競技プログラミング参加者
• 面接準備
• CS専攻学生 | • Tier 2インタラクティブHTMLで実行検証
• 多言語実装を比較
• エッジケースを理解 | 2-4ヶ月 | 実装能力とデバッグスキル |
+| **上級者** | • ソフトウェアエンジニア
• 言語最適化研究者
• テックリード | • Tier 3 React可視化で詳細分析
• 本番vs競技実装を検証
• パフォーマンスチューニング | 継続的 | 最適化戦略とアーキテクチャ設計 |
---
diff --git a/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_pandas.ipynb b/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_pandas.ipynb
new file mode 100644
index 00000000..8604b911
--- /dev/null
+++ b/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_pandas.ipynb
@@ -0,0 +1,529 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "d461c9db",
+ "metadata": {},
+ "source": [
+ "## 0) 前提\n",
+ "\n",
+ "* 環境: **Python 3.10.15 / pandas 2.2.2**\n",
+ "* **指定シグネチャ厳守**: `def daily_active_users(activity: pd.DataFrame) -> pd.DataFrame`\n",
+ "* I/O 禁止、不要な `print` や `sort_values` は使用しない\n",
+ "* Activity は重複行を含む可能性があるので、**(activity_date, user_id) でユニーク化してからカウント**\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 1) 問題\n",
+ "\n",
+ "* `{{PROBLEM_STATEMENT}}`\n",
+ " `Activity` データフレームから、**2019-07-27 を含む直近 30 日間**における、日別アクティブユーザー数を求める。\n",
+ " あるユーザーが「その日に 1 回以上アクティビティを行っていれば」その日はアクティブとみなす。\n",
+ " アクティビティ種別(`activity_type`)は `'open_session', 'end_session', 'scroll_down', 'send_message'` のいずれも有効とみなす。\n",
+ " アクティブユーザー数が 0 の日は結果に含めない。\n",
+ "\n",
+ "* `{{INPUT_DATAFRAMES}}`\n",
+ "\n",
+ " ```text\n",
+ " activity: pd.DataFrame\n",
+ " columns:\n",
+ " - user_id (int)\n",
+ " - session_id (int)\n",
+ " - activity_date (date または datetime64[ns] 相当)\n",
+ " - activity_type (category/str: 'open_session', 'end_session', 'scroll_down', 'send_message')\n",
+ " ```\n",
+ "\n",
+ "* `{{OUTPUT_COLUMNS_AND_RULES}}`\n",
+ "\n",
+ " ```text\n",
+ " 出力: pd.DataFrame\n",
+ " columns:\n",
+ " - day (date) … activity_date\n",
+ " - active_users (int) … その日に 1 回以上アクティビティを行ったユニーク user_id 数\n",
+ "\n",
+ " ルール:\n",
+ " - 対象期間: 2019-06-28 ~ 2019-07-27(両端含む)…「2019-07-27 を含む 30 日間」\n",
+ " - アクティブ判定は「その日の (user_id, activity_date) が 1 回以上存在」\n",
+ " - ユーザー数 0 の日は含めない(=そもそも行が存在しない)\n",
+ " - 行順は任意(sort は不要)\n",
+ " ```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2) 実装(指定シグネチャ厳守)\n",
+ "\n",
+ "> 列最小化 → 期間フィルタ → `(user_id, activity_date)` でユニーク化 → `groupby` で日別カウント。\n",
+ "\n",
+ "```python\n",
+ "import pandas as pd\n",
+ "\n",
+ "def daily_active_users(activity: pd.DataFrame) -> pd.DataFrame:\n",
+ " \"\"\"\n",
+ " Returns:\n",
+ " pd.DataFrame: 列名と順序は ['day', 'active_users']\n",
+ " \"\"\"\n",
+ " # 期間フィルタ: 2019-07-27 を含む直近 30 日間 → 2019-06-28〜2019-07-27\n",
+ " start = pd.to_datetime(\"2019-06-28\")\n",
+ " end = pd.to_datetime(\"2019-07-27\")\n",
+ " mask = (activity[\"activity_date\"] >= start) & (activity[\"activity_date\"] <= end)\n",
+ "\n",
+ " # 列最小化 + ユニーク化: 1 ユーザーが 1 日に複数行あっても 1 回とカウントする\n",
+ " uniq = (\n",
+ " activity.loc[mask, [\"user_id\", \"activity_date\"]]\n",
+ " .drop_duplicates()\n",
+ " )\n",
+ "\n",
+ " # 日別にユーザー数をカウント(ユーザー ID のユニーク数)\n",
+ " result = (\n",
+ " uniq\n",
+ " .groupby(\"activity_date\", as_index=False)[\"user_id\"]\n",
+ " .nunique()\n",
+ " )\n",
+ "\n",
+ " # 列名を仕様どおりに整形\n",
+ " result = result.rename(\n",
+ " columns={\n",
+ " \"activity_date\": \"day\",\n",
+ " \"user_id\": \"active_users\",\n",
+ " }\n",
+ " )\n",
+ "\n",
+ " return result\n",
+ "\n",
+ "Analyze Complexity\n",
+ "Runtime 316 ms\n",
+ "Beats 50.66%\n",
+ "Memory 68.35 MB\n",
+ "Beats 38.32%\n",
+ "\n",
+ "```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3) アルゴリズム説明\n",
+ "\n",
+ "使用している主な pandas API:\n",
+ "\n",
+ "1. **期間フィルタ**:\n",
+ "\n",
+ " ```python\n",
+ " mask = (activity[\"activity_date\"] >= start) & (activity[\"activity_date\"] <= end)\n",
+ " activity.loc[mask, ...]\n",
+ " ```\n",
+ "\n",
+ " * `activity_date` が `datetime64[ns]` / `date` でも比較可能。\n",
+ " * 2019-06-28〜2019-07-27 に含まれる行だけを対象とする。\n",
+ "\n",
+ "2. **列最小化 & ユニーク化**:\n",
+ "\n",
+ " ```python\n",
+ " activity.loc[mask, [\"user_id\", \"activity_date\"]].drop_duplicates()\n",
+ " ```\n",
+ "\n",
+ " * 使うのはアクティブ判定のキーだけなので、列を `user_id, activity_date` に絞る。\n",
+ " * 同じユーザーが同じ日に複数アクティビティをしていても、\n",
+ " `(user_id, activity_date)` のペアが一意になるように `drop_duplicates()`。\n",
+ "\n",
+ "3. **日別アクティブユーザー数**:\n",
+ "\n",
+ " ```python\n",
+ " uniq.groupby(\"activity_date\", as_index=False)[\"user_id\"].nunique()\n",
+ " ```\n",
+ "\n",
+ " * `groupby(\"activity_date\")` で日ごとにまとめる。\n",
+ " * 各日について `user_id` のユニーク数(`nunique()`)を取り、アクティブユーザー数とする。\n",
+ " * アクティビティが 1 件もない日付は `uniq` 自体に現れないため、\n",
+ " 自然と結果にも含まれない(=「0 の日は除外」要件を満たす)。\n",
+ "\n",
+ "4. **列名整形**:\n",
+ "\n",
+ " ```python\n",
+ " result.rename(columns={\"activity_date\": \"day\", \"user_id\": \"active_users\"})\n",
+ " ```\n",
+ "\n",
+ " * 問題の要求どおり出力列名を `day`, `active_users` に揃える。\n",
+ " * 並び順も `[day, active_users]` になるようにしている。\n",
+ "\n",
+ "### NULL / 重複 / 型\n",
+ "\n",
+ "* **重複**:\n",
+ " `(user_id, activity_date)` で `drop_duplicates()` しているため、\n",
+ " 同じユーザーが同日に 10 回アクションしても **1 ユーザーとしてカウント**される。\n",
+ "* **NULL**:\n",
+ "\n",
+ " * `activity_date` や `user_id` が `NaN` の行は、条件に合致しても\n",
+ " ペアとして扱いづらいため、通常はデータ仕様上「存在しない前提」。\n",
+ " (もし存在すれば、`NaN` を含む行も 1 ユニットとして扱われるので、\n",
+ " 必要に応じて `dropna(subset=[\"user_id\", \"activity_date\"])` を噛ませてもよい。)\n",
+ "* **型**:\n",
+ "\n",
+ " * `activity_date` が文字列の場合でも、`to_datetime` 済みなら問題なく比較できる。\n",
+ " * `user_id` は `int` 前提だが、`nunique()` は dtype に依存せず機能する。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4) 計算量(概算)\n",
+ "\n",
+ "Activity の行数を **N**、期間内に残る行数を **N₃₀** とすると:\n",
+ "\n",
+ "1. **期間フィルタ** `activity.loc[mask, ...]`\n",
+ "\n",
+ " * 各行を 1 回見て条件を評価 → **O(N)**\n",
+ "\n",
+ "2. **`drop_duplicates()`** on `[\"user_id\", \"activity_date\"]`\n",
+ "\n",
+ " * ハッシュベースの重複除去 → **O(N₃₀)** 〜 **O(N₃₀ log N₃₀)** 近辺\n",
+ "\n",
+ "3. **`groupby(\"activity_date\")[\"user_id\"].nunique()`**\n",
+ "\n",
+ " * グループ数を D(日数 ≤ 30)とすると\n",
+ " **O(N₃₀)**(ハッシュ集約)程度\n",
+ "\n",
+ "総じて、支配的なのは **O(N₃₀)**~**O(N₃₀ log N₃₀)**。\n",
+ "日数 D は最大 30 と小さいため、D によるオーバーヘッドは無視できるレベルです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 5) 図解(Mermaid 超保守版)\n",
+ "\n",
+ "```mermaid\n",
+ "flowchart TD\n",
+ " A[activity DF] --> B[期間フィルタ 2019-06-28〜2019-07-27]\n",
+ " B --> C[列最小化 user_id, activity_date]\n",
+ " C --> D[\"(user_id, activity_date) でdrop_duplicates\"]\n",
+ " D --> E[\"groupby(activity_date) user_id.nunique()\"]\n",
+ " E --> F[列名を day, active_users に整形]\n",
+ " F --> G[\"出力 DF (day, active_users)\"]\n",
+ "```\n",
+ "\n",
+ "この実装は\n",
+ "\n",
+ "* I/O なし\n",
+ "* `print` / `sort_values` 不使用\n",
+ "* 指定シグネチャ・出力列仕様を満たす\n",
+ "\n",
+ "ので、LeetCode などの Pandas 問題でもそのまま利用できます。\n",
+ "\n",
+ "結論から言うと、\n",
+ "\n",
+ "* **316ms / 上位 50% は十分合格ライン**\n",
+ "* LeetCode の Pandas 環境だと、**劇的な改善はほぼ出ません**\n",
+ "* とはいえ、**drop_duplicates を消す & 中間 DF を減らす**くらいのチューニング余地はあります\n",
+ "\n",
+ "という感じです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 1) ロジック的な改善ポイント\n",
+ "\n",
+ "今のコードはざっくりこんな流れでした:\n",
+ "\n",
+ "```python\n",
+ "mask = (activity[\"activity_date\"] >= start) & (activity[\"activity_date\"] <= end)\n",
+ "\n",
+ "uniq = (\n",
+ " activity.loc[mask, [\"user_id\", \"activity_date\"]]\n",
+ " .drop_duplicates()\n",
+ ")\n",
+ "\n",
+ "result = (\n",
+ " uniq\n",
+ " .groupby(\"activity_date\", as_index=False)[\"user_id\"]\n",
+ " .nunique()\n",
+ ")\n",
+ "```\n",
+ "\n",
+ "ここで\n",
+ "\n",
+ "* `(user_id, activity_date)` を `drop_duplicates` した後、\n",
+ "* さらに `groupby().nunique()` で `user_id` のユニーク数を数えている\n",
+ "\n",
+ "ので、**「重複除去」を 2 回やっている**イメージになっています。\n",
+ "\n",
+ "この問題は「日ごとの user_id のユニーク数」が欲しいだけなので、\n",
+ "\n",
+ "* **`drop_duplicates` をやめて**\n",
+ "* そのまま `groupby().nunique()` だけにする\n",
+ "\n",
+ "方がシンプル&速くなる可能性が高いです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2) 改訂版コード(中間処理を削る)\n",
+ "\n",
+ "```python\n",
+ "import pandas as pd\n",
+ "\n",
+ "def daily_active_users(activity: pd.DataFrame) -> pd.DataFrame:\n",
+ " \"\"\"\n",
+ " Returns:\n",
+ " pd.DataFrame: 列名と順序は ['day', 'active_users']\n",
+ " \"\"\"\n",
+ " # 定数は一度だけ to_datetime(ここはほぼオーバーヘッドになりません)\n",
+ " start = pd.to_datetime(\"2019-06-28\")\n",
+ " end = pd.to_datetime(\"2019-07-27\")\n",
+ "\n",
+ " # 期間フィルタ + 列最小化\n",
+ " filtered = activity.loc[\n",
+ " (activity[\"activity_date\"] >= start) & (activity[\"activity_date\"] <= end),\n",
+ " [\"user_id\", \"activity_date\"],\n",
+ " ]\n",
+ "\n",
+ " # 日別に user_id のユニーク数をカウント\n",
+ " result = (\n",
+ " filtered\n",
+ " .groupby(\"activity_date\", as_index=False)[\"user_id\"]\n",
+ " .nunique()\n",
+ " .rename(columns={\"activity_date\": \"day\", \"user_id\": \"active_users\"})\n",
+ " )\n",
+ "\n",
+ " return result\n",
+ "\n",
+ "Analyze Complexity\n",
+ "Runtime 341 ms\n",
+ "Beats 24.18%\n",
+ "Memory 68.30 MB\n",
+ "Beats 49.34%\n",
+ "\n",
+ "```\n",
+ "\n",
+ "### 変更点まとめ\n",
+ "\n",
+ "1. **`drop_duplicates()` を削除**\n",
+ "\n",
+ " * `groupby(\"activity_date\")[\"user_id\"].nunique()` が「日 × user_id のユニーク数」を出してくれるので、\n",
+ " 事前の `(user_id, activity_date)` ユニーク化は不要です。\n",
+ "2. **マスクを直接 `.loc` の中で使っても良い**\n",
+ "\n",
+ " * メモリ的にはほぼ同じですが、コードが少しスリムになります。\n",
+ "3. **中間 DF を 1 つだけに削減**\n",
+ "\n",
+ " * `uniq` をなくして `filtered` → `result` の 2 段構成に。\n",
+ "\n",
+ "これで計算量もメモリ使用量も、**わずかにだけ軽く**なります。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3) メモリ面の改善余地\n",
+ "\n",
+ "LeetCode のメモリ 68MB / Beat 38% も、正直かなり十分ですが、\n",
+ "\n",
+ "「理論上できること」としては:\n",
+ "\n",
+ "1. **使わない列を早めに捨てる**\n",
+ "\n",
+ " ```python\n",
+ " filtered = activity.loc[mask, [\"user_id\", \"activity_date\"]]\n",
+ " ```\n",
+ "\n",
+ " → これはすでにやっています。`session_id` / `activity_type` を持ち回っていないので OK。\n",
+ "\n",
+ "2. **型のダウンサイジング**\n",
+ "\n",
+ " * 実務なら、`user_id`, `session_id` を `int32` に落としたり、`activity_type` を `category` にしてメモリ削減が効きます。\n",
+ " * ただし LeetCode では\n",
+ "\n",
+ " * 変換コスト > メリット\n",
+ " * 評価環境の測り方次第\n",
+ " となることが多く、あまりスコアに出ないことも多いです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4) どこまで頑張るべきか\n",
+ "\n",
+ "* Pandas 版で **Runtime Beats ~50%** は、\n",
+ " 「アルゴリズムも実装も特に悪くない」ラインです。\n",
+ "* Leaderboard 上位は\n",
+ "\n",
+ " * CPython のバージョン差\n",
+ " * ジャストタイミングで速いマシンに当たる\n",
+ " * 評価のランダム誤差\n",
+ " なども効いてくるので、**数十 ms の差はほぼノイズ**です。\n",
+ "\n",
+ "なので、\n",
+ "\n",
+ "* ロジックをシンプルにして二重の重複除去をやめる(上記修正版)\n",
+ "* それ以上は「趣味の世界」\n",
+ "\n",
+ "くらいに考えておくのが現実的かな、と思います。\n",
+ "\n",
+ "「pandas は入出力のフレームだけ、ロジックは全部 NumPy」でゴリ押ししてみましょう。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 1) 方針(全部 NumPy)\n",
+ "\n",
+ "やりたいことはシンプルに言うと:\n",
+ "\n",
+ "1. `activity_date` を 30 日間でフィルタ\n",
+ "2. その期間内の `(activity_date, user_id)` ペアをユニークにする\n",
+ " → 「同じユーザーが同じ日に何回活動しても 1 回」\n",
+ "3. 日別にペア数(= ユニーク user 数)を数える\n",
+ "\n",
+ "これを **全部 NumPy の `unique` でやる**、という作戦です。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2) 実装例(NumPy ゴリ押し版)\n",
+ "\n",
+ "LeetCode の Pandas 版シグネチャを想定して、こう書けます:\n",
+ "\n",
+ "```python\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "\n",
+ "def daily_active_users(activity: pd.DataFrame) -> pd.DataFrame:\n",
+ " \"\"\"\n",
+ " NumPy メイン実装:\n",
+ " - フィルタ, 集約はすべて NumPy で実施\n",
+ " - pandas は列の取り出し & 最終 DataFrame 化のみ\n",
+ " Returns:\n",
+ " pd.DataFrame: 列名と順序は ['day', 'active_users']\n",
+ " \"\"\"\n",
+ " # --- 1) 必要列を NumPy 配列として取得 ---\n",
+ " # 日付は日単位の datetime64[D] にしておくと扱いやすい\n",
+ " dates = activity[\"activity_date\"].to_numpy(dtype=\"datetime64[D]\")\n",
+ " users = activity[\"user_id\"].to_numpy()\n",
+ "\n",
+ " # --- 2) 30 日間の期間フィルタ ---\n",
+ " start = np.datetime64(\"2019-06-28\", \"D\")\n",
+ " end = np.datetime64(\"2019-07-27\", \"D\")\n",
+ " mask = (dates >= start) & (dates <= end)\n",
+ "\n",
+ " dates = dates[mask]\n",
+ " users = users[mask]\n",
+ "\n",
+ " if dates.size == 0:\n",
+ " # 対象期間に何もなければ空 DataFrame を返す\n",
+ " return pd.DataFrame({\"day\": pd.Series([], dtype=\"datetime64[ns]\"),\n",
+ " \"active_users\": pd.Series([], dtype=\"int64\")})\n",
+ "\n",
+ " # --- 3) (date, user) ペアを NumPy structured array で表現 ---\n",
+ " # これで np.unique でペア単位のユニークが取れる\n",
+ " pairs = np.empty(dates.shape[0], dtype=[(\"day\", \"datetime64[D]\"), (\"user\", users.dtype)])\n",
+ " pairs[\"day\"] = dates\n",
+ " pairs[\"user\"] = users\n",
+ "\n",
+ " # --- 4) ペアをユニーク化(同じユーザーが同日に複数回いても 1 回に) ---\n",
+ " uniq_pairs = np.unique(pairs) # ソートもかかる\n",
+ "\n",
+ " # --- 5) 日付ごとにユニークユーザー数を数える ---\n",
+ " unique_days, counts = np.unique(uniq_pairs[\"day\"], return_counts=True)\n",
+ "\n",
+ " # --- 6) pandas DataFrame に戻す ---\n",
+ " # np.datetime64[D] → DataFrame 生成時に datetime64[ns] に昇格する\n",
+ " result = pd.DataFrame({\n",
+ " \"day\": unique_days.astype(\"datetime64[ns]\"),\n",
+ " \"active_users\": counts.astype(\"int64\"),\n",
+ " })\n",
+ "\n",
+ " return result\n",
+ "\n",
+ "Analyze Complexity\n",
+ "Runtime 290 ms\n",
+ "Beats 89.97%\n",
+ "Memory 67.06 MB\n",
+ "Beats 99.84%\n",
+ "\n",
+ "```\n",
+ "\n",
+ "ポイント:\n",
+ "\n",
+ "* **グループ化や重複除去は全部 `np.unique`** でやっているので、pandas の `groupby` / `drop_duplicates` は一切使っていません。\n",
+ "* `structured array`(構造化配列)で `(day, user)` のタプルを 1 要素として扱うことで、\n",
+ " `np.unique` が「行単位でのユニーク」を取ってくれます。\n",
+ "* `np.unique(uniq_pairs[\"day\"], return_counts=True)` で\n",
+ " 「日付ごとの (日, ユーザー) ペア数」をまとめて計算。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3) アルゴリズムの流れ(NumPy 目線)\n",
+ "\n",
+ "1. **カラム抽出(pandas → NumPy)**\n",
+ "\n",
+ " ```python\n",
+ " dates = activity[\"activity_date\"].to_numpy(\"datetime64[D]\")\n",
+ " users = activity[\"user_id\"].to_numpy()\n",
+ " ```\n",
+ "\n",
+ "2. **期間フィルタ(ブールマスク)**\n",
+ "\n",
+ " ```python\n",
+ " mask = (dates >= start) & (dates <= end)\n",
+ " dates = dates[mask]\n",
+ " users = users[mask]\n",
+ " ```\n",
+ "\n",
+ "3. **構造化配列で `(day, user)` を表現**\n",
+ "\n",
+ " ```python\n",
+ " pairs = np.empty(dates.shape[0], dtype=[(\"day\", \"datetime64[D]\"), (\"user\", users.dtype)])\n",
+ " pairs[\"day\"] = dates\n",
+ " pairs[\"user\"] = users\n",
+ " ```\n",
+ "\n",
+ "4. **ペアのユニーク化**\n",
+ "\n",
+ " ```python\n",
+ " uniq_pairs = np.unique(pairs)\n",
+ " ```\n",
+ "\n",
+ " これで「同じ user が同じ day に複数回活動していても一つにまとまる」。\n",
+ "\n",
+ "5. **日別にカウント**\n",
+ "\n",
+ " ```python\n",
+ " unique_days, counts = np.unique(uniq_pairs[\"day\"], return_counts=True)\n",
+ " ```\n",
+ "\n",
+ " `counts[i]` が `unique_days[i]` のユニーク user 数(= アクティブユーザー数)。\n",
+ "\n",
+ "6. **DataFrame へ逆変換**\n",
+ "\n",
+ " ```python\n",
+ " pd.DataFrame({\"day\": unique_days.astype(\"datetime64[ns]\"),\n",
+ " \"active_users\": counts.astype(\"int64\")})\n",
+ " ```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4) 計算量 & パフォーマンスの雰囲気\n",
+ "\n",
+ "`N` = 30 日間に入る行数とすると、\n",
+ "\n",
+ "* 期間フィルタ: `O(N)`\n",
+ "* 構造化配列作成: `O(N)`\n",
+ "* `np.unique(pairs)`(ペア単位ユニーク + ソート):\n",
+ " `O(N log N)`(内部ソート)くらい\n",
+ "* `np.unique(uniq_pairs[\"day\"], return_counts=True)`:\n",
+ " ユニークな日数を `D` とすると `O(D log D)`(ただし D ≤ 30 なので誤差)\n",
+ "\n",
+ "⇒ **スループットの支配は 2 つの `np.unique`** で、\n",
+ "概ね **O(N log N)** です。\n",
+ "pandas の `groupby().nunique()` と同じオーダーですが、\n",
+ "NumPy 直叩きのぶん、オーバーヘッドはだいぶ小さいです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 5) おまけ:さらに削るとしたら?\n",
+ "\n",
+ "* `activity_date` が最初から `datetime64[ns]` なら、`to_numpy(\"datetime64[D]\")` で日単位に丸める処理はそのまま速いです。\n",
+ "* ユーザー ID を `int32` に落とせる(値の範囲が小さい)のであれば、\n",
+ " `users.astype(\"int32\")` でメモリを少し削ることもできます。\n",
+ " (LeetCode だとスコアに出ないことも多いですが。)\n",
+ "\n",
+ "---\n",
+ "\n",
+ "こんな感じで、ロジックはほぼ NumPy だけで完結させられます。\n",
+ "「pandas 禁止縛りのコードレビュー」みたいな場でも、そのまま説明材料に使えるはずです。"
+ ]
+ }
+ ],
+ "metadata": {
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_postgre.ipynb b/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_postgre.ipynb
new file mode 100644
index 00000000..a6e5f7b8
--- /dev/null
+++ b/SQL/Leetcode/Basic select/1141. User Activity for the Past 30 Days I/gpt 5.1 thinking customized/User_Activity_for_the_Past_30_Days_I_postgre.ipynb
@@ -0,0 +1,299 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "cc9d8060",
+ "metadata": {},
+ "source": [
+ "## 0) 前提\n",
+ "\n",
+ "* エンジン: **PostgreSQL 16.6+**\n",
+ "* 並び順: 任意(ここでは `ORDER BY day` を付けますが省略可)\n",
+ "* `NOT IN` は使用せず、今回はそもそも不要\n",
+ "* **判定は user_id 基準**(1 日に 1 回以上アクティビティがあれば「アクティブ」)\n",
+ "* Activity テーブルは **重複行あり** なので、**日 + user_id でユニークにしてから件数カウント**する \n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 1) 問題\n",
+ "\n",
+ "* `{{PROBLEM_STATEMENT}}`\n",
+ " 2019-07-27 を終点とする直近 30 日間について、日別アクティブユーザー数(1 日に 1 回以上アクティビティがあった user 数)を求める。アクティビティ種別は `('open_session', 'end_session', 'scroll_down', 'send_message')` のどれであっても有効とみなす。ユーザー数が 0 人の日は結果に含めない。\n",
+ "\n",
+ "* `{{TABLES_OR_SCHEMAS}}`\n",
+ "\n",
+ " ```text\n",
+ " Table: Activity\n",
+ " +---------------+---------+\n",
+ " | Column Name | Type |\n",
+ " +---------------+---------+\n",
+ " | user_id | int |\n",
+ " | session_id | int |\n",
+ " | activity_date | date |\n",
+ " | activity_type | enum -- 'open_session', 'end_session', 'scroll_down', 'send_message'\n",
+ " +---------------+---------+\n",
+ " -- 1 session_id は必ず 1 user_id に属する\n",
+ " -- 行は重複している可能性がある\n",
+ " ```\n",
+ "\n",
+ "* `{{OUTPUT_COLUMNS_AND_RULES}}`\n",
+ "\n",
+ " ```text\n",
+ " 出力:\n",
+ " +------------+--------------+\n",
+ " | day | active_users |\n",
+ " +------------+--------------+\n",
+ " | DATE | INT |\n",
+ " +------------+--------------+\n",
+ "\n",
+ " ルール:\n",
+ " - 対象期間は 2019-06-28 ~ 2019-07-27(両端を含む)\n",
+ " └ 2019-07-27 を含む 30 日間なので 27 日 - 29 日 = 28 日\n",
+ " - ある day に 1 回以上アクティビティを行った user_id を「その日のアクティブユーザー」と数える\n",
+ " - 日別のアクティブユーザー数を集計する\n",
+ " - アクティブユーザーが 0 人の日は結果から除外\n",
+ " - 結果の並び順は任意(ここでは day 昇順)\n",
+ " ```\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2) 最適解(単一クエリ)\n",
+ "\n",
+ "> 今回はウィンドウ関数は不要なので、**CTE でユニーク化 → GROUP BY 集計**のシンプル構成にします。\n",
+ "\n",
+ "```sql\n",
+ "WITH uniq_activity AS (\n",
+ " SELECT DISTINCT\n",
+ " user_id,\n",
+ " activity_date\n",
+ " FROM Activity\n",
+ " WHERE activity_date BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'\n",
+ ")\n",
+ "SELECT\n",
+ " activity_date AS day,\n",
+ " COUNT(*) AS active_users\n",
+ "FROM uniq_activity\n",
+ "GROUP BY activity_date\n",
+ "ORDER BY day;\n",
+ "\n",
+ "Runtime 351 ms\n",
+ "Beats 67.57%\n",
+ "\n",
+ "```\n",
+ "\n",
+ "### ポイント\n",
+ "\n",
+ "* `uniq_activity` で\n",
+ "\n",
+ " * 期間フィルタ: `activity_date BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'`\n",
+ " * `user_id, activity_date` を `DISTINCT` して同じユーザー・同じ日に複数行あっても 1 回と数える準備\n",
+ "* メインクエリで\n",
+ "\n",
+ " * `GROUP BY activity_date` し `COUNT(*)` でその日のアクティブユーザー数を算出\n",
+ "* 0 ユーザーの日はそもそも行が存在しないので結果に出てこない(要件どおり)\n",
+ "\n",
+ "`COUNT(DISTINCT user_id)` を直接使ってもよいですが、重複除去と期間フィルタを明示したほうが読みやすいため、CTE で分けています。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3) 要点解説\n",
+ "\n",
+ "1. **30 日間の計算方法**\n",
+ "\n",
+ " * 終端: `2019-07-27`\n",
+ " * そこから 29 日前: `2019-06-28`\n",
+ " * よって `BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'` で 30 日間がカバーされる\n",
+ "\n",
+ "2. **「アクティブユーザー」の定義を SQL に落とす**\n",
+ "\n",
+ " * 1 日に 1 回以上アクティビティがあればよい\n",
+ " → 「日 + user_id の組み合わせ」を 1 ユニットとして数えればよい\n",
+ " * そのために `SELECT DISTINCT user_id, activity_date` で重複を除く\n",
+ "\n",
+ "3. **重複行対策**\n",
+ "\n",
+ " * 元テーブルに全く同一の行が複数あっても、\n",
+ "\n",
+ " * CTE 内で `DISTINCT(user_id, activity_date)` することで\n",
+ " * 集計時のカウントに影響が出ないようにしている\n",
+ "\n",
+ "4. **0 ユーザー日の扱い**\n",
+ "\n",
+ " * Activity に 1 行もない日付は CTE にも現れない\n",
+ " * GROUP BY 結果にも出てこないため、自動的に「0 ユーザーの日は結果に含めない」挙動になる\n",
+ "\n",
+ "5. **インデックス設計(パフォーマンス)**\n",
+ "\n",
+ " * 実務では次の複合インデックスが有効\n",
+ "\n",
+ " ```sql\n",
+ " CREATE INDEX idx_activity_date_user\n",
+ " ON Activity (activity_date, user_id);\n",
+ " ```\n",
+ " * これにより\n",
+ "\n",
+ " * 範囲条件 `activity_date BETWEEN ...` の絞り込み\n",
+ " * `user_id, activity_date` の `DISTINCT` / `GROUP BY`\n",
+ " の両方が効率化される\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4) 計算量(概算)\n",
+ "\n",
+ "* Activity テーブルの行数を **N** としたとき\n",
+ "\n",
+ "1. **CTE 内部 (`DISTINCT` + 範囲フィルタ)**\n",
+ "\n",
+ " * 範囲フィルタ: おおむね **O(N)**(インデックスがあれば実質 O(N_range))\n",
+ " * `DISTINCT(user_id, activity_date)`:\n",
+ "\n",
+ " * ハッシュ集約 or ソートベースで **O(N_range)** ~ **O(N_range log N_range)**\n",
+ "\n",
+ "2. **外側の `GROUP BY`**\n",
+ "\n",
+ " * すでに `(user_id, activity_date)` はユニークなので、\n",
+ " * 日数を D とすると **O(D)** 〜 **O(D log D)** 程度(D は最大 30 日)\n",
+ "\n",
+ "⇒ 全体としては **O(N_range log N_range)** が支配的\n",
+ "(`COUNT(DISTINCT user_id)` を直接使う場合も同程度)\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 5) 図解(Mermaid 超保守版)\n",
+ "\n",
+ "```mermaid\n",
+ "flowchart TD\n",
+ " A[Activity テーブル] --> B[\"日付範囲でフィルタ 2019-06-28〜2019-07-27\"]\n",
+ " B --> C[\"日 + user_id で DISTINCT (uniq_activity CTE)\"]\n",
+ " C --> D[\"activity_date ごとに GROUP BY COUNT(*)\"]\n",
+ " D --> E[出力: day, active_users]\n",
+ "```\n",
+ "\n",
+ "このクエリをそのまま PostgreSQL 16.6+ 環境に流し込めば、問題の期待する結果形式で日別アクティブユーザー数を取得できます。\n",
+ "結論から言うと、\n",
+ "\n",
+ "* **351ms / 上位 67% なら十分速い部類**\n",
+ "* LeetCode 環境(インデックス作れない・データ量少なめ)では **劇的な改善はほぼ出ません**\n",
+ "* それでも「やれること」は 2〜3 個あります\n",
+ "\n",
+ "という感じです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 1. クエリのシンプル化(1 段階集約)\n",
+ "\n",
+ "前回お渡ししたのは\n",
+ "\n",
+ "```sql\n",
+ "WITH uniq_activity AS (\n",
+ " SELECT DISTINCT\n",
+ " user_id,\n",
+ " activity_date\n",
+ " FROM Activity\n",
+ " WHERE activity_date BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'\n",
+ ")\n",
+ "SELECT\n",
+ " activity_date AS day,\n",
+ " COUNT(*) AS active_users\n",
+ "FROM uniq_activity\n",
+ "GROUP BY activity_date\n",
+ "ORDER BY day;\n",
+ "```\n",
+ "\n",
+ "でしたが、**LeetCode のような小さめデータ**であれば、\n",
+ "CTE を使わずに **1 回の GROUP BY + COUNT(DISTINCT)** にまとめた方が\n",
+ "プランナーが素直に最適化してくれることも多いです。\n",
+ "\n",
+ "```sql\n",
+ "SELECT\n",
+ " activity_date AS day,\n",
+ " COUNT(DISTINCT user_id) AS active_users\n",
+ "FROM Activity\n",
+ "WHERE activity_date BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'\n",
+ "GROUP BY activity_date;\n",
+ "-- ORDER BY は仕様上不要なら外してよい\n",
+ "\n",
+ "Runtime 366 ms\n",
+ "Beats 48.24%\n",
+ "\n",
+ "```\n",
+ "\n",
+ "### なぜ速くなる可能性があるか\n",
+ "\n",
+ "* CTE + DISTINCT + GROUP BY という **2 段階の集約** → 中間結果のマテリアライズが起きることがある\n",
+ "* 一方、`COUNT(DISTINCT user_id)` なら **1 回の GROUP BY** で完結\n",
+ "* 特に LeetCode ではオプティマイザ設定もそこまで凝ってないので、\n",
+ " **素直な単一クエリの方が有利なことが多い**です\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 2. ORDER BY を外す(仕様 OK なら)\n",
+ "\n",
+ "問題文が「順序は任意」と明言しているので、\n",
+ "**本当に順序不要**であれば `ORDER BY` を消すとソート処理がなくなり、その分は確実に速くなります。\n",
+ "\n",
+ "```sql\n",
+ "SELECT\n",
+ " activity_date AS day,\n",
+ " COUNT(DISTINCT user_id) AS active_users\n",
+ "FROM Activity\n",
+ "WHERE activity_date BETWEEN DATE '2019-06-28' AND DATE '2019-07-27'\n",
+ "GROUP BY activity_date;\n",
+ "```\n",
+ "\n",
+ "ソートは `O(D log D)`(D は日数)なので、\n",
+ "この問題では日数が最大 30 日と小さく、**体感としてはあまり変わりませんが**、\n",
+ "純粋なランタイムだけを見れば少し良くなる可能性があります。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 3. 実務ならインデックスを貼る\n",
+ "\n",
+ "LeetCode では出来ませんが、実務 PostgreSQL ならこうします:\n",
+ "\n",
+ "```sql\n",
+ "CREATE INDEX idx_activity_date_user\n",
+ " ON Activity (activity_date, user_id);\n",
+ "```\n",
+ "\n",
+ "これで\n",
+ "\n",
+ "* `activity_date BETWEEN ...` の範囲スキャン\n",
+ "* `COUNT(DISTINCT user_id)` の内部での group/hash\n",
+ "\n",
+ "が両方とも効率化されます。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "## 4. パフォーマンス評価の目安\n",
+ "\n",
+ "* 上位 **67%** は「解として十分」「実務でも困らないレベル」です\n",
+ "* もし上位 90% を狙うなら\n",
+ "\n",
+ " * 上記のように **1 段階集約 + ORDER BY 削除** を試す\n",
+ " * それでも改善が小さい/変わらないことも多い(データ量が小さいため)\n",
+ "\n",
+ "なので、**アルゴリズム/クエリ設計としては合格点以上**で、\n",
+ "あとは「CTE を減らして素直な集計に寄せる」くらいの微調整フェーズ、というイメージです。\n",
+ "\n",
+ "---\n",
+ "\n",
+ "もし「実務で数千万〜数億行ある場合はどうチューニングする?」みたいな視点も気になるようなら、\n",
+ "その前提でインデックス設計や EXPLAIN の読み方まで掘っていきましょう 💪\n"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "3.12.4",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.12.4"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}