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...
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 is a finite sequence such that for each , there exist indices with . The length of the chain is .
Definition 2. Let denote the length of a shortest addition chain for . By convention, .
Theorem 1 (Lower bound). For all , .
Proof. At each step , since (the largest element can at most double), we have by induction . In particular, , so . Since is an integer, .
Theorem 2 (Upper bound via binary method). For all , , where denotes the number of -bits (Hamming weight) of .
Proof. The binary method scans the binary representation (with ) from the most significant bit to the least. For each bit position from down to : perform a doubling (add the current value to itself). If , additionally add . This produces a valid addition chain with doublings and small additions (one per 1-bit excluding the leading bit).
Proposition 1 (Binary method is not always optimal). , but the binary method uses steps.
Proof. The chain has length 5 and is valid: , , , , . By exhaustive search (or by noting that and verifying that no chain of length 4 reaches 15), .
Theorem 3 (IDDFS optimality). Iterative deepening depth-first search (IDDFS) with depth limit starting at finds a shortest addition chain for any .
Proof. IDDFS is complete (it explores all chains of each length before incrementing ) 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 , and the chain is strictly increasing, bounding the depth by .
Lemma 1 (Pruning criterion). Let the current chain be with depth limit . If , no extension of this chain can reach , and the branch may be pruned.
Proof. Each of the remaining steps can at most double the current maximum. After doublings the maximum attainable value is . If this is strictly less than , the target is unreachable from this state.
Editorial
For a fixed target , the natural search space is the set of addition chains ending at . Since the objective is to minimize chain length, iterative deepening is a good fit: start from the information-theoretic lower bound and ask whether any chain of that length exists, then increase the depth only when necessary. The first successful depth is exactly .
The depth-first search extends a partial chain by adding the current largest element to an earlier element, which is enough because any valid next step in an increasing addition chain has that form. The doubling bound gives the essential pruning rule: if even repeated doubling of the current maximum cannot reach the target within the remaining depth, that branch is hopeless and can be discarded immediately.
Pseudocode
Set the total to zero.
For each target n from 1 through 200:
Start from the lower bound ceil(log2 n).
Repeatedly try a depth-limited search for an addition chain ending at n.
During the search, extend the chain by adding the current maximum to one of the earlier chain values.
Prune any branch whose current maximum, even after all remaining doublings, still cannot reach n.
When the first successful depth is found, add it to the total.
Return the total over all targets.
Complexity Analysis
- Time: The branching factor at depth is at most , and the maximum depth is for . Worst-case time per is , but the pruning criterion drastically reduces the effective search tree. Empirically, the total time for all is very modest.
- Space: for the DFS stack and current chain, where .
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""
Problem 122: Efficient Exponentiation
Find sum of shortest addition chain lengths m(n) for n = 1 to 200.
Uses iterative deepening DFS with doubling-based pruning.
"""
import math
def shortest_addition_chain(n):
if n == 1:
return 0
def dfs(chain, depth, max_depth):
cur = chain[-1]
if cur == n:
return True
if depth == max_depth:
return False
if cur << (max_depth - depth) < n:
return False
for i in range(depth, -1, -1):
nxt = cur + chain[i]
if nxt <= cur or nxt > n:
continue
chain.append(nxt)
if dfs(chain, depth + 1, max_depth):
return True
chain.pop()
return False
lb = max(1, math.ceil(math.log2(n)))
for max_depth in range(lb, 20):
if dfs([1], 0, max_depth):
return max_depth
def solve():
print(sum(shortest_addition_chain(n) for n in range(1, 201)))
solve()