2011-02-05 3 views
5

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

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

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

selectedOptions = [an1,an2,bn] 
+1

извините, но я немного не понимаю, что вы пытаетесь сделать. Вам нужна таблица всех возможных комбинаций элементов, где ни один из них не из одного списка? Должен ли какой-нибудь узел когда-либо быть Нет? – fncomp

+0

Я предполагаю, что я медленно понял, что вы хотели. – fncomp

ответ

6

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

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

Это, однако, не выбрать те же параметры из list1 в разных порядках. Если вы хотите получить как [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) 
+2

В терминах python генератор - это то, что использует 'yield'. У этого есть недостаток хранения всего списка с опциями в памяти. –

+1

И, конечно, OP может вызвать это, когда первые два параметра являются одним и тем же списком. комбинаций (optionList1, optionList1, optionList2) на всякий случай, если это не понятно. – Spacedman

+1

Функция фактически возвращает выражение генератора, поэтому он не создает весь список в памяти. – shang

2

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

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

Это не пробует всех комбинаций. Вместо этого он сопоставляет индексы в списке, только возвращая (a1, a1, b1), (a2, a2, b2) .... (an, an, bn), если обе будут одинаковой длины. –

+0

@Peter.Да, я просто обновил его правильным ответом. Я нахожу, что вы указали правильный ответ. –

+0

Вам не нужно добавлять список с пустыми значениями. itertools.product() может обрабатывать списки (или итераторы) с разной длиной. –

6

Вы можете использовать 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) 

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

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

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 

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

(Btw, они дают как идентичные счетчики, так как одинаково правильно.)

3

Это звучит для меня, как вы ищете 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')] 
+0

Я не уверен, что понимаю: насколько я могу судить, это работает одинаково независимо от длины одного из этих списков. Что касается ответа на вопрос, трудно сказать, поскольку сам вопрос был несколько неоднозначным: ОП не уточнил, должны ли быть включены группы вариантов выбора с дублирующимися элементами из первого набора параметров. Из-за этой двусмысленности я не могу утверждать, что это * точно * решение проблемы ОП, но сказать, что оно «даже близко» кажется довольно экстремальным. –

6

Объединить 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 
+0

Хорошее решение !! –

+1

Очень приятно! Также обратите внимание, что вы можете заменить 'перестановки'' комбинациями', если вам не нужны повторяющиеся пары с различным порядком. – shang

+0

@shang, у меня было это изначально, но было непонятно, чего хотел OP. Я думал, что перестановки более вероятны. OP тоже может быть в порядке с дубликатами, тогда 'product (o1, o1, o2)' будет в порядке. –

Смежные вопросы