Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/283.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python—从大量组合中构建满足特定条件的子列表_Python_For Loop_Itertools - Fatal编程技术网

Python—从大量组合中构建满足特定条件的子列表

Python—从大量组合中构建满足特定条件的子列表,python,for-loop,itertools,Python,For Loop,Itertools,我已经读了很长时间了,这是我第一次没能找到我正在研究的问题的答案 我有一个93个字符串的列表,每个字符串都有6个字符长。从这93个字符串中,我想确定一组20个字符串,它们都符合集合中其他字符串的特定标准。虽然itertools.combinations将提供所有可能的组合,但并非所有条件都值得检查 例如,如果[list[0]、list[1]等失败,因为list[0]和list[1]不能在一起,那么不管其他18个字符串是什么,集合每次都会失败,这是一大堆浪费的检查 目前我有20个嵌套for循环,但

我已经读了很长时间了,这是我第一次没能找到我正在研究的问题的答案

我有一个93个字符串的列表,每个字符串都有6个字符长。从这93个字符串中,我想确定一组20个字符串,它们都符合集合中其他字符串的特定标准。虽然itertools.combinations将提供所有可能的组合,但并非所有条件都值得检查

例如,如果[list[0]、list[1]等失败,因为list[0]和list[1]不能在一起,那么不管其他18个字符串是什么,集合每次都会失败,这是一大堆浪费的检查

目前我有20个嵌套for循环,但似乎必须有更好/更快的方法

for n1 in bclist:
    building = [n1]
    n2bclist = [bc for bc in bclist if bc not in building]
    for n2 in n2bclist:              #this is the start of what gets repeated 19 times
        building.append(n2)
        if test_function(building): #does set fail? (counter intuitive, True when fail, False when pass)
            building.remove(n2)
            continue
        n3bclist = [bc for bc in bclist if bc not in building]
        #insert the additional 19 for loops, with n3 in n3, n4 in n4, etc
        building.remove(n2)
在第20个for循环中有print语句,用于在存在一组20时提醒我。for语句至少允许我在单次加法失败时提前跳过集合,但没有较大组合失败时的内存:

例如,
[list[0],list[1]]
失败,因此跳到通过的
[list[0],[list[2]]
。接下来是
[list[0],list[2],list[1]
失败,因为0和1再次在一起,所以它将移动到可能通过或不通过的
[list[0],list[2],list[3]
。我担心的是,它最终还会测试:

  • [列表[0]、列表[3]、列表[2]]
  • [列表[2],列表[0],列表[3]]
  • [列表[2],列表[3],列表[0]]
  • [列表[3]、列表[0]、列表[2]]
  • [列表[3],列表[2],列表[0]]
所有这些组合的结果都将与前面的组合相同。基本上,我用devil of itertools进行交易。组合测试我知道的所有集合组合失败是因为早期的值失败了,而devil of for循环将值的顺序视为一个因素,而我不关心它们的顺序。这两种方法都非常重要增加代码完成所需的时间


任何关于如何摆脱魔鬼的想法都将不胜感激。

使用您当前的方法,但也要跟踪索引,以便在您的内部循环中可以跳过您已经检查过的元素:

bcenum = list(enumerate(bclist))
for i1, n1 in bcenum:
    building = [n1]
    for i2, n2 in bcenum[i1+1:]:              #this is the start of what gets repeated 19 times
        building.append(n2)
        if test_function(building): #does set fail? (counter intuitive, True when fail, False when pass)
            building.remove(n2)
            continue
        for i3, n3 in bcenum[i2+1:]:
            # more nested loops
        building.remove(n2)
这将从
l
中选择基数
n
的子集,以便
test(subset)=False


它试图避免不必要的工作。但是,鉴于有120种方法可以从93个元素中选择20个元素,您可能需要重新考虑您的总体方法。

您可以利用问题的两个方面:

  • 秩序不重要
  • 如果
    test\u function(L)
    True
    L
    的任何子列表的
    test\u function
    也将为
    True
  • 您还可以通过处理索引0-92而不是
    list[0]
    -
    list[92]
    -来简化一些事情-只有在
    test\u函数
    中,我们才可能关心列表的内容

    下面的代码首先找到可行的对,然后是四对集、八对集和十六对集。最后它找到十六和四的所有可行组合,得到20个列表。但是有100000多个八对集,所以速度仍然太慢,我放弃了。可能你可以按照同样的方法做一些事情但是用
    itertools
    加快速度,但可能还不够

    target = range(5, 25)
    def test_function(L):
        for i in L:
            if not i in target:
                return True
    def possible_combos(A, B):
        """
        Find all possible pairings of a list within A and a list within B
        """
        R = []
        for i in A:
            for j in B:
                if i[-1] < j[0] and not test_function(i + j):
                    R.append(i + j)
        return R
    def possible_doubles(A):
        """
        Find all possible pairings of two lists within A
        """
        R = []
        for n, i in enumerate(A):
            for j in A[n + 1:]:
                if i[-1] < j[0] and not test_function(i + j):
                    R.append(i + j)
        return R
    # First, find all pairs that are okay
    L = range(92) 
    pairs = []
    for i in L:
        for j in L[i + 1:]:
            if not test_function([i, j]):
                pairs.append([i, j])
    
    # Then, all pairs of pairs
    quads = possible_doubles(pairs)
    print "fours", len(quads), quads[0]
    # Then all sets of eight, and sixteen
    eights = possible_doubles(quads)
    print "eights", len(eights), eights[0]
    sixteens = possible_doubles(eights)
    print "sixteens", len(sixteens), sixteens[0]
    
    # Finally check all possible combinations of a sixteen plus a four
    possible_solutions = possible_combos(sixteens, fours)
    print len(possible_solutions), possible_solutions[0]
    

    您的解决方案应该基于
    itertools.compositions
    ,因为这将解决订购问题;短路过滤相对容易解决

    递归解 让我们快速回顾一下如何实现
    组合
    工作;最简单的方法是采用嵌套for循环方法并将其转换为递归样式:

    def combinations(iterable, r):
        pool = tuple(iterable)
        for i in range(0, len(pool)):
            for j in range(i + 1, len(pool)):
                ...
                    yield (i, j, ...)
    
    转换为递归形式:

    def combinations(iterable, r):
        pool = tuple(iterable)
        def inner(start, k, acc):
            if k == r:
                yield acc
            else:
                for i in range(start, len(pool)):
                    for t in inner(i + 1, k + 1, acc + (pool[i], )):
                        yield t
        return inner(0, 0, ())
    
    应用过滤器现在很容易:

    def combinations_filterfalse(predicate, iterable, r):
        pool = tuple(iterable)
        def inner(start, k, acc):
            if predicate(acc):
                return
            elif k == r:
                yield acc
            else:
                for i in range(start, len(pool)):
                    for t in inner(i + 1, k + 1, acc + (pool[i], )):
                        yield t
        return inner(0, 0, ())
    
    让我们检查一下:

    >>> list(combinations_filterfalse(lambda t: sum(t) % 2 == 1, range(5), 2))
    [(0, 2), (0, 4), (2, 4)]
    
    迭代解 中列出的
    itertools.compositions
    的实际实现使用迭代循环:

    def combinations(iterable, r):
        pool = tuple(iterable)
        n = len(pool)
        if r > n:
            return
        indices = range(r)
        yield tuple(pool[i] for i in indices)
        while True:
            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)
    
    为了优雅地适应谓词,有必要对循环稍微重新排序:

    def combinations_filterfalse(predicate, iterable, r):
        pool = tuple(iterable)
        n = len(pool)
        if r > n or predicate(()):
            return
        elif r == 0:
            yield ()
            return
        indices, i = range(r), 0
        while True:
            while indices[i] + r <= i + n:
                t = tuple(pool[k] for k in indices[:i+1])
                if predicate(t):
                    indices[i] += 1
                elif len(t) == r:
                    yield t
                    indices[i] += 1
                else:
                    indices[i+1] = indices[i] + 1
                    i += 1
            if i == 0:
                return
            i -= 1
            indices[i] += 1
    
    比较 结果表明,递归解决方案不仅更简单,而且速度更快:

    In [33]: timeit list(combinations_filterfalse_rec(lambda t: False, range(20), 5))
    10 loops, best of 3: 24.6 ms per loop
    
    In [34]: timeit list(combinations_filterfalse_it(lambda t: False, range(20), 5))
    10 loops, best of 3: 76.6 ms per loop
    

    不重复元素。你认为
    组合
    在做什么。
    组合(范围(4),3)-->012013023 123
    顺序重要吗?例如
    [list[0],list[3],list[2]
    是否可能通过,但
    [list[2],list[3],list[0]]
    fail?@Stuart不,顺序不重要。您的两个示例都会失败,或者都会通过。@GP89对不起,我可能不够清楚。我的for循环目前模仿
    itertools.permutations
    这是一个问题,因为顺序不重要,所以会测试许多不需要测试的东西。
    itertools.compositions
    不重复元素,但将测试012和013,即使01是导致故障的原因,再次测试不需要测试的东西。目前为止喜欢这个解决方案。据我所知,这提供了非冗余测试(即不会同时测试[0,2,3,4]和[4,3,2,0]),并且不会浪费对故障集的测试(即[0,1]=失败,因此不测试[0,1,2])。仍在评估其他答案。进行了一些额外的改进,但这在合理的时间内完成了工作,并且对现有代码的更改最少。其他更改:
    if(len(building)+len(bcenum[i#::]<20:在测试功能测试之前添加中断
    而不是附加、测试和删除:
    如果测试功能(构建+[n#]):继续
    def combinations_filterfalse(predicate, iterable, r):
        pool = tuple(iterable)
        n = len(pool)
        if r > n or predicate(()):
            return
        elif r == 0:
            yield ()
            return
        indices, i = range(r), 0
        while True:
            while indices[i] + r <= i + n:
                t = tuple(pool[k] for k in indices[:i+1])
                if predicate(t):
                    indices[i] += 1
                elif len(t) == r:
                    yield t
                    indices[i] += 1
                else:
                    indices[i+1] = indices[i] + 1
                    i += 1
            if i == 0:
                return
            i -= 1
            indices[i] += 1
    
    >>> list(combinations_filterfalse(lambda t: sum(t) % 2 == 1, range(5), 2))
    [(0, 2), (0, 4), (2, 4)]
    >>> list(combinations_filterfalse(lambda t: t == (1, 4), range(5), 2))
    [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (2, 3), (2, 4), (3, 4)]
    >>> list(combinations_filterfalse(lambda t: t[-1] == 3, range(5), 2))
    [(0, 1), (0, 2), (0, 4), (1, 2), (1, 4), (2, 4)]
    >>> list(combinations_filterfalse(lambda t: False, range(5), 2))
    [(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
    >>> list(combinations_filterfalse(lambda t: False, range(5), 0))
    [()]
    
    In [33]: timeit list(combinations_filterfalse_rec(lambda t: False, range(20), 5))
    10 loops, best of 3: 24.6 ms per loop
    
    In [34]: timeit list(combinations_filterfalse_it(lambda t: False, range(20), 5))
    10 loops, best of 3: 76.6 ms per loop