Тестирование всех комбинаций в Python - PullRequest
5 голосов
/ 05 февраля 2011

У меня есть два набора опций:

optionList1 = [a1,a2,a3,...,an]
optionList2 = [b1,b2,b3,...,bn]

Количество элементов в списках опций не обязательно равно, и мне приходится выбирать из первого списка опций дваждыКак мне убедиться, что я перепробовал каждую комбинацию из 2 вариантов из первого списка и одного из второго списка.Пример выбора установлен ниже ...

selectedOptions = [an1,an2,bn]

Ответы [ 5 ]

6 голосов
/ 05 февраля 2011

Объедините product и permutations из itertools, если вы не хотите дубликатов из первого списка:

>>> from itertools import product,permutations
>>> o1 = 'a1 a2 a3'.split()
>>> o2 = 'b1 b2 b3'.split()
>>> for (a,b),c in product(permutations(o1,2),o2):
...     print a,b,c
... 
a1 a2 b1
a1 a2 b2
a1 a2 b3
a1 a3 b1
a1 a3 b2
a1 a3 b3
a2 a1 b1
a2 a1 b2
a2 a1 b3
a2 a3 b1
a2 a3 b2
a2 a3 b3
a3 a1 b1
a3 a1 b2
a3 a1 b3
a3 a2 b1
a3 a2 b2
a3 a2 b3
6 голосов
/ 05 февраля 2011

Если вы не хотите дублировать записи из списка1, вот генератор, который вы можете использовать для перебора всех комбинаций:

def combinations(list1, list2):
    return ([opt1, opt2, opt3]
            for i,opt1 in enumerate(list1)
            for opt2 in list1[i+1:]
            for opt3 in list2)

Это, однако, не выбирает одинаковые опции из списка1 в разных порядках. Если вы хотите получить [a1, a2, b1] и [a2, a1, b1], вы можете использовать:

def combinations(list1, list2):
    return ([opt1, opt2, opt3]
            for opt1 in list1
            for opt2 in list1
            for opt3 in list2 if opt1 != opt2)
6 голосов
/ 05 февраля 2011

Для этого вы можете использовать itertools.product . Возвращает все возможные комбинации.

Например

for a1, a2, b in itertools.product(optionlist1,optionlist1,optionlist2):
    do_something(a1,a2,b)

Это даст «двойники» как [a1, a1, b2] и [a2, a3, b2], [a3, a2, b2]. Вы можете исправить это с помощью фильтра. Следующее предотвращает любые удвоения *:

for a1,a2,b in itertools.ifilter(lambda x: x[0]<x[1], itertools.product(optionlist1,optionlist1,optionlist2)):
    do_something(a1,a2,b)

(*) Это предполагает, что параметры имеют некоторое естественное упорядочение, которое будет иметь место со всеми примитивными значениями.

shang 's ответ тоже очень хорошо. Я написал код для их сравнения:

from itertools import ifilter, product
import random
from timeit import repeat

def generator_way(list1, list2):
    def combinations(list1, list2):
        return ([opt1, opt2, opt3]
                for i,opt1 in enumerate(list1)
                for opt2 in list1[i+1:]
                for opt3 in list2)
    count = 0
    for a1,a2,b in combinations(list1,list2):
        count += 1

    return count

def itertools_way(list1,list2):
    count = 0
    for a1,a2,b in ifilter(lambda x: x[0] < x[1], product(list1,list1,list2)):
        count += 1
    return count

list1 = range(0,100)
random.shuffle(list1)
list2 = range(0,100)
random.shuffle(list2)

print sum(repeat(lambda: generator_way(list1,list2),repeat = 10, number=1))/10
print sum(repeat(lambda: itertools_way(list1,list2),repeat = 10, number=1))/10

И результат:

0.189330005646
0.428138256073

Так что генераторный метод быстрее. Однако скорость это еще не все. Лично я нахожу свой код «чище», но выбор за вами!

(Кстати, они дают оба одинаковых числа, поэтому оба одинаково верны.)

3 голосов
/ 05 февраля 2011

Звучит так, будто ты ищешь itertools.product()

>>> options_a = [1,2]
>>> options_b = ['a','b','c']
>>> list(itertools.product(options_a, options_a, options_b))
[(1, 1, 'a'),
 (1, 1, 'b'),
 (1, 1, 'c'),
 (1, 2, 'a'),
 (1, 2, 'b'),
 (1, 2, 'c'),
 (2, 1, 'a'),
 (2, 1, 'b'),
 (2, 1, 'c'),
 (2, 2, 'a'),
 (2, 2, 'b'),
 (2, 2, 'c')]
2 голосов
/ 05 февраля 2011

Один из способов - использовать itertools.product .

for x, y, z in itertools.product(optionlist1,optionlist1,optionlist2):
    print x,y,z
...