All Euler problems
Project Euler

Efficient Exponentiation

The most efficient way to compute x^n uses the minimum number of multiplications, where each multiplication computes x^(a_i + a_j) from previously computed powers x^(a_i) and x^(a_j). This minimum...

Source sync Apr 19, 2026
Problem #0122
Level Level 05
Solved By 9,020
Languages C++, Python
Answer 1582
Length 445 words
searchoptimizationbrute_force

Problem Statement

This archive keeps the full statement, math, and original media on the page.

The most naive way of computing \(n^{15}\) requires fourteen multiplications: \[n \times n \times \cdots \times n = n^{15}.\] But using a "binary" method you can compute it in six multiplications: \begin {align*} n \times n &= n^2\\ n^2 \times n^2 &= n^4\\ n^4 \times n^4 &= n^8\\ n^8 \times n^4 &= n^{12}\\ n^{12} \times n^2 &= n^{14}\\ n^{14} \times n &= n^{15} \end {align*}

However it is yet possible to compute it in only five multiplications: \begin {align*} n \times n &= n^2\\ n^2 \times n &= n^3\\ n^3 \times n^3 &= n^6\\ n^6 \times n^6 &= n^{12}\\ n^{12} \times n^3 &= n^{15} \end {align*}

We shall define \(m(k)\) to be the minimum number of multiplications to compute \(n^k\); for example \(m(15) = 5\).

Find \(\displaystyle \sum \limits _{k = 1}^{200} m(k)\).

Problem 122: Efficient Exponentiation

Mathematical Development

Definition 1 (Addition chain). An addition chain for a positive integer nn is a finite sequence 1=a0<a1<<ar=n1 = a_0 < a_1 < \cdots < a_r = n such that for each 1kr1 \le k \le r, there exist indices 0ij<k0 \le i \le j < k with ak=ai+aja_k = a_i + a_j. The length of the chain is rr.

Definition 2. Let m(n)m(n) denote the length of a shortest addition chain for nn. By convention, m(1)=0m(1) = 0.

Theorem 1 (Lower bound). For all n1n \ge 1, m(n)log2nm(n) \ge \lceil \log_2 n \rceil.

Proof. At each step kk, since ak=ai+aj2ak1a_k = a_i + a_j \le 2a_{k-1} (the largest element can at most double), we have by induction ak2ka0=2ka_k \le 2^k a_0 = 2^k. In particular, n=ar2rn = a_r \le 2^r, so rlog2nr \ge \log_2 n. Since rr is an integer, rlog2nr \ge \lceil \log_2 n \rceil. \square

Theorem 2 (Upper bound via binary method). For all n1n \ge 1, m(n)log2n+ν(n)1m(n) \le \lfloor \log_2 n \rfloor + \nu(n) - 1, where ν(n)\nu(n) denotes the number of 11-bits (Hamming weight) of nn.

Proof. The binary method scans the binary representation n=(bsbs1b1b0)2n = (b_s b_{s-1} \cdots b_1 b_0)_2 (with bs=1b_s = 1) from the most significant bit to the least. For each bit position ii from s1s-1 down to 00: perform a doubling (add the current value to itself). If bi=1b_i = 1, additionally add a0=1a_0 = 1. This produces a valid addition chain with log2n\lfloor \log_2 n \rfloor doublings and ν(n)1\nu(n) - 1 small additions (one per 1-bit excluding the leading bit). \square

Proposition 1 (Binary method is not always optimal). m(15)=5m(15) = 5, but the binary method uses log215+ν(15)1=3+41=6\lfloor \log_2 15 \rfloor + \nu(15) - 1 = 3 + 4 - 1 = 6 steps.

Proof. The chain 1,2,3,6,12,151, 2, 3, 6, 12, 15 has length 5 and is valid: 2=1+12 = 1+1, 3=1+23 = 1+2, 6=3+36 = 3+3, 12=6+612 = 6+6, 15=3+1215 = 3+12. By exhaustive search (or by noting that m(15)log215=4m(15) \ge \lceil \log_2 15 \rceil = 4 and verifying that no chain of length 4 reaches 15), m(15)=5m(15) = 5. \square

Theorem 3 (IDDFS optimality). Iterative deepening depth-first search (IDDFS) with depth limit starting at log2n\lceil \log_2 n \rceil finds a shortest addition chain for any nn.

Proof. IDDFS is complete (it explores all chains of each length rr before incrementing rr) and optimal for uniform-cost search (the first solution found is of minimum length). The search space is finite since all chain elements lie in {1,,n}\{1, \ldots, n\}, and the chain is strictly increasing, bounding the depth by n1n - 1. \square

Lemma 1 (Pruning criterion). Let the current chain be [a0,,ad][a_0, \ldots, a_d] with depth limit rr. If ad2rd<na_d \cdot 2^{r-d} < n, no extension of this chain can reach nn, and the branch may be pruned.

Proof. Each of the remaining rdr - d steps can at most double the current maximum. After rdr - d doublings the maximum attainable value is ad2rda_d \cdot 2^{r-d}. If this is strictly less than nn, the target is unreachable from this state. \square

Editorial

Uses iterative deepening DFS with doubling-based pruning. We perform a recursive search over the admissible choices, prune branches that violate the derived constraints, and keep only the candidates that satisfy the final condition.

Pseudocode

    m[1] = 0
    For n from 2 to N:
        For r from ceil(log2(n)) to infinity:
            If IDDFS(chain=[1], target=n, depth_limit=r) then
                m[n] = r
                break
    Return sum(m[1..N])

    d = len(chain) - 1
    a_d = chain[d]
    If d == depth_limit then
        Return (a_d == target)
    If a_d << (depth_limit - d) < target then
        Return false // pruning
    For i from d down to 0:
        next_val = a_d + chain[i]
        If next_val <= a_d or next_val > target then
            continue
        chain.append(next_val)
        If IDDFS(chain, target, depth_limit) then
            Return true
        chain.pop()
    Return false

Complexity Analysis

  • Time: The branching factor at depth dd is at most d+1d + 1, and the maximum depth is r11r \le 11 for n200n \le 200. Worst-case time per nn is O(rr)O(r^r), but the pruning criterion drastically reduces the effective search tree. Empirically, the total time for all n200n \le 200 is very modest.
  • Space: O(r)O(r) for the DFS stack and current chain, where r11r \le 11.

Answer

1582\boxed{1582}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_122/solution.cpp
#include <bits/stdc++.h>
using namespace std;

int target;
vector<int> chain;

bool dfs(int depth, int maxDepth) {
    int cur = chain.back();
    if (cur == target) return true;
    if (depth == maxDepth) return false;
    if ((long long)cur << (maxDepth - depth) < target) return false;

    for (int i = depth; i >= 0; i--) {
        int nxt = cur + chain[i];
        if (nxt <= cur || nxt > target) continue;
        chain.push_back(nxt);
        if (dfs(depth + 1, maxDepth)) return true;
        chain.pop_back();
    }
    return false;
}

int shortestChain(int n) {
    if (n == 1) return 0;
    chain.clear();
    chain.push_back(1);

    int lb = 0;
    while ((1 << lb) < n) lb++;

    for (int maxDepth = lb; ; maxDepth++)
        if (dfs(0, maxDepth)) return maxDepth;
}

int main() {
    int ans = 0;
    for (int n = 1; n <= 200; n++) {
        target = n;
        ans += shortestChain(n);
    }
    cout << ans << endl;
    return 0;
}