Python itertools를 이용한 순열, 조합, 중복 순열, 중복 조합
02 Mar 2021알고리즘 문제를 풀다 보면 순열이나 조합을 사용해야 할 때가 자주 있다. 이를 구현하기 위해서는 보통 재귀함수를 사용해야 하며, 반복문으로 구현할 시 좀 복잡한 인덱스 계산이 필요하다. 하지만 python에는 표준 라이브러리에 함수형 프로그래밍 분류로 itertools
가 있는데, 여기에 순열과 조합 함수가 구현되어 있다. 속도도 빠르므로 알고 있기만 한다면 사용하는 편이 좋다.
필자는 항상 까먹고 찾아보기 때문에 공식 문서 내용과 간단한 백준 예제를 함께 정리한다.
🔷 순열(Permutation)
🔹 itertools.permutations(iterable, r=None)
iterable
에서 요소의 연속된 길이 r 순열을 반환
r이 지정되지 않았거나 None
이면, r의 기본값은 iterable
의 길이
입력 iterable
이 정렬되어 있으면, 순열 튜플이 정렬된 순서로 생성
>>> permutations('ABCD', 2)
AB AC AD BA BC BD CA CB CD DA DB DC
>>> permutations(range(3))
012 021 102 120 201 210
🔹 BOJ 15649번 - N과 M (1)
from itertools import permutations
N, M = map(int, input().rstrip().split())
for p in list(map(" ".join, permutations(map(str, range(1, N+1)),M))):
print(p)
🔷 조합(combination)
🔹 itertools.combinations(iterable, r)
내용은 permutations
와 같음
단, 요소는 값이 아니라 위치로 고유성을 다룬다(값이 같더라도 위치가 다르면 다른 요소로 판단).
>>> combinations('ABCD', 2)
AB AC AD BC BD CD
>>> combinations(range(4), 3)
012 013 023 123
>>> combinations('ABCA', 2)
AB AC AA BC BA CA
🔹 BOJ 15650번 - N과 M (2)
from itertools import combinations
N, M = map(int, input().rstrip().split())
for p in list(map(" ".join, combinations(map(str, range(1, N+1)),M))):
print(p)
🔷 중복순열(permutation with repetition)
n 개의 서로 다른 원소 중에서 중복을 허용하여 r개를 뽑아서 한 줄로 나열하는 경우의 수
r 개를 선택하는 경우, 최초에 n 개를 선택할 수 있고 이후에도 계속 n 개를 선택할 수 있기 때문에 이 순열의 개수는 n^r
🔹 itertools.product(*iterables, repeat=1)
곱(product) 튜플이 정렬된 순서로 반환
원래는 데카르트 곱(Cartesian Product)을 위한 함수로 사용하지만(이름도 product) repeat
키워드 인자를 사용하여 반복 횟수를 지정하면 iterable
자신과의 곱을 할 수 있다.
>>> product('ABCD', 'xy')
Ax Ay Bx By Cx Cy Dx Dy
>>> product(range(2), repeat=3) # product(range(2), range(2), range(2))와 동일
000 001 010 011 100 101 110 111
🔹 BOJ 15651번 - N과 M (3)
from itertools import product
N, M = map(int, input().rstrip().split())
for p in list(map(" ".join, product(map(str, range(1, N+1)),M))):
print(p)
참고로 아래는 재귀로 구현한 코드
from sys import stdout
def main(permutation=[]):
if len(permutation) == M:
stdout.write(" ".join(permutation) + "\n")
permutation.pop()
return permutation
for i in range(N):
permutation.append(num_li[i])
permutation = main(permutation)
if permutation:
permutation.pop()
return permutation
if __name__ == "__main__":
N, M = map(int, input().rstrip().split())
num_li = []
num_li = list(map(str, range(1, N+1)))
main()
🔷 중복조합(combination with repetition)
서로 다른 n개의 원소에서 중복을 허락하여 k개를 뽑는 경우의 수
공식은 위 링크에서 참고
🔹 itertools.combinations_with_replacement(iterable, r)
combinations
와 같지만, 개별 요소를 두 번 이상 반복할 수 있음
>>> combinations_with_replacement('ABC', 2)
AA AB AC BB BC CC
>>> combinations_with_replacement('ABA', 2)
AA AB AA BB BA AA
🔹 BOJ 15652번 - N과 M (4)
from sys import stdout
from itertools import combinations_with_replacement
N, M = map(int, input().rstrip().split())
for c in map(" ".join, combinations_with_replacement(map(str, range(1,N+1)), M)):
stdout.write(c + "\n")
아래는 재귀로 구현한 코드
def main(combi=[], start_i=0):
if len(combi) == M:
stdout.write(" ".join(combi) + "\n")
combi.pop()
return combi
for i in range(start_i, N):
combi.append(num_li[i])
combi = main(combi, i)
if combi:
combi.pop()
return combi
if __name__ == "__main__":
N, M = map(int, input().rstrip().split())
num_li = []
num_li = list(map(str, range(1, N+1)))
main()
REFERENCE
https://docs.python.org/ko/3/library/itertools.html#itertool-functions