小池有话说

完满数和亲和数

2014-01-25

考虑最基本的两种运算:加法和乘法。

用一堆数相乘,得出一个结果,再用这堆数相加,得到另一个结果。如果两者相同,此数为完满数

1x2x3=6
1+2+3=6

6就是完美数

乘法是加法的飞跃。我们来研究乘法。如下乘式,我们将等号左边的数叫做因,将结果叫做果。

1x2x3=6

给定一个数,我们就可以找出它的因数。所谓的因数就是有可能造成某个果的数。一个数其本身也是自身的因数,非自身的因数称之为真因数。

如 12 的因数是 1 2 3 4 6 。

找出一个数的因数:

def factor_list(n):
    return [x for x in range(1, n) if n % x == 0]

判断一个数是不是完满数:

def is_perfect_number(n):
    return sum(factor_list(x)) == x

找出1000以内的所有完满数:

[x for x in range(1, 1000) if is_perfect_number(x)]
# => [6, 28, 496]

完满数总是等于一系列自然数之和:

6=1+2+3
28=1+2+3+4+5+6+7
496=31x(31+1)/2=1+2+3+...+31

因数之和大于本身的数叫做盈数,因数之和小于本身的数叫做亏数。

2的冪都是特別有意思的數。它們的因數之和總是比它們小1 。

2 = 2^1 1=1
4 = 2^2 1+2=3
8 = 2^3 1+2+4=7
16 = 2^4 1+2+4+8=15

如果你知道等差數列如何求和的話,你就知道,這個規律自然是成立的。

等差數列求和公式的推導。

2倍冪的求和公式的直觀解釋。

因數之和比本身小1的數,稱之爲微虧。

因數之和比本身大1的數,稱之爲微贏。

不過,奇怪的是,愚蠢的人類至今尚未找到這種微贏的數。許多人認爲這種數並不存在。騷年,如果你能證明這一點的話,你就出名了。

上面的這些都是兩千五百年前畢達哥拉斯發現的。

後來有個人叫做歐幾里得,他發現了倍二和完滿數之間的一個關係。那就是:完滿數一定可以寫成一個倍二數和另一個比倍二數小一的數之積。

6 = 2x3 = 2x(2^2-1)
28 = 4x7 = 2^2x(2^3-1)
496 = 16x31 = 2^4x(2^5-1)

這樣,我們就有了一個更效率的尋找完滿數的方法。

for i in xrange(1,10):
    for j in xrange(1,10):
        x = pow(2, i) * (pow(2, j)-1)
        if is_perfect_number(x):
            print x
6
28
496
8128

好了,以上就是一百萬以內的所有完滿數。我的電腦效率低。人家超級電腦一定可以發現更多的完滿數。

如果一个数的因数之和是另一个数,而这个数的因数之和是原来的数,则称这两个数为亲(hao)和(ji)数(you)。

如220和284

220 = 1x2x4x5x10x11x20x22x44x55x110 = 1+2+4+71+142
284 = 1x2x4x71x142 = 1+2+4+5+10+11+20+22+44+55+110

而我们也可以写一个简单的程序寻找所有的亲和数

x = 0
while True:
    flx = factor_list(x)
    y = sum(flx)
    fly = factor_list(y)
    if x == sum(fly) and not x == y:
        print x, y
        if y > x:
            x = y
    x += 1

当然,这个程序有效率问题。我们可以用伴随数组来加快搜索的速度

n = 5000000
s = []
for x in xrange(1,n+10):
    s.append(1)
for x in xrange(2,n+1):
    for i in xrange(x+x,n,x):
        s[i] += x
for i in xrange(1, n+1):
    if s[i] < n and s[i] != 1 and s[i] > i and i == s[s[i]]:
        print i, s[i]

上述程序的算法来自:程序员编程艺术:第六章、求解500万以内的亲和数