ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [파이썬] from itertools
    Python/파이썬 자료구조 알고리듬 2021. 11. 8. 14:09
    반응형

    파이썬의 이터툴즈 중 조합형 이터레이터에 대해 알아보겠습니다. 

    조합형 이터레이터는 다음 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)

    이것도 좋습니다. 

    반응형
Designed by Tistory.