Keito

© 2024 Keito

技術ブログとポートフォリオ

繰り返し二乗法

分割統治法による効率的なべき乗計算。O(n)からO(log n)への劇的な改善を実現する古典アルゴリズム

O(log n)
時間計算量
O(log n)
空間計算量
中級
難易度
分割統治
アルゴリズム分類

⚡ べき乗計算設定

計算式:
3^10
二進表現:
10 = 1010
効率性:
10回 → 4回 (約2倍高速)
🚀 分割統治法:指数を二進分解して効率化
-100から100の整数
0-30の整数

📚 推奨入力例

繰り返し二乗法を実行してください

左側の入力パネルからパラメータを設定し、「繰り返し二乗法実行」ボタンを押してください

💻 実装例(JavaScript)

// 繰り返し二乗法(基本版)
function fastPower(base, exponent) {
    if (exponent === 0) return 1;
    
    let result = 1;
    let currentBase = base;
    let currentExp = exponent;
    
    while (currentExp > 0) {
        // 指数が奇数なら結果に基数を乗算
        if (currentExp % 2 === 1) {
            result *= currentBase;
        }
        
        // 基数を二乗し、指数を半分に
        currentBase *= currentBase;
        currentExp = Math.floor(currentExp / 2);
    }
    
    return result;
}

// モジュラー繰り返し二乗法
function fastPowerMod(base, exponent, modulus) {
    if (exponent === 0) return 1;
    
    let result = 1;
    let currentBase = base % modulus;
    let currentExp = exponent;
    
    while (currentExp > 0) {
        if (currentExp % 2 === 1) {
            result = (result * currentBase) % modulus;
        }
        
        currentBase = (currentBase * currentBase) % modulus;
        currentExp = Math.floor(currentExp / 2);
    }
    
    return result;
}

// 再帰版実装(教育的)
function fastPowerRecursive(base, exponent) {
    if (exponent === 0) return 1;
    if (exponent === 1) return base;
    
    if (exponent % 2 === 0) {
        // 偶数の場合: a^n = (a^(n/2))^2
        const half = fastPowerRecursive(base, exponent / 2);
        return half * half;
    } else {
        // 奇数の場合: a^n = a * a^(n-1)
        return base * fastPowerRecursive(base, exponent - 1);
    }
}

// 使用例
console.log(fastPower(3, 10)); // 59049
console.log(fastPowerMod(3, 13, 7)); // 6
console.log(fastPowerRecursive(2, 16)); // 65536

// 効率性の比較
function compareEfficiency(base, exponent) {
    console.time('ナイーブ法');
    let naiveResult = 1;
    for (let i = 0; i < exponent; i++) {
        naiveResult *= base;
    }
    console.timeEnd('ナイーブ法');
    
    console.time('繰り返し二乗法');
    const fastResult = fastPower(base, exponent);
    console.timeEnd('繰り返し二乗法');
    
    console.log(`結果一致: ${naiveResult === fastResult}`);
    
    // 理論的な計算回数
    const naiveMultiplications = exponent;
    const fastMultiplications = Math.ceil(Math.log2(exponent + 1));
    const speedup = naiveMultiplications / fastMultiplications;
    
    console.log(`ナイーブ法: ${naiveMultiplications}回の乗算`);
    console.log(`繰り返し二乗法: ${fastMultiplications}回の反復`);
    console.log(`理論的高速化: 約${speedup.toFixed(1)}倍`);
}

compareEfficiency(3, 100);

// 二進表現を使った理解
function explainBinaryExponentiation(base, exponent) {
    console.log(`${base}^${exponent} の計算過程:`);
    console.log(`指数の二進表現: ${exponent} = ${exponent.toString(2)}₂`);
    
    let result = 1;
    let currentPower = base;
    let exp = exponent;
    let step = 0;
    
    while (exp > 0) {
        const bit = exp % 2;
        console.log(`ステップ${++step}: 二進ビット = ${bit}`);
        
        if (bit === 1) {
            result *= currentPower;
            console.log(`  → result *= ${currentPower} → result = ${result}`);
        }
        
        if (exp > 1) {
            currentPower *= currentPower;
            console.log(`  → 基数を二乗: ${Math.sqrt(currentPower)} → ${currentPower}`);
        }
        
        exp = Math.floor(exp / 2);
    }
    
    console.log(`最終結果: ${result}`);
    return result;
}

explainBinaryExponentiation(3, 13);

// 暗号学的応用例(RSA暗号の簡易版)
function simpleRSADemo() {
    // 小さな素数を使用(実際のRSAはもっと大きな数)
    const p = 61, q = 53;
    const n = p * q; // 3233
    const phi = (p - 1) * (q - 1); // 3120
    const e = 17; // 公開指数
    
    // 秘密指数dを計算(簡略化)
    const d = 2753; // modularInverse(e, phi)
    
    const message = 123;
    
    // 暗号化: c = m^e mod n
    const encrypted = fastPowerMod(message, e, n);
    console.log(`暗号化: ${message}^${e} mod ${n} = ${encrypted}`);
    
    // 復号化: m = c^d mod n
    const decrypted = fastPowerMod(encrypted, d, n);
    console.log(`復号化: ${encrypted}^${d} mod ${n} = ${decrypted}`);
    
    console.log(`復号成功: ${message === decrypted}`);
}

simpleRSADemo();

// 大きな数での効率性実証
function largePowerDemo() {
    const base = 2;
    const exponent = 1000;
    const modulus = 1000000007; // 大きな素数
    
    console.log(`大きな数での計算: ${base}^${exponent} mod ${modulus}`);
    
    console.time('モジュラー繰り返し二乗法');
    const result = fastPowerMod(base, exponent, modulus);
    console.timeEnd('モジュラー繰り返し二乗法');
    
    console.log(`結果: ${result}`);
    console.log(`反復回数: ${Math.ceil(Math.log2(exponent + 1))}`);
    console.log(`ナイーブ法なら${exponent}回の乗算が必要`);
}

largePowerDemo();

🎯 繰り返し二乗法の特徴

アルゴリズムの特性

  • • 分割統治法による効率的な計算
  • • 指数の二進表現を活用
  • • O(n)からO(log n)への劇的改善
  • • 再帰・反復両方の実装が可能

実用的応用

  • • RSA暗号のモジュラー冪乗計算
  • • 離散対数問題と楕円曲線暗号
  • • 大きな数の高速べき乗計算
  • • 数学的計算ライブラリの基盤

💡 学習ポイント: 繰り返し二乗法は分割統治法の美しい応用例として、効率的アルゴリズム設計の核心を学べる 優秀な教材であり、現代暗号学の理解への入り口でもあります。