Как пропустить некоторые итерации при использовании itertools.product?

#python #for-loop #iteration #itertools

Вопрос:

Предположим, что существует три отсортированных списка: A, B, C.

 A = [1, 2, 3, 4] B = [3, 4, 5] C = [2, 3, 4]  

Я использую itertools.product , чтобы найти все возможные комбинации, сумма которых меньше 10.

Если у меня есть только три списка, я буду использовать следующий код.

А = [1, 2, 3, 4] B = [3, 4, 5] C = [2, 3, 4]

 for a in A:  for b in B:  for c in C:  if a   b   c lt; 10:  print(a, b, c)  else:  break  

Здесь каждый список отсортирован, и поэтому я использовал break для повышения эффективности.

Но когда я использую itertools.product, то как я использую перерыв? Я имею в виду, как перейти к непосредственно конкретной итерации (например, a = 3, b = 3, c = 3)?

 for a, b, c in itertools.product(A, B, C):  ....?  

Комментарии:

1. Используйте continue для перехода к следующей итерации.

2. @ddejohn Я не думаю, что это то, чего хочет ОП. Он/она хочет пропустить несколько итераций.

3. А, теперь я понимаю. ОП, с помощью этого невозможно сделать то, что вы хотите itertools.product .

Ответ №1:

Вы можете попробовать следующее:

 from itertools import product, dropwhile  A = [1, 2, 3, 4]  B = [3, 4, 5]  C = [2, 3, 4]  for a, b, c in dropwhile(lambda x: x != (3,3,3), product(A, B, C)):  print(a, b, c)  

Это дает:

 3 3 3 3 3 4 3 4 2 3 4 3 3 4 4 . . .  

Обратите внимание, что на самом деле это не относится непосредственно к данной итерации. Вместо itertools.dropwhile этого запускает итератор до тех пор, пока не будет выполнено указанное условие, и только после этого начинает возвращать его значения.

Ответ №2:

Пропустить итерации невозможно itertools.product , но, учитывая, что списки отсортированы, можно сократить количество итераций, используя двоичный поиск и поиск элементов, которые меньше требуемой разницы, а также используя запоминание:

 import itertools import bisect   def bisect_fast(A, B, C, threshold):  seen_b_diff = {}  seen_c_diff = {}   for a in A:  b_diff = threshold - a  if b_diff not in seen_b_diff:  index = bisect.bisect_left(B, b_diff)  seen_b_diff[b_diff] = index   # In B we are only interested in items that are less than `b_diff`  for ind in range(seen_b_diff[b_diff]):  b = B[ind]  c_diff = threshold - (a   b)  # In `C` we are only interested in items that are less than `c_diff`  if c_diff not in seen_c_diff:  index = bisect.bisect_left(C, c_diff)  seen_c_diff[c_diff] = index   for ind in range(seen_c_diff[c_diff]):  yield a, b, C[ind]    def naive(A, B, C, threshold):  for a, b, c in itertools.product(A, B, C):  if a   b   c lt; threshold:  yield a, b, c  

Выход

 gt;gt;gt; from random import choice gt;gt;gt; A, B, C = [sorted([choice(list(range(1000))) for _ in range(250)]) for _ in range(3)] gt;gt;gt; list(naive(A, B, C, 1675)) == list(bisect_fast(A, B, C, 1675)) True gt;gt;gt; %timeit list(bisect_fast(A, B, C, 1675)) 1.59 s ± 32.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each) gt;gt;gt; %timeit list(naive(A, B, C, 1675)) 3.09 s ± 55.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)