組み合せで表現可能な数字の和

30分プログラム、その293。昨日の続きで、『組み合わせで表現可能な数の和』を計算するコード。ハッシュを使ったりして高速化をはかるも挫折。
明日は方針を変えて、Xが過剰数のときN-Xが過剰数であるかをチェックする方法でやってみよう。

使い方

>>> combination(xrange(1,100))
19700

ソースコード

#! /usr/bin/python
# -*- mode:python; coding:utf-8 -*-
#
# problem23.py -
#
# Copyright(C) 2008 by mzp
# Author: MIZUNO Hiroki / mzpppp at gmail dot com
# http://howdyworld.org
#
# Timestamp: 2008/04/28 22:10:13
#
# This program is free software; you can redistribute it and/or
# modify it under MIT Lincence.
#

## 素数リストを作る
def sieve(max):
    (n,xs) = (2,[])
    while n < max:
        if all(map(lambda x: n % x != 0,xs)):
            xs.append(n)
        n += 1
    return xs

## 素因数分解を利用した約数の計算
def factor(n,primes):
    def f(x,y,n):
        if x % y == 0:
            return f(x/y,y,n+1)
        else:
            return n
    xs = []
    for prime in primes:
        if n < prime:
            return xs
        elif n % prime == 0:
            xs.append((prime,f(n,prime,0)))
    return xs

def product(xs):
    return reduce(lambda x,y: x*y,xs,1)

def divisor_sum(n,primes):
    xs = factor(n,primes)
    return product(map(lambda (p,a):(p**(a+1)-1)/(p-1),xs))

## 過剰数の計算
def is_abundant(n,primes):
    return divisor_sum(n,primes) > n

def combination(xs):
    dic = {}
    sum = 0
    for x in xs:
        for y in xs:
            if x+y > 28123:
                break
            elif dic.get(x+y) == None:
                sum += x+y
                dic[x+y] = True
    return sum


def main():
    primes = sieve(28123)
    xs = xrange(1,28123)
    y = sum(filter(lambda x:is_abudant(x,primes),xs))
    return sum(xs) - y