给定一个列表和排除元素,是否可以忽略包含这些元素的组合的计算?

Example 1

给定l = [1, 2, 3, 4, 5],我想计算size 4的所有组合,不包括在计算之前包含(1, 3)的组合.

结果将是:

    All results:            Wanted results:

    [1, 2, 3, 4]            [1, 2, 4, 5]
    [1, 2, 3, 5]            [2, 3, 4, 5]
    [1, 2, 4, 5]
    [1, 3, 4, 5]
    [2, 3, 4, 5]

包含1 and 3个的所有组合都已删除.

Example 2

由@Eric Duminil建议

结果分别是l = [1, 2, 3, 4, 5, 6]分、size 4分和l = [1, 2, 3, 4, 5, 6]

  • 不包括第二列中的(1, 2, 3)
  • 不包括第三栏中的(1, 2)

    All results:        Wanted results 1            Wanted results 2
                        (Excluding [1, 2, 3]):      (Excluding [1, 2])
    
    [1, 2, 3, 4]        [1, 2, 4, 5]                [1, 3, 4, 5]
    [1, 2, 3, 5]        [1, 2, 4, 6]                [1, 3, 4, 6]
    [1, 2, 3, 6]        [1, 2, 5, 6]                [1, 3, 5, 6]
    [1, 2, 4, 5]        [1, 3, 4, 5]                [1, 4, 5, 6]
    [1, 2, 4, 6]        [1, 3, 4, 6]                [2, 3, 4, 5]
    [1, 2, 5, 6]        [1, 3, 5, 6]                [2, 3, 4, 6]
    [1, 3, 4, 5]        [1, 4, 5, 6]                [2, 3, 5, 6]
    [1, 3, 4, 6]        [2, 3, 4, 5]                [2, 4, 5, 6]
    [1, 3, 5, 6]        [2, 3, 4, 6]                [3, 4, 5, 6]
    [1, 4, 5, 6]        [2, 3, 5, 6]                                
    [2, 3, 4, 5]        [2, 4, 5, 6]                                
    [2, 3, 4, 6]        [3, 4, 5, 6]                                
    [2, 3, 5, 6]           
    [2, 4, 5, 6]           
    [3, 4, 5, 6]        
    

包含1 and 2 and 3个的所有组合都已从想要的结果1中删除

包含1 and 2个的所有组合都已从想要的结果2中删除

我有一个更大的组合要计算,但它需要很多时间,我想减少使用这些排除的时间.

Tried solutions

在方法1中,仍然计算组合

使用方法2,我试图修改combinations function,但在计算之前,我找不到一个适当的方法来忽略我的排除列表.

            Method 1                    |               Method 2
                                        |               
def main():                             |   def combinations(iterable, r):
    l = list(range(1, 6))               |       pool = tuple(iterable)
    comb = combinations(l, 4)           |       n = len(pool)
                                        |       if r > n:
    for i in comb:                      |           return
        if set([1, 3]).issubset(i):     |       indices = list(range(r))
            continue                    |       yield tuple(pool[i] for i in indices)
        else                            |       while True:
            process()                   |           for i in reversed(range(r)):
                                        |               if indices[i] != i + n - r:
                                        |                   break
                                        |               else:
                                        |                   return
                                        |           indices[i] += 1
                                        |           for j in range(i+1, r):
                                        |               indices[j] = indices[j-1] + 1
                                        |           yield tuple(pool[i] for i in indices)

EDIT:

首先,感谢大家的帮助,我忘了提供更多关于约束的细节.

  • 输出的顺序不相关,例如,如果结果为[1, 2, 4, 5] [2, 3, 4, 5][2, 3, 4, 5] [1, 2, 4, 5],则不重要.

  • 组合的元素应该(如果可能)排序为[1, 2, 4, 5] [2, 3, 4, 5],而不是[2, 1, 5, 4] [3, 2, 4, 5],但这并不重要,因为组合可以在之后排序.

  • 排除列表是组合together中包含的所有项目的列表.e、 g如果我的排除列表是(1, 2, 3),则不应计算包含1 and 2 and 3的所有组合.但是,允许使用1 and 2 and not 3的组合.在这种情况下,如果我排除包含(1, 2)(1, 2, 3)的组合,它是完全无用的,因为所有将被(1, 2, 3)过滤的组合都已经被(1, 2)过滤了

  • Multiple exclude lists必须是可能的,因为我对我的组合使用了多个约束.

Tested answers

@托比亚斯·k

@Kasramvd和@mikuszefski

谢谢

推荐答案

(事实证明,我的previous answer分并不能真正满足问题的约束条件,这里是另一个.我将此作为一个单独的答案发布,因为方法大不相同,原始答案可能仍有助于其他人.)

您可以递归地实现这一点,每次在递归之前向组合添加另一个元素,判断这是否会违反排除集之一.允许两个以上的组合(不包括(1,3), (1,5)个以上的组合,类似于(2,4,5)的组合)并排除所有无效组合.

