在python中找到友好数字的最有效方法是什么?

时间:2016-06-29 08:58:53

标签: python-2.7 numbers

我用Python编写代码来计算10000以下友好数字的总和:

def amicable(a, b):
   total = 0
   result = 0
   for i in range(1, a):
      if a % i == 0:
        total += i
   for j in range(1, b):
      if b % j == 0:
        result += j
   if total == b and result == a:
        return True
   return False

sum_of_amicables = 0
for m in range (1, 10001):
    for n in range (1, 10001):
        if amicable(m, n) == True and m != n:
            sum_of_amicables = sum_of_amicables + m + n

Python 2.7.11中的代码运行时间超过20分钟。好吗?我该如何改进呢?

6 个答案:

答案 0 :(得分:3)

优化为O(n)

def sum_factors(n):  
     result = []
     for i in xrange(1, int(n**0.5) + 1):
         if n % i == 0:
             result.extend([i, n//i])
     return sum(set(result)-set([n]))

def amicable_pair(number):
    result = []
    for x in xrange(1,number+1):
        y = sum_factors(x)
        if sum_factors(y) == x and x != y:
            result.append(tuple(sorted((x,y))))
    return set(result)

运行它

start = time.time()
print (amicable_pair(10000))
print time.time()-start

结果

set([(2620, 2924), (220, 284), (6232, 6368), (1184, 1210), (5020, 5564)])
0.180204153061

在macbook pro上只需0.2秒

答案 1 :(得分:2)

让我们分解代码并改进花费大量时间的代码部分。

<强> 1 -

如果您将if amicable(m, n) == True and m != n:替换为if m != n and amicable(m, n) == True:,它将为您保存10000次调用amicable方法(最昂贵​​的方法)m != nfalse

2 - amicable方法中,您将循环1到n以查找这两个数字的所有因子。您需要一个更好的算法来查找因子。您可以使用提到的here。为了找到因素,它会将O(n)复杂度降低到O(sqrt(n))

def factors(n):    
    return set(reduce(list.__add__, 
                ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))

考虑到代码上方的两点都是

def amicable(a, b):
    if sum(factors(a) - {a}) == b and sum(factors(b) - {b}) == a:
        return True
    return False

sum_of_amicables = 0
for m in range (1, 10001):
    for n in range (1, 10001):
        if m!= n and amicable(m, n) == True:
            sum_of_amicables = sum_of_amicables + m + n

这个最终代码花了 10分钟为我跑,这是你提到的时间的一半。

我可以通过优化factors方法将其优化为 1:30分钟

factors方法有10000 * 10000次调用。并且每个号码都会调用factors 10000次。也就是说,它为相同的数字计算因子10000次。因此,我们可以通过缓存先前因子计算的结果来优化它,而不是在每次调用时计算它们。

以下是我修改factors以缓存结果的方法。

def factors(n, cache={}):
    if cache.get(n) is not None:
            return cache[n]
    cache[n] = set(reduce(list.__add__,
                    ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))
    return cache[n]

完整代码:(运行时间1:30分钟)

所以完整和最终的代码变成了

def factors(n, cache={}):
    if cache.get(n) is not None:
            return cache[n]
    cache[n] = set(reduce(list.__add__,
                    ([i, n//i] for i in range(1, int(n**0.5) + 1) if n % i == 0)))
    return cache[n]

def amicable(a, b):
    if sum(factors(a) - {a}) == b and sum(factors(b) - {b}) == a:
        return True
    return False

sum_of_amicables = 0
for m in range (1, 10001):
    for n in range (1, 10001):
        if m!= n and amicable(m, n) == True:
            sum_of_amicables = sum_of_amicables + m + n

您仍然可以进一步改进它。

提示: sum每个号码也会被调用10000次。

答案 2 :(得分:0)

请注意,您不需要进行双循环。只需将M从1循环到10000, 对每个M进行分解并计算除数之和:S(M)。然后检查N = S(M)-M是否具有相同的除数之和。这是一个源自友好对的定义的直接算法。

优化友好对搜索还有很多其他技巧。只需几分之一秒即可找到1,000,000,000以下的所有友好数字。阅读this in-depth article,您还可以查看该文章中的参考C++ code

答案 3 :(得分:0)

添加答案:

def sum_factors(self, n):  
    s = 1
    for i in range(2, int(math.sqrt(n))+1):
        if n % i == 0:
            s += i
            s += n/i
    return s

def amicable_pair(self, number):
    result = 0
    for x in range(1,number+1):
        y = self.sum_factors(x)
        if self.sum_factors(y) == x and x != y:
            result += x
    return result

不需要集合或数组。提高存储和清晰度。

答案 4 :(得分:0)

#fetching two numbers from the user
num1=int(input("Enter first number"));
num2=int(input("enter the second number"));
fact1=[];
fact2=[];
factsum1=0;
factsum2=0;


#finding the factors of the both numbers
for i in range(1,num1):
    if(num1%i==0):
        fact1.append(i)


for j in range(1,num2):
    if(num2%j==0):
        fact2.append(j)

print ("factors of {} is {}".format(num1,fact1));
print ("factors of {} is {}".format(num2,fact2));
#add the elements in the list
for k in range(len(fact1)):
    factsum1=factsum1+fact1[k]

for l in range(len(fact2)):
    factsum2=factsum2+fact2[l]

print (factsum1);
print (factsum2);

#compare them
if(factsum1==num2 and factsum2==num1 ):
    print "both are amicable";
else:
    print "not amicable ";








this is my owm understanding of the concept

答案 5 :(得分:0)

大家仔细阅读代码和注释,可以轻松理解

def amicable_number(number):

    list_of_tuples=[] 
    amicable_pair=[] 

    for i in range(2,number+1): # in which range you want to find amicable

        divisors = 1 # initialize the divisor

        sum_of_divisors=0 #here we add the divisors

        while divisors < i: # here we take one number and add their divisors

            if i%divisors ==0:   #checking condition of complete divison
                sum_of_divisors += divisors
            divisors += 1
        list_of_tuples.append((i,sum_of_divisors)) #append that value and sum of there divisors

    for i in list_of_tuples: 
                              #with the help of these loops we find amicable with duplicacy
        for j in list_of_tuples:
            if i[0] == j[1] and i[1] == j[0] and j[0] != j[1]: #condition of amicable number 

                amicable_pair.append((j[0],i[0])) # append the amicable pair

    # i write this for_loop for removing the duplicacy if i will mot use this for loop this
    # be print both (x,y) and (y,x) but we need only one among them
    for i in amicable_pair:

        for j in amicable_pair[1:len(amicable_pair)]: #subscript the list
            if i[0] == j[1]:
                amicable_pair.remove(i) # remove the duplicacy

    print('list of amicable pairs number are: \n',amicable_pair)

amicable_number(284) #call the function
相关问题