(프로그래머스 42890) 후보키

ps  · 3 mins read

이해

  • 관계 데이터베이스에서 Relation의 Tuple을 유일하게 식별할 수 있는 속성(Attribute) 또는 속성의 집합 중, 아래 두 성질을 만족하는 것을 후보 키(Candidate Key)라고 한다.
    • 유일성(uniqueness): 릴레이션에 있는 모든 튜플에 대해 유일하게 식별되어야 한다.
    • 최소성(minimality): 유일성을 가진 키를 구성하는 속성 중 하나라도 제외하는 경우 유일성이 깨지는 것을 의미한다. 예를 들어, “학번” 속성 하나로 릴레이션에 있는 모든 튜플이 유일하게 식별된다고 할 때, [“학번”, “이름”]을 함께 사용해도 릴레이션의 모든 튜플을 유일하게 식별할 수 있지만, “이름” 속성을 제외하여도 “학번” 속성 하나로 유일성을 만족하기 때문에 [“학번”, “이름”]은 후보 키가 될 수 없다.
  • 릴레이션을 나타내는 문자열 배열 relation이 주어질 때, 후보 키의 개수를 return하라.

계획

  • 속성의 모든 조합에 대해 완전 탐색을 진행하여 각 조합이 후보키가 될 수 있는지를 확인한다.
  • 각 조합에 대해 유일성과 최소성을 체크한다.
  • 유일성 체크
    • 속성 조합에 해당하는 튜플들을 set에 모두 넣은 후 set의 원소 개수가 전체 튜플의 개수와 같은 지 확인한다.
  • 최소성 체크
    • 속성의 조합을 탐색하는 동안 속성의 조합이 이전의 속성의 조합들을 통해 구한 후보키들 중에서 아무것도 포함하지 않는 경우에 대해서만 탐색해준다.

실행

from itertools import combinations
def solution(relation):
    n, m = len(relation), len(relation[0])
    candidates = []
    for i in range(1, m+1):
        
        for comb in combinations(range(m), i):
            flag = True
            for candidate in candidates:
                if len(list(candidate - set(comb))) == 0:
                    flag = False
                    break
            if not flag:
                continue
            
            tuple_set = set()
            
            for record in relation:
                temp = []
                for c in comb:
                    temp.append(record[c])
                tuple_set.add(tuple(temp))
            if len(list(tuple_set)) == n:
                candidates.append(set(comb))
    return len(candidates)

개선

  • set을 list로 바꾸지 않고 len함수를 사용하여 set의 원소의 개수를 구한다.
  • 파이썬의 List Comprehension 기능을 사용하여 코드를 더 간단하게 만든다.
from itertools import combinations
def solution(relation):
    n, m = len(relation), len(relation[0])
    candidates = []
    for i in range(1, m+1):
        
        for comb in combinations(range(m), i):
            flag = True
            for candidate in candidates:
                if len(candidate - set(comb)) == 0:
                    flag = False
                    break
            if not flag:
                continue
            tuple_set = set([tuple([record[c] for c in comb]) for record in relation])

            if len(tuple_set) == n:
                candidates.append(set(comb))
    return len(candidates)

회고

  • 구현하는 과정이 살짝 까다로웠고, for문을 여러 개 중첩시켜서 구현하다 보니 제대로 풀고 있는 지 의구심이 들었다. 하지만 매개변수인 문자열 배열 relation의 column과 row길이가 아주 작으므로 for문 3개 정도 중첩하는 것은 크게 걱정하지 않아도 됐었다. 자신있게 풀어야 겠다.

  • set에 원소로 배열을 넣었더니 다음 아래와 같은 error가 출력됐다. TypeError: unhashable type: ‘list’

    • 파이썬에서 set에 포함되는 원소들은 해시 가능해야 하고, list는 mutable하기 때문에 해싱이 불가능하다. 따라서 list를 tuple로 변경한 후에 집합에 포함시켜주면 된다.