Python:如何使这些代码更快地运行

时间:2017-06-15 16:35:38

标签: python algorithm performance processing-efficiency

我是Python新手,我正在通过Codewars慢慢学习。我知道这可能违反规则,但我有一个效率问题。

您将获得一个整数列表

ls = [100,76,56,44,89,73,68,56,64,123,2333,144,50,132,123,34,89]

你必须写一个函数choose_best_sum(t,k,ls)

这样你就可以找到ls的k个整数的组合,使得这些k个整数的总和接近或等于t。

我的最终解决方案通过了测试,但更详细的测试可能因效率而失败。我想更多地了解效率。这是我的代码

import itertools

def choose_best_sum(t, k, ls):
    if sum(sorted(ls)[:k]) > t or len(ls) < k:
       return None
    else:
       combos = itertools.permutations(ls, k)
       return max([[sum(i)] for i in set(combos) if sum(i) <= t])[0]

有人可以突出显示瓶颈在哪里(我假设在排列调用中)以及如何更快地完成此功能?

编辑:

以上解决方案描述了

1806730函数调用0.458秒

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.457    0.457 <string>:1(<module>)
    1    0.000    0.000    0.457    0.457 exercises.py:14(choose_best_sum)
742561    0.174    0.000    0.305    0.000 exercises.py:19(<genexpr>)
321601    0.121    0.000    0.425    0.000 exercises.py:20(<genexpr>)
    1    0.000    0.000    0.458    0.458 {built-in method builtins.exec}
    1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
    1    0.032    0.032    0.457    0.457 {built-in method builtins.max}
    1    0.000    0.000    0.000    0.000 {built-in method builtins.sorted}
742561    0.131    0.000    0.131    0.000 {built-in method builtins.sum}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

使用我得到最终解决方案的帮助是:

def choose_best_sum(t, k, ls):
   ls = [i for i in ls if i < t and i < (t - sum(sorted(ls)[:k-1]))]
   if sum(sorted(ls)[:k]) > t or len(ls) < k:
      return None
   else:
      return max(s for s in (sum(i) for i in itertools.combinations(ls, k)) if s <= t)

订购者:标准名称

7090次函数调用0.002秒

 ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.003    0.003 <string>:1(<module>)
 2681    0.001    0.000    0.003    0.000 exercises.py:10(<genexpr>)
    1    0.000    0.000    0.003    0.003 exercises.py:5(choose_best_sum)
    1    0.000    0.000    0.000    0.000 exercises.py:6(<listcomp>)
    1    0.000    0.000    0.003    0.003 {built-in method builtins.exec}
    1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
    1    0.000    0.000    0.003    0.003 {built-in method builtins.max}
   17    0.000    0.000    0.000    0.000 {built-in method builtins.sorted}
 4385    0.001    0.000    0.001    0.000 {built-in method builtins.sum}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

1 个答案:

答案 0 :(得分:3)

表达式中有几个明显的缺陷

max([[sum(i)] for i in set(combos) if sum(i) <= t])[0]
  1. 由于没有充分理由,您正在运行sum(i)两次;

  2. 您正在将结果打包到列表中([sum(i)]),然后将其解包([0]

  3. 您正在无条件地将combos转换为一套

  4. 尝试用

    替换它
    sums = [sum(c) for c in combos]
    return max(s for s in sums if s <= t)
    

    编辑:好的,关于更好算法的一些想法:

    D'哦!首先,使用itertools.combinations代替itertools.permutations。你只是拿这笔钱;物品的顺序没有区别。如果你运行ie k = 4,combinations将返回4! = =相同输入数据上的条目少于permutations的24倍。

    其次,我们希望在一开始就从ls丢弃尽可能多的项目。显然我们可以抛出任何价值&gt;吨;但我们可以得到更严格的约束。如果我们添加(k - 1)最小值,则最大允许值必须是&lt; = t - (k-1)_sum。

    (如果我们正在寻找一个确切的总和,我们可以反向运行这个技巧 - 添加(k - 1)最大值会给我们一个最小允许值 - 我们可以重复应用这两个规则来放弃更多的可能性这不适用于此。)

    第三,我们可以查看(k - 1)值的所有组合,然后使用bisect.bisect_left直接跳到最佳k'值。有一点复杂,因为你必须仔细检查第k个值是否还没有被选为(k - 1)值之一 - 你不能直接使用内置的{{{ 1}}函数,但您可以使用itertools.combinations code的修改副本(即测试itertools.combinations返回的索引高于当前使用的最后一个索引)。

    这些应该可以使你的代码加速bisect_left ...祝你好运!

    编辑2:

    让这成为过度优化危险的教训: - )

    len(ls) * k * k!