如何在剔除坏序列时生成排列?
Posted
技术标签:
【中文标题】如何在剔除坏序列时生成排列?【英文标题】:How to generate permutations while culling bad sequences? 【发布时间】:2017-07-31 21:13:23 【问题描述】:问题陈述:我有 150 个带有权重和值的对象。对象的权重可能会根据它们被选择的顺序而改变,通常选择大约 70-80 个项目。我最多只能选择一个最大权重,因此一旦我找到具有该序列的子解决方案,就需要跳过所有以相同序列开头的排列。目标是实现价值最大化。
我可以轻松地创建所有排列:
from itertools import permutations
for i in permutations(list(range(150))):
# do something with i
但是,这会创建许多我不需要检查的序列。我也可以用 r 限制排列长度,这样
from itertools import permutations
for i in permutations(list(range(150)), r=80):
# do something with i
但是对于非常糟糕的序列,仍然会有很多冗余检查。此外,这可能会在“最佳”解决方案之前停止。
我可以做类似的事情
from itertools import permutations
v = []
for i in permutations(list(range(150)), r=80):
if v and v == i[:len(v)]:
continue
# do something with i
v = i # would be some optimal subset of i
但是,这仍然需要很长时间才能运行,因为 Python 仍在生成和检查序列。关于我应该如何处理这个问题的任何想法?理想情况下,我将能够并行运行检查。
更多背景:我正在尝试为一款名为 Black Desert Online 的游戏创建优化的资源图(位于 somethinglovely.net/bdo/ 的图示例)。该图有大约 150 个资源节点,每个资源节点可以连接到 14 个根节点的子集。图上的中间节点具有与之关联的权重。每个城市都有可以连接的最大节点数量,并且将资源节点连接到城市需要额外的权重成本。我在随机图生成以及用于“寻找”最佳解决方案的遗传算法方面没有取得成功。此外,仅仅做出贪婪的选择会导致一个次优的解决方案。我目前对如何生成一个蛮力+综合解决方案感到困惑,该解决方案将在合理的时间内运行(合理的在一天之内在合理的台式计算机上运行。
【问题讨论】:
如果你使用通常的递归算法,你可以很容易地从递归中的任何地方返回来修剪整个子树。 之前尝试过,但遇到了递归深度错误。但是在过度设计问题时,我忘记了 setrecursionlimit。虽然我认为这仍然需要比我愿意运行程序更长的时间才能完成,但它确实回答了我提出的问题。 如果对象的权重根据它们被选择的顺序而变化,那么这会使问题变得更加困难——因为同一组对象的权重可能会根据它们的顺序而有所不同选择。这似乎意味着您必须在每个排列中尝试所有对象组合。 您能否更详细地描述问题的工作原理?可能会有更详细的答案 是增加连接的成本只是额外边的权重加上“连接惩罚”的总和(或者无论如何你称之为,将更多节点链接到根的成本会上升)?因为那样看起来您不必关注顺序(只是边缘是否正在使用的二进制事实,以及连接到根的事物的数量)并且不优化顺序,您应该有很多较小的问题。也许您也可以将其编写为线性程序,以获得良好的剪枝启发式。 【参考方案1】:浏览库存清单,一次一件,并尝试包装有和没有该物品(两次递归)。当我们达到以下两个点之一时报告解决方案:
-
没有更多需要考虑的事项
剩余的列表符合我们的体重预算。
这会通过主动构建来处理剔除。
代码:
items = [
# Description, weight
("petrol", 10),
("clothes", 8),
("tents", 7),
("beer", 16),
("food", 20),
("teddy bear", 3),
("tank", 25),
("skin lotion", 2),
("library", 17),
("mortar", 9),
("cut lumber", 12),
("sports gear", 14),
]
limit = 20
def load(inventory, max_weight, current):
still_okay = [item for item in inventory if item[1] <= max_weight]
if len(still_okay) == 0:
# Can't add any more; emit solution and back up
print "RESULT", current
else:
# If the rest of the list fits in our weight budget,
# take everything.
if sum([item[1] for item in still_okay]) <= max_weight:
print "RESULT", current + still_okay
else:
item = still_okay.pop()
# recur on two branches: one each with and without this item
load(still_okay, max_weight - item[1], current + [item])
load(still_okay, max_weight, current)
load(items, limit, [])
输出:
RESULT [('sports gear', 14), ('teddy bear', 3), ('skin lotion', 2)]
RESULT [('cut lumber', 12), ('skin lotion', 2), ('teddy bear', 3)]
RESULT [('cut lumber', 12), ('teddy bear', 3)]
RESULT [('cut lumber', 12), ('tents', 7)]
RESULT [('cut lumber', 12), ('clothes', 8)]
RESULT [('mortar', 9), ('skin lotion', 2), ('teddy bear', 3)]
RESULT [('mortar', 9), ('skin lotion', 2), ('tents', 7)]
RESULT [('mortar', 9), ('skin lotion', 2), ('clothes', 8)]
RESULT [('mortar', 9), ('teddy bear', 3), ('tents', 7)]
RESULT [('mortar', 9), ('teddy bear', 3), ('clothes', 8)]
RESULT [('mortar', 9), ('tents', 7)]
RESULT [('mortar', 9), ('clothes', 8)]
RESULT [('mortar', 9), ('petrol', 10)]
RESULT [('library', 17), ('skin lotion', 2)]
RESULT [('library', 17), ('teddy bear', 3)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('tents', 7), ('clothes', 8)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('clothes', 8)]
RESULT [('skin lotion', 2), ('teddy bear', 3), ('petrol', 10)]
RESULT [('skin lotion', 2), ('beer', 16)]
RESULT [('skin lotion', 2), ('tents', 7), ('clothes', 8)]
RESULT [('skin lotion', 2), ('tents', 7), ('petrol', 10)]
RESULT [('skin lotion', 2), ('petrol', 10), ('clothes', 8)]
RESULT [('teddy bear', 3), ('beer', 16)]
RESULT [('teddy bear', 3), ('tents', 7), ('clothes', 8)]
RESULT [('teddy bear', 3), ('tents', 7), ('petrol', 10)]
RESULT [('teddy bear', 3), ('clothes', 8)]
RESULT [('teddy bear', 3), ('petrol', 10)]
RESULT [('food', 20)]
RESULT [('beer', 16)]
RESULT [('tents', 7), ('clothes', 8)]
RESULT [('tents', 7), ('petrol', 10)]
RESULT [('petrol', 10), ('clothes', 8)]
【讨论】:
这回答了我的问题的一个更简单的版本。您的解决方案采用静态权重,而我的版本顺序很重要,因为它可以更改权重。然而,这应该给我一个比我最初的贪婪算法更好的近似值。我只需要在运行模拟现实的选择顺序(以及静态权重)之前弄清楚如何对项目进行排序。 是的,我认为这会奏效。您需要在第一个递归步骤中插入权重更新。也许颠倒顺序,所以你做“没有”递归,然后改变权重,最后进行“有”递归调用。以上是关于如何在剔除坏序列时生成排列?的主要内容,如果未能解决你的问题,请参考以下文章