我是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}
答案 0 :(得分:3)
表达式中有几个明显的缺陷
max([[sum(i)] for i in set(combos) if sum(i) <= t])[0]
由于没有充分理由,您正在运行sum(i)
两次;
您正在将结果打包到列表中([sum(i)]
),然后将其解包([0]
)
您正在无条件地将combos
转换为一套
尝试用
替换它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!