#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)