[파이썬] from itertools
파이썬의 이터툴즈 중 조합형 이터레이터에 대해 알아보겠습니다.
조합형 이터레이터는 다음 4가지가 있습니다.
product(),
permutations(),
combinations(),
combinations_with_replacement()
가장 익숙한 순열부터 보도록 하죠.
permutations(순열)
고등학교 교육과정에서 배운 개념.
'순'열에는 '순'서가 있습니다.
from itertools import permutations
print(tuple(permutations((1, 2, 3))))
# ((1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1))
3가지 원소를 1열로 세운다면...
모든 경우의 수는 3! 이죠. 3 * 2 * 1
3가지 원소중 2개만 뽑아 1열로 세운다면..
모든 경우의 수는 3 * 2입니다.
print(tuple(permutations((1, 2, 3), 2)))
# ((1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2))
combinations(조합)
마찬가지로 고등학교 교육과정에 있는 내용이죠.
from itertools import combinations
print(tuple(combinations((1, 2, 3))))
# TypeError: combinations() missing required argument 'r' (pos 2)
두 번째 인자를 입력하지 않으면 에러가 나옵니다.
조합에는 순서가 없습니다.
3 종류 원소가 있을 때 순서 없이 (=순서를 무시하고) 3개를 뽑을 때 모든 경우의 수는 1입니다.
print(tuple(combinations((1, 2, 3), 3)))
# ((1, 2, 3),)
그래서 두 번째 인자가 없으면 에러가 발생하도록 만들었나 봅니다.
3가지 요소 중 순서 없이 2개를 뽑는 모든 경우의 수
print(tuple(combinations((1, 2, 3), 2)))
# ((1, 2), (1, 3), (2, 3))
정상적으로 사용하면 이런 결과가 나옵니다.
n! / (r! * (n-r)!)
product
from itertools import product
print(tuple(product((1, 2, 3))))
# ((1,), (2,), (3,))
이건 뭘까요?
다중 for문을 연상하면 됩니다.
print(tuple(product((1, 2, 3), ('a', 'b'))))
# ((1, 'a'), (1, 'b'), (2, 'a'), (2, 'b'), (3, 'a'), (3, 'b'))
for num in (1, 2, 3):
for letter in ('a', 'b'):
print(num, letter)
# 1 a
# 1 b
# 2 a
# 2 b
# 3 a
# 3 b
이런 걸 데카르트 곱(cartesian product)이라고 합니다.
print(tuple(product((1, 2, 3), (1, 2, 3))))
# ((1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3))
product를 중복 순열이라고도 합니다.
from itertools import product
print(tuple(product((0, 1, 2), (0, 1, 2), (0, 1, 2))))
print(tuple(product((0, 1, 2), repeat=3))) # 위와 같다.
((0, 0, 0), (0, 0, 1), (0, 0, 2), (0, 1, 0), (0, 1, 1), (0, 1, 2), (0, 2, 0), (0, 2, 1), (0, 2, 2), (1, 0, 0), (1, 0, 1), (1, 0, 2), (1, 1, 0), (1, 1, 1), (1, 1, 2), (1, 2, 0), (1, 2, 1), (1, 2, 2), (2, 0, 0), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 1), (2, 1, 2), (2, 2, 0), (2, 2, 1), (2, 2, 2))
combinations_with_replacement
from itertools import combinations_with_replacement
print(tuple(combinations_with_replacement((1, 2, 3))))
# TypeError: combinations_with_replacement() missing required argument 'r' (pos 2)
에러가 나옵니다.
그럼 이렇게....
print(tuple(combinations_with_replacement((1, 2, 3), 2)))
# ((1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3))
이 결과를 다음(프로덕트)과 비교해 보시면 뭔지 아실 수 있을 겁니다.
print(tuple(product((1, 2, 3), (1, 2, 3))))
# ((1, 1), (1, 2), (1, 3), (2, 1), (2, 2), (2, 3), (3, 1), (3, 2), (3, 3))
둘을 비교해 보면
combinations_with_replacement는
순서가 없는 (순서를 무시한) 프로덕트란 것을 알 수 있습니다.
중복조합이라고도 합니다.
프로덕트는 for문과 같다고 했습니다.
같은 배열로 여러 번 for를 돌렸을 때,
결과물의 순서가 없다면
이런 결과가 나오는 것이죠.
왜 두 번째 인수가 필요한 지도
알 수 있습니다.
------------------------------------------
어떤 그룹의 물건을 n회 뽑아서
가격표를 만들어야 할 때
'combinations_with_replacement'를
이용할 수 있습니다.
다중 for 문을 이용하면 겹치는 경우가 많이 발생합니다.
group_a = dict(x=0, a=360, b=170, c=57)
for key1, value1 in group_a.items():
for key2, value2 in group_a.items():
print(key1, key2, value1 + value2)
x x 0
x a 360
x b 170
x c 57
a x 360
a a 720
a b 530
a c 417
b x 170
b a 530
b b 340
b c 227
c x 57
c a 417
c b 227
c c 114
combinations_with_replacement를 이용하면,
연산 횟수가 16회에서 10회로 줍니다.
from itertools import combinations_with_replacement
group_a = dict(x=0, a=360, b=170, c=57)
for (key1, value1), (key2, value2) in combinations_with_replacement(group_a.items(), 2):
print(key1, key2, value1 + value2)
x x 0
x a 360
x b 170
x c 57
a a 720
a b 530
a c 417
b b 340
b c 227
c c 114
------------------------------------------
딕셔너리를
group_a = {'x': 0, 'a': 360, 'b': 170, 'c': 57}
이렇게 사용하는 게 일반적이지만,
작은따옴표의 압박이 있을 때는
group_a = dict(x=0, a=360, b=170, c=57)
이것도 좋습니다.