All Euler problems
Project Euler

Near Power Sums

A positive integer n is a near power sum if there exists a positive integer k such that the sum of the k -th powers of the digits of n equals either n + 1 or n - 1. Define S(d) as the sum of all ne...

Source sync Apr 19, 2026
Problem #0749
Level Level 16
Solved By 883
Languages C++, Python
Answer 13459471903176422
Length 533 words
linear_algebramodular_arithmeticrecursion

Problem Statement

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

A positive integer, \(n\), is a <dfn>near power sum</dfn> if there exists a positive integer, \(k\), such that the sum of the \(k\)th powers of the digits in its decimal representation is equal to either \(n+1\) or \(n-1\). For example \(35\) is a near power sum number because \(3^2+5^2 = 34\).

Define \(S(d)\) to be the sum of all near power sum numbers of \(d\) digits or less. Then \(S(2) = 110\) and \(S(6) = 2562701\).

Find \(S(16)\).

Problem 749: Near Power Sums

Mathematical Foundation

Theorem 1 (Digit Multiset Invariance). For a fixed power kk, the digit power sum Pk(n)=i=09ciikP_k(n) = \sum_{i=0}^{9} c_i \cdot i^k depends only on the multiset of digits (c0,c1,,c9)(c_0, c_1, \ldots, c_9) of nn, not on their arrangement.

Proof. The digit power sum Pk(n)=jdjkP_k(n) = \sum_{j} d_j^k where djd_j are the digits of nn. Since addition is commutative, rearranging digits does not change the sum. Grouping by digit value: Pk(n)=i=09ciikP_k(n) = \sum_{i=0}^{9} c_i \cdot i^k where cic_i is the count of digit ii. \square

Theorem 2 (Search Space Reduction). Instead of checking all n10dn \le 10^d, it suffices to enumerate all digit multisets (c0,,c9)Z010(c_0, \ldots, c_9) \in \mathbb{Z}_{\ge 0}^{10} with 1cid1 \le \sum c_i \le d. For each multiset and each valid kk, compute P=Pk(c0,,c9)P = P_k(c_0, \ldots, c_9) and check whether P+1P + 1 or P1P - 1 has the same digit multiset.

Proof. If nn is a near power sum with digit multiset (c0,,c9)(c_0, \ldots, c_9) for some power kk, then either Pk=n+1P_k = n + 1 (so n=Pk1n = P_k - 1) or Pk=n1P_k = n - 1 (so n=Pk+1n = P_k + 1). In both cases, nn is determined by PkP_k and the ±1\pm 1 offset. The digit multiset of nn must match (c0,,c9)(c_0, \ldots, c_9). Since PkP_k depends only on the multiset, we can:

  1. Compute PkP_k from the multiset.
  2. Check if Pk+1P_k + 1 or Pk1P_k - 1 is a positive integer whose sorted digits match the multiset.

This replaces iteration over O(10d)O(10^d) numbers with iteration over O((d+99))O(\binom{d+9}{9}) multisets. \square

Lemma 1 (Bound on kk). For a number with exactly \ell digits (101n<1010^{\ell-1} \le n < 10^\ell), a necessary condition for Pk(n)nP_k(n) \approx n is:

10119kand9k110^{\ell - 1} - 1 \le \ell \cdot 9^k \quad \text{and} \quad 9^k \ge 1

The upper bound on kk is:

kln10ln9+O(1)1.048k \le \frac{\ell \cdot \ln 10}{\ln 9} + O(1) \approx 1.048 \ell

For =16\ell = 16: k17k \le 17 (approximately). The lower bound is k1k \ge 1.

Proof. The maximum digit power sum for an \ell-digit number is 9k\ell \cdot 9^k. For this to be approximately equal to nn (which is at least 10110^{\ell-1}), we need 9k1011\ell \cdot 9^k \ge 10^{\ell-1} - 1. Taking logarithms: k(1)ln10/ln9ln/ln9k \ge (\ell - 1) \ln 10 / \ln 9 - \ln \ell / \ln 9. The maximum kk satisfies 9k10+1\ell \cdot 9^k \le 10^{\ell} + 1, giving kln10/ln9+O(1)k \le \ell \ln 10 / \ln 9 + O(1). For 16\ell \le 16, this gives k17k \le 17 or 1818. \square

Lemma 2 (Multiset Counting). The number of digit multisets with total count d\le d is:

=1d(+99)=(d+1010)1\sum_{\ell=1}^{d} \binom{\ell + 9}{9} = \binom{d + 10}{10} - 1

For d=16d = 16: (2610)1=5,311,734\binom{26}{10} - 1 = 5{,}311{,}734.

Proof. Stars and bars: the number of non-negative integer solutions to c0+c1++c9=c_0 + c_1 + \cdots + c_9 = \ell is (+99)\binom{\ell + 9}{9}. Summing over =1,,d\ell = 1, \ldots, d gives (d+1010)1\binom{d+10}{10} - 1 by the hockey-stick identity. \square

Theorem 3 (Correctness of the Algorithm). The algorithm correctly finds all near power sums: for every near power sum nn with at most dd digits, there exists a digit multiset and power kk in the search space such that either Pk1=nP_k - 1 = n or Pk+1=nP_k + 1 = n, and the digit multiset of nn matches. Conversely, every match found by the algorithm corresponds to a valid near power sum.

Proof. (\Rightarrow) If nn is a near power sum, then k\exists k with Pk(n)=n±1P_k(n) = n \pm 1. The digit multiset of nn is enumerated (since nn has d\le d digits), and PkP_k is computed for this multiset. Then Pk±1=nP_k \pm 1 = n, and the digit multiset check succeeds.

