約数

約数の個数

自然数の約数(divisor)は、素因数分解されていると簡単に求まります。まず、8の約数は、23だから、2をいくつ選ぶかで決まります。すなわち、0〜3個のうちどれかを選ぶことになります。

1 = 20
2 = 21
4 = 22
8 = 23

ですから、8の約数の個数は4です。
次に24を考えましょう。24 = 23 • 31ですが、約数を列挙すると次のようになります。

1 = 20 • 30
2 = 21 • 30
4 = 22 • 30
8 = 23 • 30
3 = 20 • 31
6 = 21 • 31
12 = 22 • 31
24 = 23 • 31

24は素因数に2と3を持つので、約数は2と3を独立にいくつ選ぶかで決まります。2は4通り、3は2通り選べるので、8通り選べる、すなわち約数の個数は8となります。
一般に、

n = p1e1pmem

とすると、nの約数の個数は、

(e1 + 1) … (em + 1)

となります。


# factorizeは下参照
def num_divisors(n):
return reduce(lambda x, y: x * (y[1] + 1), factorize(n), 1)

print num_divisors(24)

約数の総和

まず、8の約数の総和は、

1 + 2 + 22 + 23 = 24 - 1

となります。
次に、24の約数の総和は、3で割り切れるかどうかで括って、

(1 + 2 + 22 + 23) + 3(1 + 2 + 22 + 23) = (24 - 1)(32 - 1) / 2

一般に、

n = p1e1pmem

とすると、nの約数の個数は、

(p1e1+1 - 1) / (p1 - 1) … (pmem+1 - 1) / (pm - 1)

となります。


def sum_divisors(n):
def f(x, y): return x * (y[0] ** (y[1] + 1) - 1) / (y[0] - 1)
return reduce(f, factorize(n), 1)

print sum_divisors(24)

約数を列挙する

これも同様です。24ならまず、3から1と3が出ます。8から1,2,4,8が出るので、そこに1と3をそれぞれ掛けます。


def calc_exp(n, p):
e = 0
while n % p == 0:
e += 1
n /= p
return e, n

def gen_prime_candidates():
yield 2
n = 3
while True:
yield n
n += 2

def factorize(n):
f = ()
for m in gen_prime_candidates():
if m * m > n:
break
e, n = calc_exp(n, m)
if e:
f = f + ( (m, e), )
if n != 1:
f = f + ( (n, 1), )
return f

def gen_divisors(f, k = 0):
if k == len(f):
yield 1
else:
p, e = f[k]
for d in gen_divisors(f, k + 1):
for e1 in range(e + 1):
yield p ** e1 * d

f = factorize(24)
for d in gen_divisors(f):
print d