Task: Practical numbers


C++: #include <algorithm>
#include <iostream>
#include <numeric>
#include <sstream>
#include <vector>


template <typename iterator>
bool sum_of_any_subset(int n, iterator begin, iterator end) {
    if (begin == end)
        return false;
    if (std::find(begin, end, n) != end)
        return true;
    int total = std::accumulate(begin, end, 0);
    if (n == total)
        return true;
    if (n > total)
        return false;
    --end;
    int d = n - *end;
    return (d > 0 && sum_of_any_subset(d, begin, end)) ||
           sum_of_any_subset(n, begin, end);
}


std::vector<int> factors(int n) {
    std::vector<int> f{1};
    for (int i = 2; i * i <= n; ++i) {
        if (n % i == 0) {
            f.push_back(i);
            if (i * i != n)
                f.push_back(n / i);
        }
    }
    std::sort(f.begin(), f.end());
    return f;
}

bool is_practical(int n) {
    std::vector<int> f = factors(n);
    for (int i = 1; i < n; ++i) {
        if (!sum_of_any_subset(i, f.begin(), f.end()))
            return false;
    }
    return true;
}

std::string shorten(const std::vector<int>& v, size_t n) {
    std::ostringstream out;
    size_t size = v.size(), i = 0;
    if (n > 0 && size > 0)
        out << v[i++];
    for (; i < n && i < size; ++i)
        out << ", " << v[i];
    if (size > i + n) {
        out << ", ...";
        i = size - n;
    }
    for (; i < size; ++i)
        out << ", " << v[i];
    return out.str();
}

int main() {
    std::vector<int> practical;
    for (int n = 1; n <= 333; ++n) {
        if (is_practical(n))
            practical.push_back(n);
    }
    std::cout << "Found " << practical.size() << " practical numbers:\n"
              << shorten(practical, 10) << '\n';
    for (int n : {666, 6666, 66666, 672, 720, 222222})
        std::cout << n << " is " << (is_practical(n) ? "" : "not ")
                  << "a practical number.\n";
    return 0;
}






Python: from itertools import chain, cycle, accumulate, combinations
from typing import List, Tuple



def factors5(n: int) -> List[int]:
    
    def prime_powers(n):
        
        for c in accumulate(chain([2, 1, 2], cycle([2,4]))):
            if c*c > n: break
            if n%c: continue
            d,p = (), c
            while not n%c:
                n,p,d = n//c, p*c, d + (p,)
            yield(d)
        if n > 1: yield((n,))

    r = [1]
    for e in prime_powers(n):
        r += [a*b for a in r for b in e]
    return r[:-1]



def powerset(s: List[int]) -> List[Tuple[int, ...]]:
    
    return chain.from_iterable(combinations(s, r) for r in range(1, len(s)+1))



def is_practical(x: int) -> bool:
    
    if x == 1:
        return True
    if x %2:
        return False  
    f = factors5(x)
    ps = powerset(f)
    found = {y for y in {sum(i) for i in ps}
             if 1 <= y < x}
    return len(found) == x - 1


if __name__ == '__main__':
    n = 333
    p = [x for x in range(1, n + 1) if is_practical(x)]
    print(f"There are {len(p)} Practical numbers from 1 to {n}:")
    print(' ', str(p[:10])[1:-1], '...', str(p[-10:])[1:-1])
    x = 666
    print(f"\nSTRETCH GOAL: {x} is {'not ' if not is_practical(x) else ''}Practical.")