본문 바로가기
Python/Data Structure

[Python] 리스트 안에서 순열(permutation)과 조합(combination) 활용하기

by 전봇대파괴자 2021. 2. 25.

 

하나 혹은 여러 개의 리스트가 주어졌을 때, 해당 리스트 내의 값들로 특정 경우의 수를 구해야 할 때가 있습니다. 다음과 같은 리스트가 있다고 해봅시다.

 

fruits_li=['사과', '배', '바나나']

 

이 중 2개의 과일을 고르는 수는 몇 가지일까요? 그렇게 어려운 문제처럼 보이진 않습니다. 하지만 리스트가 늘어나면 어떨까요? 

 

bread_li=['식빵', '소보루빵', '크림빵']
drink_li=['주스', '맥주', '요거트']

 

빵 하나, 음료수 하나, 과일 하나를 고른다고 하면 그 경우의 수는 몇 가지일까요? 생각만으로도 복잡해집니다. 다행히 파이썬에는 이런 상황을 위한 라이브러리 itertools가 존재합니다. itertools에는 여러 가지 내장함수가 존재합니다만, 우리가 오늘 사용해볼 것은 순열(permutation)조합(combination)입니다. 다음과 같이 라이브러리를 불러와 봅시다. 

 

from itertools import permutations, combinations

 

 

1. 단일 리스트 내 값들의 순열, 조합 구하기

아까 과일 리스트에서 2개의 과일을 살 때 몇 가지의 경우의 수가 있는지 구하려고 했었죠? 그럼 실제로 구해보겠습니다. 

 

from itertools import permutations, combinations

fruits_li=['사과', '배', '바나나']
bread_li=['식빵', '소보루빵', '크림빵']
drink_li=['주스', '맥주', '요거트']


# 각 리스트에서 N개만큼 뽑아 나열할 경우(순서 고려)
per_fruits=list(permutations(fruits_li, 2)) # N=2
print(per_fruits)

>> [('사과', '배'), ('사과', '바나나'), ('배', '사과'), ('배', '바나나'), ('바나나', '사과'), ('바나나', '배')]

 

보다시피 한 리스트 안에 튜플 형식으로 경우의 수들이 들어있는 것을 볼 수 있습니다. 어라, 그런데 ('사과', '배'), ('배', '사과')가 서로 다른 경우로 되어 있군요? ('사과', '바나나'), ('바나나', '사과')도 마찬가지고요. 이렇게 순서가 바뀐 경우를 고려하지 않으려면 순열이 아닌 조합(combination)을 사용해야 합니다. 다시 가보겠습니다. 

 

# 각 리스트 나열 시 N개만큼 뽑아 나열할 경우 출력(순서 고려하지 않음)
fruits_con=list(combinations(fruits_li, 2)) # N=2
print(fruits_con)

>> [('사과', '배'), ('사과', '바나나'), ('배', '바나나')]

 

경우의 수가 잘 뽑혔습니다. 하지만 조금 부족합니다. 사과를 두 개 살 수도 있고, 바나나를 두 개 살 수도 있는 거잖아요. 상식적으로 과일 가게에 과일이 딱 하나만 놓여있지는 않겠지요. 과일에 금칠이라도 해놓지 않는 한 말이죠. 이럴 경우에는 값이 중복되는 것을 허용하는 다른 내장함수 combinations_with_replacement를 사용해야 합니다. 

 

 

from itertools import permutations, combinations, combinations_with_replacement

# 각 리스트 나열 시 N개만큼 뽑아 나열할 경우 출력(순서 고려하지 않음, 중복 허용)
fruits_con2=list(combinations_with_replacement(fruits_li, 2)) # N=2
print(fruits_con2)

>> [('사과', '사과'), ('사과', '배'), ('사과', '바나나'), ('배', '배'), ('배', '바나나'), ('바나나', '바나나')]

 

모든 경우의 수가 출력되는 것을 볼 수 있습니다. 값들의 순서를 고려해야 한다면 permutation을, 그렇지 않다면 combinations를 사용하면 됩니다. 만약 combinations에서도 같은 값들이 중복되는 경우를 포함하고 싶다면 combinations_with_replacement를 쓰면 되겠습니다. 

 

 

2. 두 개 이상의 리스트에서 조합 구하기

이번에는 두 개 이상의 리스트에서 N개의 값들을 뽑는 방법을 알아보겠습니다. 위의 빵 리스트와 음료수 리스트에서 하나씩을 뽑는 경우를 찾아보겠습니다. 이럴 때는 또 다른 내장함수인 product를 사용합니다. 

 

from itertools import permutations, combinations, combinations_with_replacement, product

bread_li=['식빵', '소보루빵', '크림빵']
drink_li=['주스', '맥주', '요거트']

# 2개의 리스트로 만들 수 있는 경우의 수
two_li=[bread_li,drink_li]
two_con=list(product(*two_li))
print(two_con)


>> [('식빵', '주스'), ('식빵', '맥주'), ('식빵', '요거트'), ('소보루빵', '주스'), ('소보루빵', '맥주'), ('소보루빵', '요거트'), ('크림빵', '주스'), ('크림빵', '맥주'), ('크림빵', '요거트')]

 

보다시피 빵-음료수의 모든 조합이 나온 것을 볼 수 있습니다. 세 개의 리스트를 활용하는 경우도 이와 마찬가지입니다. 과일 하나, 빵 하나, 맥주 하나를 뽑는 경우는 다음과 같습니다.

 

# 3개의 리스트에서 구할 수 있는 모든 조합
all_li=[fruits_li,bread_li,drink_li]
all_con=list(product(*all_li))
print(all_con)

>> [('사과', '식빵', '주스'), ('사과', '식빵', '맥주'), ('사과', '식빵', '요거트'), ('사과', '소보루빵', '주스'), ('사과', '소보루빵', '맥주'), ('사과', '소보루빵', '요거트'), ('사
과', '크림빵', '주스'), ('사과', '크림빵', '맥주'), ('사과', '크림빵', '요거트'), ('배', '식빵', '주스'), ('배', '식빵', '맥주'), ('배', '식빵', '요거트'), ('배', '소보루빵', ' 
주스'), ('배', '소보루빵', '맥주'), ('배', '소보루빵', '요거트'), ('배', '크림빵', '주스'), ('배', '크림빵', '맥주'), ('배', '크림빵', '요거트'), ('바나나', '식빵', '주스'), (' 
바나나', '식빵', '맥주'), ('바나나', '식빵', '요거트'), ('바나나', '소보루빵', '주스'), ('바나나', '소보루빵', '맥주'), ('바나나', '소보루빵', '요거트'), ('바나나', '크림빵', ' 
주스'), ('바나나', '크림빵', '맥주'), ('바나나', '크림빵', '요거트')]