coding tips [Python] Dictionary로 조합(Combination, Product Set, Cartesian Product) 만들기 Programming

[Python] Dictionary로 조합(Combination, Product Set, Cartesian Product) 만들기

Posted by

여러 항목에서 몇몇으로 구성된 조합, 곱집합이 알고 싶다면?

조합(組合, 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

Leave a Reply

Your email address will not be published. Required fields are marked *