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

时间:2015-06-24 16:55:57

标签: python algorithm combinatorics

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

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']

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

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

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

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

<div class="row">
    <div class="col-xs-12 col-md-6 col-md-push-6">
        <div class="row">
            <div class="col-xs-12 col-md-12"><div class="well xtall">b1</div></div>
            <div class="col-xs-12 col-md-12"><div class="well">b2</div></div>
        </div>
    </div>
    <div class="col-xs-12 col-md-6 col-md-pull-6">
        <div class="row">
            <div class="col-xs-12 col-md-12"><div class="well tall">a1</div></div>
            <div class="col-xs-12 col-md-12"><div class="well tall">a2</div></div>
        </div>
    </div>
    <div class="col-xs-12 col-md-6">
        <div class="row">
            <div class="col-xs-12 col-md-12"><div class="well tall">a4</div></div>
            <div class="col-xs-12 col-md-12"><div class="well">a3</div></div>
        </div>
    </div>
    <div class="col-xs-12 col-md-6"><div class="well tall">b3</div></div>
    <div class="col-xs-12 col-md-6"><div class="well">a5</div></div>
  </div>

在实际应用中,链条更长,可能有数千种组合,并且应用数百种约束是相当计算密集的,所以我关注可扩展性。

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

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

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

因为这些限制难以创造;我希望可视化每个约束中所有块的组合看起来像什么,但只有通过所有约束的有效组合。因此我的问题。谢谢!

2 个答案:

答案 0 :(得分:6)

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

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,它实际上只归结为只评估表达式,因为它们的值是必要的。

答案 1 :(得分:1)

通过从列表切换到生成器,Rob的答案可以节省空间,但不能节省时间(至少不是渐近)。您已经提出了一个非常广泛的问题,即如何枚举所有基本上为constraint satisfaction problem的解决方案。最大的胜利来自强制执行local consistency,但如果没有具体的约束知识,很难给你建议。