Python 快速生成带约束组合的方法?

Python 快速生成带约束组合的方法?,python,algorithm,combinatorics,Python,Algorithm,Combinatorics,我有一个生成函数,它创建列表的笛卡尔积。实际应用程序使用更复杂的对象,但它们可以用字符串表示: 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', '

我有一个生成函数,它创建列表的笛卡尔积。实际应用程序使用更复杂的对象,但它们可以用字符串表示:

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”;第二个约束减少了我需要考虑的第一个约束的有效组合的数量。
因为这些约束很难建立;我希望可视化每个约束中的所有块的组合是什么样子的,但只显示通过所有约束的有效组合。这就是我的问题。谢谢

尝试提供迭代器,直接生成要筛选的名称,如下所示:

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)
这是一个发电机


这里的重要区别在于,列表理解使用指定的条件和生成器创建列表,而仅提供生成器允许过滤器根据需要生成值。重要的机制是,这实际上可以归结为只在表达式的值变得必要时对表达式求值。

通过从列表切换到生成器,Rob的答案节省了空间,但不节省时间(至少不是渐进的)。您提出了一个非常广泛的问题,关于如何列举基本上是一个问题的所有解决方案。最大的成功将来自于强制执行,但如果不知道具体的约束条件,就很难给您提供建议。

不要将迭代器强制转换为列表(),只需在列表理解中使用迭代器(p当前为)。很好!我编辑了这个示例。
s3=['c']
是否也能正常工作?也就是说,与其过滤输出,不如限制输入?@BrentWashburne是的,我认为在这个示例中,可以设置
s3=['c']
,但在实际应用中,存在更复杂的约束,其中输入无法事先消除。例如,如果存在只允许两个连续的相同字母(例如“cc”、“dd”、“ee”、“ff”)的约束,那么在这种情况下,
s2=s3=['c']
,然后
s2=s3=['d']
等等。我的观点是,你可以通过巧妙地处理输入而不是过滤每一个可能的组合来加快速度,因为你知道其中很多都是未使用的。如果所有其他方法都失败,请使用过滤器。很好。但是你应该使用
re.search
而不是
re.match
;毕竟,字符串不必以正则表达式开头(在本例中,它们只是碰巧以正则表达式开头),而只是为了包含它。明白了,这很有意义。但这真的是懒惰的评估吗?每个组合都必须在过滤器内部生成,不是吗?这就是我试图避免的情况。是的,这是一种懒惰的评估,因为实际的列表元素只有在准备好使用时才会生成(也就是在对正则表达式进行测试时)。我不确定你的具体用例是什么,所以这是最好的,除非你有更好的方法来限制生成的组合数量,比如限制输入。谢谢你提供的信息链接!如果有帮助的话,我编辑了我的问题以添加更多细节。