All Euler problems
Project Euler

Perfection Quotients

For a positive integer n, let sigma(n) denote the sum of all positive divisors of n. The perfection quotient of n is defined as p(n) = sigma(n)/n. A positive integer n is called a half-integer perf...

Source sync Apr 19, 2026
Problem #0241
Level Level 14
Solved By 1,127
Languages C++, Python
Answer 482316491800641154
Length 380 words
number_theorysearchmodular_arithmetic

Problem Statement

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

For a positive integer \(n\), let \(\sigma (n)\) be the sum of all divisors of \(n\). For example, \(\sigma (6) = 1 + 2 + 3 + 6 = 12\).

A perfect number, as you probably know, is a number with \(\sigma (n) = 2n\).

Let us define the perfection quotient of a positive integer as \(p(n) = \dfrac {\sigma (n)}{n}\).

Find the sum of all positive integers \(n \le 10^{18}\) for which \(p(n)\) has the form \(k + \dfrac {1}{2}\), where \(k\) is an integer.

Problem 241: Perfection Quotients

Mathematical Development

Definition 1 (Abundancy index). For a positive integer nn, the abundancy index is σ(n)/n\sigma(n)/n. We say nn is half-integer perfect when σ(n)/nZ+12\sigma(n)/n \in \mathbb{Z} + \tfrac{1}{2}.

Theorem 1 (Characterisation). A positive integer n>1n > 1 is half-integer perfect if and only if the rational number 2σ(n)/n2\sigma(n)/n is an odd integer 3\ge 3.

Proof. Suppose σ(n)/n=k+12\sigma(n)/n = k + \tfrac{1}{2} for some non-negative integer kk. Then 2σ(n)/n=2k+12\sigma(n)/n = 2k + 1, which is an odd integer. Since σ(n)1+n>n\sigma(n) \ge 1 + n > n for n>1n > 1 (as nn has at least two divisors), we have σ(n)/n>1\sigma(n)/n > 1, hence 2k+132k + 1 \ge 3.

Conversely, if 2σ(n)/n=2k+12\sigma(n)/n = 2k + 1 for some integer k1k \ge 1, then σ(n)/n=k+12\sigma(n)/n = k + \tfrac{1}{2}. \blacksquare

Theorem 2 (Multiplicative decomposition). Write n=2amn = 2^a \cdot m with mm odd and a0a \ge 0. Since σ\sigma is multiplicative and gcd(2a,m)=1\gcd(2^a, m) = 1:

σ(n)=σ(2a)σ(m)=(2a+11)σ(m).\sigma(n) = \sigma(2^a)\,\sigma(m) = (2^{a+1} - 1)\,\sigma(m).

The half-integer perfect condition 2σ(n)/n{3,5,7,}2\sigma(n)/n \in \{3, 5, 7, \ldots\} becomes:

2(2a+11)σ(m)2am=(2a+11)σ(m)2a1m{3,5,7,}.\frac{2(2^{a+1} - 1)\,\sigma(m)}{2^a \cdot m} = \frac{(2^{a+1} - 1)\,\sigma(m)}{2^{a-1} \cdot m} \in \{3, 5, 7, \ldots\}.

Proof. The identity σ(2a)=2a+11\sigma(2^a) = 2^{a+1} - 1 follows from the geometric series j=0a2j\sum_{j=0}^{a} 2^j. Multiplicativity of σ\sigma for coprime arguments is standard. The algebraic manipulation is direct. \blacksquare

Lemma 1 (Divisibility constraint on σ(m)\sigma(m)). For a1a \ge 1, the expression (2a+11)σ(m)/(2a1m)(2^{a+1} - 1)\,\sigma(m) / (2^{a-1}\,m) can be an integer only if 2a1σ(m)2^{a-1} \mid \sigma(m).

Proof. Observe that 2a+112^{a+1} - 1 is odd and mm is odd, so gcd(2a1,(2a+11)m)=1\gcd(2^{a-1},\, (2^{a+1} - 1) \cdot m) = 1. For the fraction to be integral, the denominator 2a1m2^{a-1} \cdot m must divide the numerator (2a+11)σ(m)(2^{a+1} - 1)\,\sigma(m). Since gcd(2a1,(2a+11)m)=1\gcd(2^{a-1}, (2^{a+1}-1)m) = 1, the factor 2a12^{a-1} must divide σ(m)\sigma(m). \blacksquare

Lemma 2 (Abundancy upper bound). For n=p1e1pkekn = p_1^{e_1} \cdots p_k^{e_k},

σ(n)n=i=1kσ(piei)piei=i=1kj=0eipij<i=1kpipi1.\frac{\sigma(n)}{n} = \prod_{i=1}^{k} \frac{\sigma(p_i^{e_i})}{p_i^{e_i}} = \prod_{i=1}^{k} \sum_{j=0}^{e_i} p_i^{-j} < \prod_{i=1}^{k} \frac{p_i}{p_i - 1}.

Proof. Each factor σ(pe)/pe=1+p1++pe\sigma(p^e)/p^e = 1 + p^{-1} + \cdots + p^{-e} is a partial sum of the geometric series j=0pj=p/(p1)\sum_{j=0}^{\infty} p^{-j} = p/(p-1), hence strictly less than p/(p1)p/(p-1). \blacksquare

