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
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 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()