여러 항목에서 몇몇으로 구성된 조합, 곱집합이 알고 싶다면?
조합(組合, Combination)
서로 다른 n개의 원소를 가지는 어떤 집합 S에서 k개를 선택하여 조를 만드는 것.
위키피디아
곱집합(Product Set), 데카르트곱(Cartesian Product)
A1,A2를 임의의 집합이라 할 때, a∈A1인 원소 a를 첫째 원소로 하고, b∈A2인 b를 둘째 원소로 하는 모든 순서쌍(a,b)의 집합을 A1과 A2의 곱집합이라 하고 A1×A2로 나타낸다.
두산백과
코딩을 하다 보면 여러 값의 조합으로 case를 만들고 for문을 돌리면서 계산하거나 분석해야 할 일이 생기기도 한다.
이번에도 마찬가지로 여러가지 값에 대한 곱집합이 필요했다. 내가 가진 문제는 3가지 List가 존재하고 3가지 List에서 각각 1개 씩 뽑아 3가지 List의 값으로 구성되는 조합의 모든 경우의 수를 확인 하는 것이었다.
list_a = [1, 2, 3]
list_b = ['a', 'b']
list_c = [2021, 2022]
result = [(1, 'a', 2021), (1, 'a', 2022), (1, 'b', 2021), (1, 'b', 2022), (2, 'a', 2021), (2, 'a', 2022), (2, 'b', 2021), (2, 'b', 2022), (3, 'a', 2021), (3, 'a', 2022), (3, 'b', 2021), (3, 'b', 2022)]
해당 결과 손으로 작성해도 어렵지 않지만, 리스트의 원소들이 많아지면 복잡해지거나 손으로 작성하다 틀리는 경우가 있다. 이럴 때는 파이썬에서 제공하는 표준 라이브러리 itettools
를 이용하는 것이 편리하고 정확하다.
from itertools import product
result = product(list_a, list_b, list_c)
print(result)
를 한다면 <itertools.product object at 0x000020D5....>
라고 뜨면서 iterator를 반환해준다.
이럴 때 list()
로 감싸주면 정상적으로 출력 된다.
from itertools import product
result = list(product(list_a, list_b, list_c))
print(result)
>> [(1, 'a', 2021), (1, 'a', 2022), (1, 'b', 2021), (1, 'b', 2022), (2, 'a', 2021), (2, 'a', 2022), (2, 'b', 2021), (2, 'b', 2022), (3, 'a', 2021), (3, 'a', 2022), (3, 'b', 2021), (3, 'b', 2022)]
하지만, 이렇게 한다면 매번 List를 정의하여 써야 하며, List의 이름을 변수의 종류나 특정 값으로 List의 이름을 정하기가 힘들어 진다.(물론 방법은 있다.)
이럴 때 내가 주로 사용하는 방법은 dictionary를 활용하는 것이다.
dict_for_product = {}
를 선언해두면 다음에 key, value 값만 지정해주면 능동적으로 변수를 생성해서 쓸 수 있고, 변수들의 특성에 따라 하나의 dictionary로 관리할 수 있으므로, 사후 관리가 편리해지는 장점을 가지게 된다.
dict_for_product['list_a'] = [1, 2, 3]
dict_for_product['list_b'] = ['a', 'b']
dict_for_product['list_c'] = [2021, 2022]
print(dict_for_product)
>> {'list_a': [1, 2, 3], 'list_b': ['a', 'b'], 'list_c': [2021, 2022]}
그럼 dict_for_product라는 dictionary를 product 해보면, 아래와 같이 key값만 튜플로 나타내준다.
from itertools import product
list(product(dict_for_product))
>> [('list_a',), ('list_b',), ('list_c',)]
그렇다면 어떻게 해야 할 것인가?
아래와 같이 value를 변수로 받고 value들의 곱집합을 하는 함수를 하나 만들자.
from itertools import product
def product_dict(**kwargs):
vals = kwargs.values()
for instance in product(*vals):
yield instance
이와 같이 만들고 아래와 같이 사용하면 우리가 원하는 결과 값을 가져올 수 있다.
list(product_dict(**dict_for_product))
>> [(1, 'a', 2021),
(1, 'a', 2022),
(1, 'b', 2021),
(1, 'b', 2022),
(2, 'a', 2021),
(2, 'a', 2022),
(2, 'b', 2021),
(2, 'b', 2022),
(3, 'a', 2021),
(3, 'a', 2022),
(3, 'b', 2021),
(3, 'b', 2022)]
하지만 또 각 튜플에서 원소들이 dictionary의 어느 리스트 값을 가져왔는지 궁금할 때가 있다.
이럴 땐 key와 value를 변수로 받고 zip으로 묶어주는 함수를 만들고 사용하면 된다.
from itertools import product
def product_dict(**kwargs):
keys = kwargs.keys()
vals = kwargs.values()
for instance in product(*vals):
yield dict(zip(keys, instance))
list(product_dict(**dict_for_product))
>> [{'list_a': 1, 'list_b': 'a', 'list_c': 2021},
{'list_a': 1, 'list_b': 'a', 'list_c': 2022},
{'list_a': 1, 'list_b': 'b', 'list_c': 2021},
{'list_a': 1, 'list_b': 'b', 'list_c': 2022},
{'list_a': 2, 'list_b': 'a', 'list_c': 2021},
{'list_a': 2, 'list_b': 'a', 'list_c': 2022},
{'list_a': 2, 'list_b': 'b', 'list_c': 2021},
{'list_a': 2, 'list_b': 'b', 'list_c': 2022},
{'list_a': 3, 'list_b': 'a', 'list_c': 2021},
{'list_a': 3, 'list_b': 'a', 'list_c': 2022},
{'list_a': 3, 'list_b': 'b', 'list_c': 2021},
{'list_a': 3, 'list_b': 'b', 'list_c': 2022}]
자, 이제 원하는 변수를 프로그램 내에서 직접 생성하고(dictionary 안에) 조합까지 생성 할 수 있다.
source: stackoverflow.com