【问题标题】:This code is too inefficient, how can I increase memory and execution efficiency?这段代码效率太低,如何提高内存和执行效率?
【发布时间】:2019-05-01 13:40:25
【问题描述】:

我正在尝试完成以下挑战:https://app.codesignal.com/challenge/ZGBMLJXrFfomwYiPs。 我编写了似乎可以工作的代码,但是,它的效率太低以至于无法通过测试(执行时间过长并且使用了太多内存)。有什么方法可以提高效率吗?我对构建高效脚本很陌生。有人提到“map()”可以用来代替“for i in range(1, n)”。感谢 Xero Smith 和其他人提出的优化它的建议:

from functools import reduce
from operator import mul
from itertools import combinations

# Starting from the maximum, we can divide our bag combinations to see the total number of integer factors                                                                                    

def prime_factors(n):
    p = 2
    dct = {}
    while n != 1:
        if n % p:
            p += 1
        else:
            dct[p] = dct.get(p, 0) + 1
            n = n//p
    return dct


def number_of_factors(n):
    return reduce(mul, (i+1 for i in prime_factors(n).values()), 1)


def kinderLevon(bags):
    candies = list()
    for x in (combinations(bags, i) for i in range(1, len(bags)+1)):
        for j in x:
            candies.append(sum(j))
    satisfied_kids = [number_of_factors(i) for i in candies]
    return candies[satisfied_kids.index(max(satisfied_kids))]

任何帮助将不胜感激。

谢谢,

亚伦

【问题讨论】:

  • 您认为是什么原因导致效率低下?
  • 您需要先问自己几个问题:您认为什么需要时间复杂度?你认为什么会占用大量内存?一旦你确定了这一点,你就可以去尝试优化这些点。然而,这类挑战的解决方案通常是做一些完全不同的事情
  • 例如有一个测试有 bag = [1, 11, 121, 1331, 14641, 151151, 22, 33, 666, 999]。这是一个问题,因为算法正在对这些数字进行所有可能的组合并将它们存储在一个数组中。我认为可能需要一个 map 或 yield 函数来停止将所有内容存储在数组中,就像它们目前一样。
  • 对于初学者,将factors 的运行时间减半,一旦你达到 n/2 就停止,然后只需添加 n 本身(n 大于 n/2 的唯一因数)。此外,无需构建result 列表,只需计算因素即可。
  • 提出问题后,请不要使用给定的解决方案更新它,因为这会删除/使原始上下文无效,从而降低每个人的价值。

标签: python python-3.x algorithm optimization


【解决方案1】:

根据我的评论,我已经可以确定内存和复杂性的改进。在您的 factors 函数中,由于您只需要因子的数量,因此您只能计算它们而不是存储它们。

def factors(n):
    k = 2
    for i in range(2, n//2 +1):
        if n % i == 0:
            k += 1
    return k

编辑:按照 cmets 中的建议,提前停止计数器。

这实际上降低了大数字的时间复杂度,但对于较小的数字却没有。

这比使用列表推导(仍然分配内存)的改进要好得多

此外,两次分配您的组合列表是没有意义的。你在做

x = list(combinations(bags, i));
for j in list(x):
    ...

将元组(由combinations 返回)转换为列表的第一行,因此复制了数据。第二行list(x) 重新分配列表的副本,占用更多内存!在那里你真的应该写:

for j in combination(bags, i):
    ...

作为语法问题,请不要在 Python 中使用分号;

【讨论】:

  • 这有帮助,但是代码显然仍然需要很长时间才能执行。我希望有一些我可以插入代码来自动优化速度(和/或内存)的东西。
  • 好吧,如果这存在,挑战将毫无意义。这是程序员的工作。如果代码仍然需要太长时间,那么您需要以不同的方式解决问题
  • 你是对的,虽然这不是优化问题。问题是创建一个返回所需输出的函数。我已经做到了。这里的问题是该解决方案不符合网站解释器的执行约束,所以我不得不优化我的解决方案:/,
  • 你也可以k = 2; for i in range(2, n//2+1):
【解决方案2】:

首先,组合是可迭代的。这意味着您不必在迭代它们之前将它们转换为列表;事实上,这样做是非常低效的。

接下来可以显着改进的是您的factors 程序。目前它是线性的。我们可以做得更好。我们可以通过以下算法得到整数N的因子个数:

  1. 得到N的素因数分解使得N = p1^n1 * p2^n2 * ...
  2. N的因子个数为(1+n1) * (1+n2) * ...

详情请参阅https://www.wikihow.com/Find-How-Many-Factors-Are-in-a-Number

另外,您当前的解决方案有很多未使用的变量和计算。摆脱他们。

有了这些,我们得到以下应该可以工作的东西:

from functools import reduce
from operator import mul
from itertools import combinations

# Starting from the maximum, we can divide our bag combinations to see the total number of integer factors                                                                                    

def prime_factors(n):
    p = 2
    dct = {}
    while n != 1:
        if n % p:
            p += 1
        else:
            dct[p] = dct.get(p, 0) + 1
            n = n//p
    return dct


def number_of_factors(n):
    return reduce(mul, (i+1 for i in prime_factors(n).values()), 1)


def kinderLevon(bags):
    candies = list()
    for x in (combinations(bags, i) for i in range(1, len(bags)+1)):
        for j in x:
            candies.append(sum(j))
    satisfied_kids = [number_of_factors(i) for i in candies]
    return candies[satisfied_kids.index(max(satisfied_kids))]

【讨论】:

  • 这个解决方案很棒,在测试方面比我的解决方案更进一步。然而,它仍然失败,这令人难以置信的沮丧。令人失望的是它在隐藏测试中失败了。据说最严格的测试(我可以看到)大约需要 1.6e-06 秒才能完成,所以我想时间阈值会在 1e-05 左右。
  • @alpacinohead 欢迎来到 SO,当答案对您有帮助时,您应该对答案进行投票和/或投票。见stackoverflow.com/help/someone-answers。至于您的问题,您要求一种加快实施速度的方法,这就是所做的。可能是您需要开发不同的算法,这是另一回事。
  • 你是对的。我确实要求帮助加快我的算法,这确实是你所做的。我会将此标记为正确答案。谢谢。
  • 使用这个解决方案并将“while n!= 1”更改为“while n!=1 and p
【解决方案3】:

使用列表推导。因子函数可以这样转换:

def factors(n):
    return len([i for i in range(1, n + 1) if n % i == 0])

【讨论】:

  • 谢谢。我已经添加了这个更改,但是代码仍然不符合接受要求的执行速度。
  • 你可以让这更简单。 return sum(1 for i in range(2, n+1) if n % i == 0) + 1 对于 n > 1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-21
  • 2014-05-06
  • 2015-12-23
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
相关资源
最近更新 更多