(\Leftarrow) If the algorithm finds a multiset with Pk±1P_k \pm 1 having the same digit multiset, then n=Pk1n = P_k \mp 1 is a number whose digit power sum is Pk=n±1P_k = n \pm 1, confirming nn is a near power sum.

Uniqueness is handled by collecting results in a set. \square

Editorial

Project Euler 749: Near Power Sums A positive integer n is a near power sum if there exists k such that sum of k-th powers of digits of n equals n+1 or n-1. S(d) = sum of all near power sum numbers with at most d digits. Find S(16). We check P - 1. Finally, check P + 1.

Pseudocode

Check P - 1
Check P + 1

Complexity Analysis

  • Time: O ⁣(kmax(d+1010))O\!\left(k_{\max} \cdot \binom{d+10}{10}\right). For d=16d = 16, kmax18k_{\max} \approx 18, giving roughly 18×5.3×10610818 \times 5.3 \times 10^6 \approx 10^8 operations. Each operation involves computing a digit power sum (O(10)O(10)) and extracting/comparing digits (O(d)O(d)). Total: O(108d)O(10^8 \cdot d).
  • Space: O ⁣((d+1010))O\!\left(\binom{d+10}{10}\right) for the multiset enumeration (can be done recursively with O(d)O(d) stack space), plus O(results)O(|\text{results}|) for the result set.

Answer

13459471903176422\boxed{13459471903176422}

Code

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

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

typedef long long ll;
typedef unsigned long long ull;
typedef __int128 lll;

// Get digit counts of a number
vector<int> digit_counts(ll n) {
    vector<int> cnt(10, 0);
    if (n == 0) { cnt[0] = 1; return cnt; }
    while (n > 0) {
        cnt[n % 10]++;
        n /= 10;
    }
    return cnt;
}

int num_digits(ll n) {
    if (n == 0) return 1;
    int d = 0;
    while (n > 0) { d++; n /= 10; }
    return d;
}

int main() {
    const int D = 16;
    set<ll> near_power_sums;

    // Precompute powers: i^k for i=0..9, k=1..20
    // Use __int128 to avoid overflow during computation
    // But final values must fit in range [1, 10^16]

    for (int k = 1; k <= 20; k++) {
        // Precompute i^k
        lll pw[10];
        pw[0] = 0;
        bool too_big = false;
        for (int i = 1; i <= 9; i++) {
            pw[i] = 1;
            for (int j = 0; j < k; j++) {
                pw[i] *= i;
                if (pw[i] > (lll)1e18) { too_big = true; break; }
            }
            if (too_big) break;
        }
        if (too_big) break;

        // Check if D * 9^k is reasonable
        if (pw[9] > (lll)1e17 / D) {
            // Even a single 9 might make sum too large
            // But we can still check - the sum just needs to be <= 10^16
        }

        // Enumerate digit multisets using recursion
        // c[0..9] with sum(c) in [1, D]
        // Compute power_sum = sum(c[i] * pw[i])

        vector<int> c(10, 0);

        function<void(int, int, lll)> enumerate = [&](int digit, int remaining, lll power_sum) {
            if (digit == 10) {
                if (remaining == D) return; // no digits used
                // Check power_sum +/- 1
                for (int delta : {-1, 1}) {
                    lll candidate = power_sum + delta;  // This is n = power_sum +/- 1
                    // But we need: sum of k-th powers of digits of n = n -/+ 1 = power_sum
                    // Actually: n is near power sum if digit_power_sum(n) = n +/- 1
                    // So digit_power_sum(n) = power_sum, and n = power_sum -/+ 1? No.
                    // Let me re-read: n is near power sum if sum of k-th powers of digits of n = n+1 or n-1
                    // So: power_sum = n+1 or power_sum = n-1
                    // Thus: n = power_sum - 1 or n = power_sum + 1
                    // But power_sum is computed from c[], which should be the digit counts of n

                    ll n_val;
                    if (delta == -1) {
                        // n = power_sum - 1 (case: digit_power_sum = n + 1)
                        if (power_sum < 1) continue;
                        if (power_sum - 1 > (lll)1e16) continue;
                        n_val = (ll)(power_sum - 1);
                    } else {
                        // n = power_sum + 1 (case: digit_power_sum = n - 1)
                        if (power_sum + 1 > (lll)1e16) continue;
                        if (power_sum + 1 < 1) continue;
                        n_val = (ll)(power_sum + 1);
                    }

                    if (n_val < 1) continue;
                    int nd = num_digits(n_val);
                    int total_digits = D - remaining;
                    if (nd != total_digits) continue;

                    // Check digit counts match
                    vector<int> dc = digit_counts(n_val);
                    bool match = true;
                    for (int i = 0; i < 10; i++) {
                        if (dc[i] != c[i]) { match = false; break; }
                    }
                    if (match) {
                        near_power_sums.insert(n_val);
                    }
                }
                return;
            }

            int max_c = remaining;
            for (int cnt = 0; cnt <= max_c; cnt++) {
                c[digit] = cnt;
                lll new_sum = power_sum + (lll)cnt * pw[digit];
                if (digit > 0 && new_sum > (lll)1e16 + 1) {
                    c[digit] = 0;
                    break;
                }
                enumerate(digit + 1, remaining - cnt, new_sum);
                c[digit] = 0;
            }
        };

        enumerate(0, D, 0);
    }

    ll answer = 0;
    for (ll x : near_power_sums) {
        answer += x;
    }

    cout << answer << endl;

    return 0;
}