def comb_with_excludes(lst, n, excludes, i=0, taken=()):
    if n == 0:
        yield taken  # no more needed
    elif i <= len(lst) - n:
        t2 = taken + (lst[i],)  # add current element
        if not any(e.issubset(t2) for e in excludes):
            yield from comb_with_excludes(lst, n-1, excludes, i+1, t2)
        if i < len(lst) - n:  # skip current element
            yield from comb_with_excludes(lst, n, excludes, i+1, taken)

例子:

>>> lst = [1, 2, 3, 4, 5, 6]
>>> excludes = [{1, 3}, {1, 5}, {2, 4, 5}]
>>> list(comb_with_excludes(lst, 4, excludes))
[[1, 2, 4, 6], [2, 3, 4, 6], [2, 3, 5, 6], [3, 4, 5, 6]]

我现在已经计时了,结果证明这比在生成器表达式中使用itertools.combination和过滤器要慢得多,就像你已经做的那样:

def comb_naive(lst, r, excludes):
    return (comb for comb in itertools.combinations(lst, r)
                 if not any(e.issubset(comb) for e in excludes))

在Python中计算组合只比使用库(可能是用C实现的)然后过滤结果慢.根据可以排除的组合数量,在某些情况下,这might个组合可能会更快,但老实说,我有我的怀疑.

如果你能用itertools.combinations来处理子问题,比如Kasramvd's answer,你可能会得到更好的结果,但是对于多个非间断排除集,这就更难了.一种方法可能是将列表中的元素分为两组:有约束的元素和没有约束的元素.然后,两者都使用itertoolc.combinations,但只判断约束对那些重要元素组合的影响.你仍然需要判断和过滤结果,但只是其中的一部分.(不过有一点需要注意:结果不是按顺序生成的,而且生成的组合中元素的顺序也有点混乱.)

def comb_with_excludes2(lst, n, excludes):
    wout_const = [x for x in lst if not any(x in e for e in excludes)]
    with_const = [x for x in lst if     any(x in e for e in excludes)]
    k_min, k_max = max(0, n - len(wout_const)), min(n, len(with_const))
    return (c1 + c2 for k in range(k_min, k_max)
                    for c1 in itertools.combinations(with_const, k)
                    if not any(e.issubset(c1) for e in excludes)
                    for c2 in itertools.combinations(wout_const, n - k))

这已经比递归纯Python解决方案好得多,但仍然不如上面示例中的"朴素"方法好:

>>> lst = [1, 2, 3, 4, 5, 6]
>>> excludes = [{1, 3}, {1, 5}, {2, 4, 5}]
>>> %timeit list(comb_with_excludes(lst, 4, excludes))
10000 loops, best of 3: 42.3 µs per loop
>>> %timeit list(comb_with_excludes2(lst, 4, excludes))
10000 loops, best of 3: 22.6 µs per loop
>>> %timeit list(comb_naive(lst, 4, excludes))
10000 loops, best of 3: 16.4 µs per loop

然而,结果在很大程度上取决于输入.对于一个更大的列表,约束只适用于其中的几个元素,这种方法实际上比简单的方法更快:

>>> lst = list(range(20))
>>> %timeit list(comb_with_excludes(lst, 4, excludes))
10 loops, best of 3: 15.1 ms per loop
>>> %timeit list(comb_with_excludes2(lst, 4, excludes))
1000 loops, best of 3: 558 µs per loop
>>> %timeit list(comb_naive(lst, 4, excludes))
100 loops, best of 3: 5.9 ms per loop

Python-3.x相关问答推荐

只有在Chrome尚未打开的情况下,打开Chrome后,PySimpleGUI窗口才会崩溃

如何将CSV或FDF数据解析到Python词典并注入到模板PDF表单中?

网站抓取:当我使用Chrome DevTools中的网络选项卡时,找不到正确的URL来提供我想要的数据

具有多个值的极轴旋转和熔化/取消旋转(反转旋转)操作(Pandas 堆叠/取消堆叠交替/UDF覆盖)

在BaseHTTPRequestHandler中填充和返回列表

Python中根据分组/ID对两个数据框进行映射,以更接近值的升序排列

在 string.find() 条件下加入两个 Dataframes

如何转置和 Pandas DataFrame 并命名新列?

Python3:是否可以将变量用作函数调用的一部分

numpy是如何添加@运算符的?

是否可以将多个 if 转换为数组?

是否将dict转换为一个数据帧,每个值都有重复的键?

在初始化之前禁用`__setattr__`的干净方法

multiprocessing.Queue 中的 ctx 参数

Python图例属性错误

如何找出从哪个模块导入名称?

TypeError: write() 参数必须是 str,而不是字节(Python 3 vs Python 2)

如何为 Python 3.x 安装 psycopg2?

如何对字典的函数输出列表进行单元测试?

通过字典有效地替换Pandas 系列中的值