Skip to content

Commit c4514d5

Browse files
authored
Merge pull request #261 from myoshi2891/dev-from-macmini
Add JavaScript/2626. Array Reduce Transformation
2 parents 0081da0 + 679aea3 commit c4514d5

3 files changed

Lines changed: 2209 additions & 0 deletions

File tree

Lines changed: 395 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,395 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "markdown",
5+
"id": "c1a3e2d9",
6+
"metadata": {},
7+
"source": [
8+
"# TypeScript Reduce関数の実装\n",
9+
"\n",
10+
"## 1. 問題の分析\n",
11+
"\n",
12+
"### 競技プログラミング視点での分析\n",
13+
"- **実行速度**: 配列を1回走査するだけの単純な反復処理(O(n))\n",
14+
"- **メモリ効率**: 追加のデータ構造が不要、累積値のみを保持(O(1))\n",
15+
"- **最適化ポイント**: ループ方式の選択(for vs forEach vs for-of)\n",
16+
"\n",
17+
"### 業務開発視点での分析\n",
18+
"- **型安全性**: 関数型引数とジェネリクスによる型保証が重要\n",
19+
"- **可読性**: reduceの動作を明確に示す実装\n",
20+
"- **エラーハンドリング**: 空配列の処理、null/undefined安全性\n",
21+
"- **保守性**: シンプルで理解しやすいコード\n",
22+
"\n",
23+
"### TypeScript特有の考慮点\n",
24+
"- **型推論**: `Fn`型の厳密な定義により、コンパイル時に型エラーを検出\n",
25+
"- **イミュータビリティ**: 元の配列を変更しない純粋関数\n",
26+
"- **null安全性**: strict modeでの安全な実装\n",
27+
"\n",
28+
"## 2. アルゴリズムアプローチ比較\n",
29+
"\n",
30+
"| アプローチ | 時間計算量 | 空間計算量 | TS実装コスト | 型安全性 | 可読性 | 備考 |\n",
31+
"|---------|----------|---------|------------|---------|-------|------|\n",
32+
"| forループ | O(n) | O(1) | 低 | 高 | 高 | 最も基本的で高速 |\n",
33+
"| for-ofループ | O(n) | O(1) | 低 | 高 | 高 | モダンな構文、若干遅い |\n",
34+
"| forEachメソッド | O(n) | O(1) | 低 | 高 | 中 | 関数呼び出しオーバーヘッド |\n",
35+
"| 再帰 | O(n) | O(n) | 中 | 高 | 中 | スタックオーバーフローリスク |\n",
36+
"\n",
37+
"## 3. 選択したアルゴリズムと理由\n",
38+
"\n",
39+
"### 選択したアプローチ: **forループ**\n",
40+
"\n",
41+
"### 理由\n",
42+
"1. **計算量的な優位性**\n",
43+
" - 時間: O(n) - 配列を1回走査\n",
44+
" - 空間: O(1) - 累積値のみを保持\n",
45+
" - インデックスベースのアクセスで最速\n",
46+
"\n",
47+
"2. **TypeScript環境での型安全性**\n",
48+
" - コンパイル時に型チェックが完全に機能\n",
49+
" - 累積値の型が明確に推論される\n",
50+
" - 副作用なしの純粋関数として実装可能\n",
51+
"\n",
52+
"3. **保守性・可読性の観点**\n",
53+
" - reduce操作の意図が明確\n",
54+
" - デバッグが容易\n",
55+
" - エッジケース(空配列)の処理が明示的\n",
56+
"\n",
57+
"### TypeScript特有の最適化ポイント\n",
58+
"- **型アノテーション**: 累積値の型が自動推論されるため、型注釈が最小限\n",
59+
"- **const使用**: イミュータビリティの確保\n",
60+
"- **早期リターン**: 空配列チェックによる不要な処理の回避\n",
61+
"\n",
62+
"## 4. 実装コード\n",
63+
"\n",
64+
"```typescript\n",
65+
"// Analyze Complexity\n",
66+
"// Runtime 51 ms\n",
67+
"// Beats 31.36%\n",
68+
"// Memory 56.13 MB\n",
69+
"// Beats 50.75%\n",
70+
"\n",
71+
"type Fn = (accum: number, curr: number) => number\n",
72+
"\n",
73+
"/**\n",
74+
" * 配列の各要素に対してreducer関数を順次適用し、単一の累積値を返す\n",
75+
" * \n",
76+
" * @param nums - 処理対象の数値配列\n",
77+
" * @param fn - 累積値と現在値を受け取り、新しい累積値を返すreducer関数\n",
78+
" * @param init - 初期累積値\n",
79+
" * @returns 全要素に対してfnを適用した最終的な累積値\n",
80+
" * @complexity Time: O(n), Space: O(1)\n",
81+
" * \n",
82+
" * @example\n",
83+
" * reduce([1,2,3,4], (acc, curr) => acc + curr, 0) // returns 10\n",
84+
" * reduce([], (acc, curr) => acc + curr, 25) // returns 25\n",
85+
" */\n",
86+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
87+
" // 早期リターン: 空配列の場合は初期値をそのまま返す\n",
88+
" if (nums.length === 0) {\n",
89+
" return init;\n",
90+
" }\n",
91+
" \n",
92+
" // 累積値を初期値で初期化\n",
93+
" let accumulator: number = init;\n",
94+
" \n",
95+
" // 配列の各要素に対してreducer関数を順次適用\n",
96+
" for (let i = 0; i < nums.length; i++) {\n",
97+
" accumulator = fn(accumulator, nums[i]);\n",
98+
" }\n",
99+
" \n",
100+
" // 最終的な累積値を返す\n",
101+
" return accumulator;\n",
102+
"}\n",
103+
"```\n",
104+
"\n",
105+
"### 代替実装(より関数型的なアプローチ)\n",
106+
"\n",
107+
"```typescript\n",
108+
"// Analyze Complexity\n",
109+
"// Runtime 43 ms\n",
110+
"// Beats 75.08%\n",
111+
"// Memory 58.72 MB\n",
112+
"// Beats 5.23%\n",
113+
"/**\n",
114+
" * for-ofループを使用した実装\n",
115+
" * より読みやすいが、わずかに遅い可能性がある\n",
116+
" */\n",
117+
"function reduceAlternative(nums: number[], fn: Fn, init: number): number {\n",
118+
" let accumulator: number = init;\n",
119+
" \n",
120+
" for (const num of nums) {\n",
121+
" accumulator = fn(accumulator, num);\n",
122+
" }\n",
123+
" \n",
124+
" return accumulator;\n",
125+
"}\n",
126+
"```\n",
127+
"\n",
128+
"## LeetCode提出用コード\n",
129+
"\n",
130+
"```typescript\n",
131+
"// Analyze Complexity\n",
132+
"// Runtime 44 ms\n",
133+
"// Beats 70.65%\n",
134+
"// Memory 56.58 MB\n",
135+
"// Beats 26.43%\n",
136+
"type Fn = (accum: number, curr: number) => number\n",
137+
"\n",
138+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
139+
" let accumulator = init;\n",
140+
" for (let i = 0; i < nums.length; i++) {\n",
141+
" accumulator = fn(accumulator, nums[i]);\n",
142+
" }\n",
143+
" return accumulator;\n",
144+
"}\n",
145+
"```\n",
146+
"\n",
147+
"## 実装の特徴\n",
148+
"\n",
149+
"### 型安全性\n",
150+
"- ✅ `Fn`型により、reducer関数のシグネチャが厳密に定義\n",
151+
"- ✅ `number`型の一貫性が保証される\n",
152+
"- ✅ コンパイル時に型エラーを検出\n",
153+
"\n",
154+
"### パフォーマンス\n",
155+
"- ✅ O(n)の時間計算量 - 最適\n",
156+
"- ✅ O(1)の空間計算量 - メモリ効率的\n",
157+
"- ✅ ループオーバーヘッドが最小\n",
158+
"\n",
159+
"### エッジケース処理\n",
160+
"- ✅ 空配列 → `init`を返す\n",
161+
"- ✅ 単一要素 → `fn(init, nums[0])`を返す\n",
162+
"- ✅ 制約条件(0 ≤ length ≤ 1000)を満たす\n",
163+
"\n",
164+
"この実装は、TypeScriptの型安全性を活かしながら、最高のパフォーマンスと可読性を実現しています。"
165+
]
166+
},
167+
{
168+
"cell_type": "markdown",
169+
"id": "2bcb244c",
170+
"metadata": {},
171+
"source": [
172+
"# パフォーマンス分析と改善提案\n",
173+
"\n",
174+
"## 結果の分析\n",
175+
"\n",
176+
"### 興味深い発見\n",
177+
"1. **空配列チェックのオーバーヘッド**: 早期リターンを追加したことで逆に遅くなっている(51ms vs 44ms)\n",
178+
"2. **for-ofの優位性**: 意外にもfor-ofループが最速(43ms)だが、メモリ使用量が多い\n",
179+
"3. **トレードオフ**: 速度とメモリのバランス\n",
180+
"\n",
181+
"### 問題点\n",
182+
"- 空配列チェックは制約条件(0 ≤ length ≤ 1000)では頻繁に発生しないため、分岐予測ミスのコストの方が高い\n",
183+
"- V8エンジンの最適化において、シンプルなコードの方が効率的\n",
184+
"\n",
185+
"## 改善版実装\n",
186+
"\n",
187+
"### 最適化版1: 最小限のコード(速度重視)\n",
188+
"\n",
189+
"```typescript\n",
190+
"// Analyze Complexity\n",
191+
"// Runtime 46 ms\n",
192+
"// Beats 59.80%\n",
193+
"// Memory 56.44 MB\n",
194+
"// Beats 30.85%\n",
195+
"type Fn = (accum: number, curr: number) => number\n",
196+
"\n",
197+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
198+
" let val = init;\n",
199+
" for (let i = 0; i < nums.length; i++) {\n",
200+
" val = fn(val, nums[i]);\n",
201+
" }\n",
202+
" return val;\n",
203+
"}\n",
204+
"```\n",
205+
"\n",
206+
"**改善ポイント**:\n",
207+
"- ✅ 空配列チェックを削除(オーバーヘッド削減)\n",
208+
"- ✅ 変数名を短縮(`accumulator` → `val`)でメモリアクセス最適化\n",
209+
"- ✅ 極限までシンプルに\n",
210+
"\n",
211+
"**予想パフォーマンス**: Runtime ~42-45ms, Memory ~55-56MB\n",
212+
"\n",
213+
"---\n",
214+
"\n",
215+
"### 最適化版2: キャッシング活用(速度最重視)\n",
216+
"\n",
217+
"```typescript\n",
218+
"// Analyze Complexity\n",
219+
"// Runtime 40 ms\n",
220+
"// Beats 86.03%\n",
221+
"// Memory 56.20 MB\n",
222+
"// Beats 43.12%\n",
223+
"type Fn = (accum: number, curr: number) => number\n",
224+
"\n",
225+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
226+
" let val = init;\n",
227+
" const len = nums.length;\n",
228+
" for (let i = 0; i < len; i++) {\n",
229+
" val = fn(val, nums[i]);\n",
230+
" }\n",
231+
" return val;\n",
232+
"}\n",
233+
"```\n",
234+
"\n",
235+
"**改善ポイント**:\n",
236+
"- ✅ `nums.length`をキャッシュして、ループ毎のプロパティアクセスを削減\n",
237+
"- ✅ V8の最適化を促進\n",
238+
"\n",
239+
"**予想パフォーマンス**: Runtime ~40-43ms, Memory ~55-56MB\n",
240+
"\n",
241+
"---\n",
242+
"\n",
243+
"### 最適化版3: アンローリング(極限の最適化)\n",
244+
"\n",
245+
"```typescript\n",
246+
"// Analyze Complexity\n",
247+
"// Runtime 47 ms\n",
248+
"// Beats 53.17%\n",
249+
"// Memory 56.15 MB\n",
250+
"// Beats 50.75%\n",
251+
"\n",
252+
"type Fn = (accum: number, curr: number) => number\n",
253+
"\n",
254+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
255+
" let val = init;\n",
256+
" let i = 0;\n",
257+
" const len = nums.length;\n",
258+
" \n",
259+
" // ループアンローリング: 4要素ずつ処理\n",
260+
" const unrollLimit = len - (len % 4);\n",
261+
" for (; i < unrollLimit; i += 4) {\n",
262+
" val = fn(val, nums[i]);\n",
263+
" val = fn(val, nums[i + 1]);\n",
264+
" val = fn(val, nums[i + 2]);\n",
265+
" val = fn(val, nums[i + 3]);\n",
266+
" }\n",
267+
" \n",
268+
" // 残りの要素を処理\n",
269+
" for (; i < len; i++) {\n",
270+
" val = fn(val, nums[i]);\n",
271+
" }\n",
272+
" \n",
273+
" return val;\n",
274+
"}\n",
275+
"```\n",
276+
"\n",
277+
"**改善ポイント**:\n",
278+
"- ✅ ループアンローリングでブランチ予測とパイプライン効率を向上\n",
279+
"- ✅ 4要素ずつ処理することで、ループオーバーヘッドを75%削減\n",
280+
"\n",
281+
"**予想パフォーマンス**: Runtime ~38-42ms(大きな配列で効果大)\n",
282+
"\n",
283+
"---\n",
284+
"\n",
285+
"## 推奨実装\n",
286+
"\n",
287+
"### 🏆 ベストバランス版(推奨)\n",
288+
"\n",
289+
"```typescript\n",
290+
"// Analyze Complexity\n",
291+
"// Runtime 42 ms\n",
292+
"// Beats 79.60%\n",
293+
"// Memory 56.62 MB\n",
294+
"// Beats 23.42%\n",
295+
"type Fn = (accum: number, curr: number) => number\n",
296+
"\n",
297+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
298+
" let val = init;\n",
299+
" const len = nums.length;\n",
300+
" for (let i = 0; i < len; i++) {\n",
301+
" val = fn(val, nums[i]);\n",
302+
" }\n",
303+
" return val;\n",
304+
"}\n",
305+
"```\n",
306+
"\n",
307+
"**選択理由**:\n",
308+
"1. **速度**: lengthキャッシングで ~5-10%高速化\n",
309+
"2. **メモリ**: 最小限の変数使用\n",
310+
"3. **可読性**: 十分にシンプルで理解しやすい\n",
311+
"4. **保守性**: 過度な最適化による複雑性なし\n",
312+
"\n",
313+
"**期待結果**: Runtime 40-43ms (80%+), Memory 55-56MB (60%+)\n",
314+
"\n",
315+
"---\n",
316+
"\n",
317+
"## さらなる最適化のヒント\n",
318+
"\n",
319+
"### V8エンジン最適化のベストプラクティス\n",
320+
"\n",
321+
"```typescript\n",
322+
"type Fn = (accum: number, curr: number) => number\n",
323+
"\n",
324+
"function reduce(nums: number[], fn: Fn, init: number): number {\n",
325+
" // 型の一貫性を保つ(V8の隠しクラス最適化)\n",
326+
" let val: number = init;\n",
327+
" \n",
328+
" // プロパティアクセスを削減\n",
329+
" const length: number = nums.length;\n",
330+
" \n",
331+
" // モノモーフィックなループ(常に同じ型)\n",
332+
" for (let i: number = 0; i < length; i++) {\n",
333+
" val = fn(val, nums[i]);\n",
334+
" }\n",
335+
" \n",
336+
" return val;\n",
337+
"}\n",
338+
"```\n",
339+
"\n",
340+
"### TypeScriptコンパイラ最適化\n",
341+
"\n",
342+
"tsconfig.jsonで以下を設定:\n",
343+
"```json\n",
344+
"{\n",
345+
" \"compilerOptions\": {\n",
346+
" \"target\": \"ES2022\",\n",
347+
" \"module\": \"ES2022\",\n",
348+
" \"strict\": true,\n",
349+
" \"noUncheckedIndexedAccess\": false,\n",
350+
" \"skipLibCheck\": true\n",
351+
" }\n",
352+
"}\n",
353+
"```\n",
354+
"\n",
355+
"---\n",
356+
"\n",
357+
"## パフォーマンス比較予測\n",
358+
"\n",
359+
"| 実装 | Runtime予測 | Memory予測 | 複雑度 | 推奨度 |\n",
360+
"|------|-------------|------------|--------|--------|\n",
361+
"| 現在のメイン実装 | 51ms (31%) | 56.13MB (51%) | 低 | ⭐⭐ |\n",
362+
"| 最適化版1(最小限) | 42-45ms (70-80%) | 55-56MB (60%) | 低 | ⭐⭐⭐⭐ |\n",
363+
"| 最適化版2(キャッシング) | 40-43ms (75-85%) | 55-56MB (60%) | 低 | ⭐⭐⭐⭐⭐ |\n",
364+
"| 最適化版3(アンローリング) | 38-42ms (80-90%) | 56-57MB (50%) | 高 | ⭐⭐⭐ |\n",
365+
"\n",
366+
"---\n",
367+
"\n",
368+
"## 結論\n",
369+
"\n",
370+
"**最も推奨する実装は「最適化版2(キャッシング活用)」です。**\n",
371+
"\n",
372+
"この実装により、以下の改善が期待できます:\n",
373+
"- ✅ Runtime: 51ms → 40-43ms(約20%改善、75-85%にランクアップ)\n",
374+
"- ✅ Memory: ほぼ変わらず(55-56MB、60%前後)\n",
375+
"- ✅ 可読性: 維持\n",
376+
"- ✅ 保守性: 維持"
377+
]
378+
}
379+
],
380+
"metadata": {
381+
"kernelspec": {
382+
"display_name": "TypeScript",
383+
"language": "typescript",
384+
"name": "typescript"
385+
},
386+
"language_info": {
387+
"file_extension": ".ts",
388+
"mimetype": "text/typescript",
389+
"name": "typescript",
390+
"version": "5.3.3"
391+
}
392+
},
393+
"nbformat": 4,
394+
"nbformat_minor": 5
395+
}

0 commit comments

Comments
 (0)