Corollary 1 (Pruning criterion). During a depth-first search that constructs nn by choosing odd prime factors in increasing order, if the product pm,pp0p/(p1)\prod_{p \mid m,\, p \ge p_0} p/(p-1) (taken over all primes p0\ge p_0 dividing mm and all larger potential primes) is insufficient to bring 2σ(n)/n2\sigma(n)/n to any odd integer 3\ge 3, the branch can be pruned.

Editorial

The search also tries “denominator-targeted” primes: for each divisor dd of the current denominator, the values d±1d \pm 1 and 2d±12d \pm 1 are tested for primality, since such primes are likely to cancel denominator factors. We build n by multiplying by prime powers p^e in increasing order of p. We then update fraction: new_num/new_den = (num * sig_pe) / (den * pe). Finally, reduce by gcd.

Pseudocode

Begin search: n = 1, ratio 2*sigma(1)/1 = 2/1
Build n by multiplying by prime powers p^e in increasing order of p
num/den = 2*sigma(n)/n in lowest terms
Update fraction: new_num/new_den = (num * sig_pe) / (den * pe)
Reduce by gcd

Complexity Analysis

  • Time. The search tree explores factorisations of odd parts mm with 2am10182^a m \le 10^{18}. The abundancy bound (Lemma 2) and divisibility constraint (Lemma 1) provide strong pruning: empirically, O(106)O(10^6) nodes are visited across all branches.
  • Space. O(logN)O(\log N) for the recursion stack. The maximum depth equals the number of distinct prime factors of nn, which is at most log3(1018)=38\lfloor \log_3(10^{18}) \rfloor = 38.

Answer

482316491800641154\boxed{482316491800641154}

Code

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

C++ project_euler/problem_241/solution.cpp
/*
 * Project Euler Problem 241: Perfection Quotients
 *
 * Find all n <= 10^18 with sigma(n)/n a half-integer, i.e., 2*sigma(n)/n
 * is an odd integer >= 3.  We build n by DFS over its prime factorisation,
 * tracking the ratio 2*sigma(n)/n as a reduced fraction num/den.
 */

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef __int128 lll;

static const ll LIMIT = 1000000000000000000LL; // 10^18

ll my_gcd(ll a, ll b) {
    while (b) { a %= b; swap(a, b); }
    return a;
}

vector<ll> results;
vector<int> small_primes;

bool is_prime_check(ll n) {
    if (n < 2) return false;
    if (n < 4) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    for (ll i = 5; i * i <= n; i += 6)
        if (n % i == 0 || n % (i + 2) == 0) return false;
    return true;
}

void search(ll n_val, ll num, ll den, ll min_prime) {
    if (den == 1 && num % 2 == 1 && num >= 3 && n_val > 1)
        results.push_back(n_val);

    if (num > 200LL * den) return;

    ll max_p = LIMIT / n_val;
    if (max_p < min_prime) return;

    auto try_prime = [&](ll p) {
        if (p < min_prime || p > max_p) return;
        ll pe = 1;
        ll sig_pe = 1;
        for (int e = 1; pe <= max_p / p; e++) {
            pe *= p;
            sig_pe += pe;
            ll new_n = n_val * pe;
            if (new_n > LIMIT) break;

            ll g1 = my_gcd(num, pe);
            ll g2 = my_gcd(sig_pe, den);
            ll a_num = num / g1, a_pe = pe / g1;
            ll a_sig = sig_pe / g2, a_den = den / g2;

            if (a_num > 0 && a_sig > (ll)4e18 / a_num) break;
            ll new_num = a_num * a_sig;
            if (a_den > 0 && a_pe > (ll)4e18 / a_den) break;
            ll new_den = a_den * a_pe;

            ll g3 = my_gcd(new_num, new_den);
            new_num /= g3;
            new_den /= g3;

            search(new_n, new_num, new_den, p + 1);
        }
    };

    for (int p : small_primes) {
        if (p > max_p) break;
        if (p < min_prime) continue;
        try_prime(p);
    }

    if (den > 1) {
        set<ll> candidates;
        for (ll d = 1; d * d <= den; d++) {
            if (den % d == 0) {
                for (ll c : {d - 1, d + 1, 2*d - 1, 2*d + 1,
                             den/d - 1, den/d + 1, 2*(den/d)-1, 2*(den/d)+1}) {
                    if (c >= min_prime && c <= max_p && c > 2000000)
                        candidates.insert(c);
                }
            }
        }
        for (ll c : candidates)
            if (is_prime_check(c))
                try_prime(c);
    }
}

int main() {
    ios_base::sync_with_stdio(false);

    vector<bool> sieve(2000001, true);
    for (int i = 2; i * i <= 2000000; i++)
        if (sieve[i])
            for (int j = i*i; j <= 2000000; j += i)
                sieve[j] = false;
    for (int i = 2; i <= 2000000; i++)
        if (sieve[i]) small_primes.push_back(i);

    search(1, 2, 1, 2);

    ll ans = 0;
    for (ll x : results) ans += x;
    cout << ans << endl;
    return 0;
}