How to tractably solve the assignment optimisation task
我正在编写一个脚本,该脚本从
他们都是1:1配对的,每个公司只有一个人,每个人只属于一个公司,公司数量等于人数。我使用自顶向下的方法和记忆表 (
我相信我可以大大提高这里发生的事情的速度,但我不确定如何。我担心的区域标有
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | def getMaxCTR(companies, people): if(memDict.has_key((companies,people))): return memDict[(companies,people)] #here's where we return the memoized version if it exists if(not len(companies) or not len(people)): return 0 maxCTR = None remainingCompanies = companies[1:len(companies)] #slow? for p in people: remainingPeople = list(people) #slow? remainingPeople.remove(p) #slow? ctr = ctrPairs[(companies[0],p)] + getMaxCTR(remainingCompanies,tuple(remainingPeople)) #recurse if(ctr > maxCTR): maxCTR = ctr memDict[(companies,people)] = maxCTR return maxCTR |
对于所有对学习理论的用途感到疑惑的人来说,这个问题是一个很好的例证。正确的问题不是关于"在 Python 中的列表和元组之间快速跳转的方法"——缓慢的原因是更深层次的原因。
您在这里尝试解决的问题被称为赋值问题:给定两个包含 n 个元素的列表和 n×n 个值(每对的值),如何分配它们以使总"值" " 最大化(或等效地最小化)。有几种算法,例如匈牙利算法(Python 实现),或者您可以使用更通用的最小成本流算法来解决它,甚至将其转换为线性程序并使用 LP 求解器。其中大多数的运行时间为 O(n3).
你上面的算法所做的是尝试各种可能的配对方式。 (记忆仅有助于避免重新计算子集对的答案,但您仍在查看所有子集对。)这种方法至少是 Ω(n222n)。对于 n=16,n3 为 4096,n222n 为 1099511627776。当然每个算法中都有常数因子,但是看到区别了吗? :-)(问题中的方法仍然比朴素的 O(n!) 更好,这会更糟糕。)使用 O(n^3) 算法之一,我预测它应该及时运行到 n=10000 左右,而不是最多 n=15。
"过早优化是万恶之源",正如 Knuth 所说,但延迟/逾期优化也是如此:你应该在实施之前首先仔细考虑一个合适的算法,而不是选择一个不好的算法,然后再想知道哪些部分其中很慢。 :-) 即使在 Python 中糟糕地实现一个好的算法也会比修复上面代码的所有"慢?"部分(例如,通过用 C 重写)快几个数量级。
我在这里看到两个问题:
效率:您正在为每个公司重新创建相同的
memoization:您使用元组而不是列表将它们用作
如果你想得到一个元组的副本作为一个列表,你可以这样做
mylist = list(mytuple)
这一行:
剩余的公司 = 公司[1:len(公司)]
可以用这一行替换:
1 | remainingCompanies = companies[1:] |
非常轻微的速度增加。这是我看到的唯一改进。