Python 优化非丰和算法

Python 优化非丰和算法,python,Python,我试图解决: 一个完全数是一个由它的适当因子之和构成的数 正好等于这个数字。例如,适当的 28的除数是1+2+4+7+14=28,这意味着28 是一个完美的数字 如果一个数n的真因子之和为零,则称为亏数n 小于n,如果该和超过n,则称为富足 因为12是最小的富足数,1+2+3+4+6=16,所以 可以写成两个丰富数之和的最小数 她24岁。通过数学分析,可以证明所有整数 大于28123可以写成两个充裕数之和。 然而,通过分析无法进一步降低该上限 尽管我们知道最大的数字是不可能的 表示为两个充裕数之

我试图解决:

一个完全数是一个由它的适当因子之和构成的数 正好等于这个数字。例如,适当的 28的除数是1+2+4+7+14=28,这意味着28 是一个完美的数字

如果一个数n的真因子之和为零,则称为亏数n 小于n,如果该和超过n,则称为富足

因为12是最小的富足数,1+2+3+4+6=16,所以 可以写成两个丰富数之和的最小数 她24岁。通过数学分析,可以证明所有整数 大于28123可以写成两个充裕数之和。 然而,通过分析无法进一步降低该上限 尽管我们知道最大的数字是不可能的 表示为两个充裕数之和小于此极限

求所有不能写入的正整数之和 两个充裕数之和

我的解决方案:

#returns a list of the divisors of a given number
def Divs(Number):
    Divisors = []

    for i in range(2 , int(Number**0.5) + 1):
        if Number % i == 0:
            Divisors.append(i)

    for q in range(len(Divisors)):
        if Divisors[q] != (Number / Divisors[q]):
            Divisors.append(Number / Divisors[q])

    Divisors.insert(0,1)
    return Divisors

#returns a list of abundant numbers up to and including the limit
def AbList(limit):
    Abundant = []

    for i in range(11,limit + 1):
        if sum(Divs(i)) > i:
            Abundant.append(i)

    return Abundant

#Finds the sum of all positive integers that cannot be written as the
#sum of two abundant numbers...
def AbSum(limit):
    Abundant = AbList(limit)
    NoAbSum = 0
    for i in range(1 , limit):
        AbSum = 0
        x = 0
        for x in Abundant:
            if i - x in Abundant[:i]:
                AbSum = 1
                break
        if AbSum == 0:
            NoAbSum += i
    return NoAbSum
这花了我的3.4GHz处理器大约15分钟来解决,我正在寻找更好的方法。我不关心前两个函数,因为它们一起运行不到一秒钟。第三个功能是这里的kicker。它在数字范围内运行到极限(在本例中为20000左右),每次,它都会运行大量数字列表,从当前数字中减去每个数字,然后根据大量数字列表检查答案。如果存在匹配项,循环将中断,并使用下一个数字重试,直到达到极限


我知道一定有更好的方法可以做到这一点,但我对编程有些陌生。如何加快该算法的速度?

一件有帮助的事情是,一旦大量的数字大于您正在测试的数字,就从内部循环中退出

我也不明白你的这段代码:

 for q in range(len(Divisors)):
    if Divisors[q] != (Number / Divisors[q]):
        Divisors.append(Number / Divisors[q])

一旦你确认模为0,它就是一个除数。我不知道你为什么要进行身份检查。

你的代码看起来可能会从映射、筛选或列表理解中受益,从而有利于循环。

你在测试1和限制(比如30000)之间的每一个数字,而不是每一个丰富的数字,所以你大约要进行30000*7428次迭代;您正在检查结果是否在列表中,这是一个非常缓慢的操作——它会检查列表中的每个项目,直到找到匹配项

相反,您应该生成由两个充裕数之和组成的每个数。最多需要7428*7428次迭代——如果执行得当,迭代次数会更少(提示:通过确保b始终>=a来避免同时检查a+b和b+a;正如其他人所建议的,当总和过大时,请确保停止)。在
limit
下面的数字列表中标记这些数字,然后将剩余的数字相加

换言之:

[... 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43 ...]
变成

[... 31, 0, 33, 34, 35, 0, 37, 0, 39, 0, 41, 0, 43 ...]

编辑:在玩了几分钟的实现之后,我可以自信地说,
如果I-x丰富[:I]:
就是问题所在。发布到ProjectEuler的p23论坛上的第一个python解决方案本质上是算法的一个巧妙实现,唯一的主要区别是它使用了一组大量的数字,而不是列表。它在Atom处理器上以15秒的时间解决了这个问题;当我将它改为使用列表时,15分钟后,它仍然没有解决问题

这个故事的寓意是:
x在列表中
很慢

不过,直接生成总和要比减法和检查快。:)


请注意,表达式中的
需要O(i)个时间,因此循环是O(n²)。如果您使用
集合
而不是
列表
,,您可以使用一个简单的数学技巧:不能写成两个富足数之和的所有数之和是所有数之和减去可以写成两个富足数之和的数:

 solution = sum(range(limit)) - sum(all_two_sums(abundant_numbers))
sum(range(limit))
也可以用数学简化,但除非你是高斯;-),否则你可能找不到它。)


您已经有了一个丰富的数字列表,因此创建一组数字相对容易,这些数字可以写成两个丰富数字的总和,并且总和小于限制。只要确保没有重复的数字,Python的
集合就可以做到这一点。

让我们先搜索一点,找出无法表达的最大数字,因为两个丰富的数字之和实际上是20161。然后,我们可以用一个简单的集合成员测试来解决这个问题。另外,它运行得相当快。:-)


在i5上平均运行0.6463340939061518秒,基于
timeit

您当前的解决方案是
O(m*n)
,其中
m
(限制)远大于
n
(大量数字的数量低于限制)。现在,计算所有其他丰富数之和的数的时间复杂度是多少?这比您当前的解决方案好还是坏?为什么要对除数进行排序?一旦您提交了正确的解决方案,您就可以访问该问题的讨论线程,在那里您可以找到一些最有效的解决方案。洛特,出于某种原因,我在最后懒洋洋地加了1。修好了,谢谢。科菲周,我通读了讨论线索,在那里我看到了很多解决方案,但很少有解释。先进的、优化的解决方案对我来说都是极其难以理解的…:/@crayZsaaron:这与问题没有直接关系,但我觉得您的代码中有几个小的低效,我应该提到——比任何东西都更美观。首先,
AbSum()
中的
x=0
行不是必需的;x在
for
语句中定义。其次,不要使用本地
AbSum
变量来存储<
 solution = sum(range(limit)) - sum(all_two_sums(abundant_numbers))
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from math import sqrt

def d(n):
    sum = 1
    t = sqrt(n)
    # only proper divisors; start from 2.
    for i in range(2, int(t)+1):
        if n % i == 0:
            sum += i + n / i
    # don't count the square root twice!
    if t == int(t):
        sum -= t
    return sum

limit = 20162
sum = 0
# it's a set, after all. sets are faster than lists for our needs.
abn = set()
for n in range(1, limit):
    if d(n) > n:
        abn.add(n)
    # if the difference of the number we're examining and every number in the set
    # is in the set, then the number is the sum of two abundant numbers.
    # otherwise, we must add it to our sum in question.
    if not any( (n-a in abn) for a in abn ):
        sum += n