🚀 ダブリング法による数字操作問題
詳細アルゴリズム解析
📋 問題概要
操作定義: 数値から各桁の数字の和を引く
目標: 1からNの各数値に対してK回操作後の値を求める
制約: N ≤ 300,000, K ≤ 10⁹
108
→
108-(1+0+8)=99
→
99-(9+9)=81
→
81-(8+1)=72
⚖️ アルゴリズム選択の比較
| アプローチ |
時間計算量 |
空間計算量 |
制約での実行可能性 |
| 単純な繰り返し |
O(N × K) |
O(1) |
❌ TLE (最大 3×10¹⁴ 操作) |
| サイクル検出 |
O(N × √max_value) |
O(√max_value) |
⚠️ 大きな値でTLE |
| ダブリング法 |
O(N × log K) |
O(N × log K) |
✅ 高速 (最大 9×10⁶ 操作) |
🔄 ダブリング法の原理
基本概念
2
二進分解
K を2の冪の和で表現
例: 13 = 8+4+1 = 2³+2²+2⁰
3
高速計算
事前計算した値を組み合わせて
O(log K)で結果を取得
ダブリングテーブル構築例
| 値\ビット |
| 10 |
9 |
0 |
0 |
0 |
| 20 |
18 |
9 |
0 |
0 |
| 108 |
99 |
81 |
45 |
36 |
| 123 |
117 |
102 |
99 |
81 |
🔧 実装の詳細解析
1. 基本操作関数
function getDigitSum(n: number): number { let sum = 0; while (n > 0) { sum += n
% 10; // 最下位桁を取得 n = Math.floor(n / 10); // 桁を右シフト } return sum; }
// 例: getDigitSum(123) → 1+2+3 = 6 // 計算量: O(log₁₀ n) ≈ 桁数に比例
2. ダブリングテーブル構築
構築過程:
1. table[0][i] = i から1回操作後の値を計算
2. table[bit][i] = table[bit-1][table[bit-1][i]]
3. 2^bit回 = 2^(bit-1)回 + 2^(bit-1)回 の性質を利用
// bit=1の構築例(2回操作) for (let i = 0; i <= maxIndex; i++) { table[1][i] =
table[0][table[0][i]]; // i → 1回操作 → 1回操作 = 2回操作 } //
bit=2の構築例(4回操作) for (let i = 0; i <= maxIndex; i++) { table[2][i] =
table[1][table[1][i]]; // i → 2回操作 → 2回操作 = 4回操作 }
3. クエリ処理(K回操作の計算)
K=13をビット分解
→
13 = 1101₂
→
8+4+1回操作
// K=13 (1101₂) の場合 function queryExample(start: number) { let current =
start; // ビット0が立っている → 1回操作 if (13 & 1) current = table[0][current];
// ビット2が立っている → 4回操作 if (13 & 4) current = table[2][current]; //
ビット3が立っている → 8回操作 if (13 & 8) current = table[3][current]; return
current; // 合計13回操作後の値 }
💾 メモリ使用量解析
サイズ: maxIndex × log₂K
例: 600,000 × 30 = 18M 要素
メモリ: 約72MB
サイズ: N 個の文字列
例: 300,000 個
メモリ: 約2-3MB
クエリ処理用: O(1)
その他: 最小限
メモリ: < 1MB
メモリ最適化技術:
• 適応的上限設定: Math.min(N×2, 1,000,000)
• 範囲外は直接計算: 事前計算範囲を制限
• 定期的GC: 大きなNでのメモリクリーンアップ
⚡ パフォーマンス比較
| 段階 |
処理内容 |
計算量 |
実行時間(推定) |
| 前処理 |
ダブリングテーブル構築 |
O(maxIndex × log K) |
~200ms |
| クエリ処理 |
N個の値を並列計算 |
O(N × log K) |
~500ms |
| 出力生成 |
文字列結合・出力 |
O(N) |
~100ms |
🎯 ハイブリッド最適化戦略
1
閾値判定
K ≤ 100: 直接計算
K > 100: ダブリング法
2
範囲チェック
事前計算範囲内: テーブル使用
範囲外: 直接計算フォールバック
3
メモリ管理
適応的上限設定
定期的ガベージコレクション
最適化の効果:
• 小さなK: オーバーヘッド削減で2-3倍高速化
• 大きなK: ダブリング法で指数的高速化
• メモリ効率: 制約内で最大パフォーマンスを実現
🎯 結論
ダブリング法の優位性:
• 時間効率: O(N log K) - 制約下で確実に実行可能
• 空間効率: 適応的制限でメモリ使用量を最適化
• 実装安定性: TypeScriptの型安全性で堅牢な実装
• 拡張性: より大きな制約にも対応可能な設計