【问题标题】:Fast way of generating combinations with constraints?快速生成带有约束的组合?
【发布时间】:2015-09-11 00:16:16
【问题描述】:

我有生成列表的笛卡尔积的生成器函数。真正的应用程序使用更复杂的对象,但它们可以用字符串来表示:

import itertools

s1 = ['a', 'b']
s2 = ['c', 'd', 'e', 'f']
s3 = ['c', 'd', 'e', 'f']
s4 = ['g']

p = itertools.product(*[s1,s2,s3,s4])
names = [''.join(s) for s in p]

在本例中,结果是 32 个字符组合:

names
['accg', 'acdg', 'aceg', 'acfg', 'adcg', 'addg', 'adeg', 'adfg', 'aecg',
 'aedg', 'aeeg', 'aefg', 'afcg', 'afdg', 'afeg', 'affg', 'bccg', 'bcdg',
 'bceg', 'bcfg', 'bdcg', 'bddg', 'bdeg', 'bdfg', 'becg', 'bedg', 'beeg',
 'befg', 'bfcg', 'bfdg', 'bfeg', 'bffg']

现在,假设我有一些限制,例如某些字符组合是非法的。例如,假设只允许包含正则表达式 '[ab].c' 的字符串。 ('a' 或 'b' 后接任意字母,后接 'c')

应用这些约束后,我们只剩下 8 个字符串的缩减集:

import re
r = re.compile('[ab].c')
filter(r.match, names)
['accg', 'adcg', 'aecg', 'afcg', 'bccg', 'bdcg', 'becg', 'bfcg']

在实际应用程序中,链更长,可能有数千种组合,并且应用数百个约束是相当计算密集型的,因此我担心可扩展性。

现在我正在检查每一个组合并检查其有效性。是否存在可以加快这一过程的算法/数据结构?

编辑: 也许这会澄清一点:在实际应用程序中,我从简单的基本块(如柱子、屋顶段、窗户等)组装建筑物的随机 2D 图片。这些约束限制了可以将哪种类型的块(及其旋转)组合在一起,因此生成的随机图像看起来很逼真,而不是随机的混乱。

给定的约束可以包含多种模式组合。但在所有这些组合中,许多组合都是无效的,因为不同的约束会禁止其中的某些部分。因此,在我的示例中,一个约束将包含上述字符的完整笛卡尔积。第二个约束是'[ab].c';这第二个约束减少了我需要考虑的第一个约束的有效组合的数量。

因为这些约束很难创建;我希望可视化每个约束中的所有块组合的样子,但只有通过所有约束的 valid 组合。因此我的问题。谢谢!

【问题讨论】:

  • 不要将迭代器强制转换为 list(),只需在列表理解中使用迭代器(p 当前所在的位置)。
  • 好收获!我编辑了示例。
  • s3 = ['c'] 不也能正常工作吗?也就是说,与其过滤输出,不如限制输入?
  • @BrentWashburne 是的,我认为在此示例中您可以设置 s3 = ['c'],但在实际应用中存在更复杂的约束,其中无法先验消除输入。例如,如果有一个限制,即只允许两个连续的相同字母(例如,'cc'、'dd'、'ee、'ff')
  • 在这种情况下,s2 = s3 = ['c'],然后是s2 = s3 = ['d'],依此类推。我的观点是,您可以通过智能输入而不是过滤所有可能的组合来真正加快速度,因为您知道其中许多组合是未使用的。如果一切都失败了,请使用过滤器。

标签: python algorithm combinatorics


【解决方案1】:

尝试只提供生成名称的迭代器直接过滤,如下所示:

import itertools
import re

s1 = ['a', 'b']
s2 = ['c', 'd', 'e', 'f']
s3 = ['c', 'd', 'e', 'f']
s4 = ['g']

p = itertools.product(*[s1,s2,s3,s4])
r = re.compile('[ab].c')
l = filter(r.search, (''.join(s) for s in p))
print(list(l))

这样,它不应该在内存中组装全套组合,它只会保留符合条件的组合。可能还有另一种方法。

编辑:

与原版的主要区别之一是:

[''.join(s) for s in p]

这是一个列表推导式,我们使用:

(''.join(s) for s in p)

这是一个生成器。

这里的重要区别是列表推导式使用指定的条件和生成器创建列表,而仅提供生成器允许过滤器根据需要生成值。重要的机制是lazy evaluation,它实际上只是归结为仅在表达式的值变得必要时评估它们。

【讨论】:

  • 不错。但是你应该使用re.search 而不是re.match;毕竟,字符串不必以该正则表达式开始(在示例中它们只是偶然这样做),而只是为了包含它。
  • 明白了,这是有道理的。但真的是懒评价吗?每个组合仍然必须在过滤器内部生成,不是吗?这就是我要避免的情况。
  • 是的,这是惰性求值,因为实际的列表元素仅在它们准备好使用时(即针对正则表达式进行测试时)生成。我不确定您的确切用例是什么,所以除非您有更好的方法来限制首先生成的组合数量,例如通过限制输入,否则它会尽可能好。
【解决方案2】:

通过从列表切换到生成器,Rob 的答案节省了空间,但没有节省时间(至少,不是渐近的)。您问了一个非常广泛的问题,即如何枚举本质上是constraint satisfaction problem 的所有解决方案。执行local consistency 将带来巨大的胜利,但如果没有具体了解您的限制条件,很难给您建议。

【讨论】:

  • 感谢您提供的信息链接!如果有帮助,我编辑了我的问题以添加更多细节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-04
  • 2015-03-20
  • 2019-06-18
  • 2013-07-16
  • 1970-01-01
  • 2021-05-07
相关资源
最近更新 更多