프로그래밍

교집합과 합집합

수학 시간에 배운 집합을
그대로 담을 수 있습니다.

학습 목표

  1. 중복이 없고 순서가 없는 자료 담기가 무엇인지 설명할 수 있다.
  2. 수학의 교집합·합집합을 그대로 옮기는 연산을 쓸 수 있다.
  3. 순서가 없는 자료를 순서대로 내놓으려면 무엇이 더 필요한지 설명할 수 있다.
  4. 같은 일을 반복으로 직접 짜 보고 자료 담기가 무엇을 대신해 주는지 비교할 수 있다.

무엇을 만드나

수학의 집합이 프로그램에도 있습니다.

주어진 문제

두 줄로 숫자들을 입력받아 두 집합의 교집합과 합집합을 구해 각각 작은 것부터 차례로 내놓는 프로그램을 만듭니다.

수학 시간에 배운 그대로입니다. 교집합은 양쪽에 다 있는 것, 합집합은 어느 한쪽에라도 있는 것입니다. 파이썬에는 집합이 들어 있습니다. 수학의 집합과 같은 성질을 가진 담는 방식입니다.

단계 여기서 답할 질문
1 요구 분석 무엇을 받고 어떤 순서로 내놓을 것인가
2 설계 집합으로 담으면 무엇이 저절로 해결되는가
3 구현 순서가 없는 것을 어떻게 순서대로 내놓는가
4 테스트 겹치는 것이 없거나 전부 겹칠 때는 어떻게 되는가

먼저 개발 문서 양식을 내려받으세요

읽기만 하지 말고 직접 채워 보세요. 단계마다 자기 답을 적어 두면 그 기록이 요구 사항 명세서이자 설계서가 됩니다. 답을 보기 전에 먼저 적어 봅니다.

요구 분석과 설계는 언어와 상관없습니다. 두 양식의 앞부분은 같고, 구현을 적는 자리만 언어에 맞춰 다릅니다. 수업에서 쓰는 언어에 맞는 것을 받으면 됩니다. 이 강의노트의 코드 예시는 파이썬으로 되어 있습니다.

1단계 · 무엇을 만들 것인가

「집합을 입력받는다」는 어떻게 받으라는 걸까요?

되물어야 할 것들

  • 몇 개인지 먼저 알려 주나요? 아니면 한 줄에 다 적나요
  • 같은 수가 두 번 들어오면? 집합에는 같은 것이 두 번 있을 수 없습니다
  • 어떤 순서로 내놓나요? 들어온 순서인가요, 작은 것부터인가요
  • 교집합이 비면? 아무것도 안 내놓나요
  • 한 줄에 내놓나요? 사이를 무엇으로 띄우나요

정리한 요구 사항

  1. 두 줄로 받는다. 한 줄에 정수 여러 개를 공백으로 띄운다. 개수는 알려 주지 않는다.
  2. 같은 수가 여러 번 들어와도 하나로 친다.
  3. 첫 줄에 교집합, 둘째 줄에 합집합을 내놓는다.
  4. 둘 다 작은 것부터 차례로, 공백으로 띄워 한 줄에 내놓는다.
  5. 교집합이 비면 빈 줄을 내놓는다.

4번이 없으면 답이 사람마다 다릅니다

집합에는 원래 순서가 없습니다. {1, 2, 3}과 {3, 1, 2}는 같은 집합입니다. 그런데 화면에 내놓으려면 어떻게든 순서를 정해야 합니다. 순서를 요구 사항에 적지 않으면 실행할 때마다, 또는 사람마다 다른 순서가 나와도 틀렸다고 할 수가 없습니다. 순서가 없는 것도 화면에 내놓으려면 순서를 정해야 합니다.

스스로 확인

요구 사항 2번(같은 수는 하나로 친다)을 따로 적어 둔 이유가 무엇일까요? 집합으로 담기만 하면 저절로 되는데도 적어 둔 까닭을 생각해 보세요.

2단계 · 담는 방식이 일을 대신해 준다

집합으로 담으면 두 가지가 공짜로 됩니다.

토막 무엇을 정하는가
입력 두 줄, 각 줄에 정수 여러 개
처리 집합으로 담아 교집합과 합집합을 구하고, 순서대로 늘어놓는다
출력 두 줄 — 교집합, 합집합

집합으로 담으면 저절로 되는 것

겹친 것이 사라진다

같은 값을 여러 번 넣어도 하나만 남습니다. 요구 사항 2번을 따로 코드로 짤 필요가 없습니다.

집합 연산이 준비되어 있다

교집합과 합집합을 기호 하나로 구할 수 있습니다. 반복을 돌며 하나씩 비교할 필요가 없습니다.

어떻게 담느냐가 얼마나 짜야 하는지를 정합니다. 목록으로 담았다면 겹친 것을 걷어내는 반복과 양쪽을 비교하는 반복을 직접 짜야 했을 것입니다.

공짜로 안 되는 것도 있습니다

순서는 공짜가 아닙니다. 집합에는 순서가 없으니까요. 작은 것부터 내놓으려면 따로 늘어놓아야 합니다. 이 실습에서는 담는 방식이 대신해 주는 것과 아닌 것을 구분해 봅니다.

푸는 순서

  1. 첫 줄을 받아 집합으로 담는다.
  2. 둘째 줄도 집합으로 담는다.
  3. 두 집합의 교집합을 구해 작은 것부터 늘어놓아 한 줄로 내놓는다.
  4. 두 집합의 합집합을 구해 작은 것부터 늘어놓아 한 줄로 내놓는다.
스스로 확인

1 2 3 4와 3 4 5 6을 넣으면 교집합과 합집합에 각각 무엇이 들어갈까요? 손으로 적어 보세요.

3단계 · 파이썬으로 옮기기

네 줄이면 끝납니다.

먼저 스스로 적어 본 뒤에 코드를 보세요. 아주 짧은 코드입니다.

집합.기본.py

# 두 줄을 받아 각각 집합으로 담는다. 겹친 값은 저절로 하나로 합쳐진다
a = set(map(int, input().split()))
b = set(map(int, input().split()))

# 오름차순으로 늘어놓아 한 줄에 내놓는다
print(*sorted(a & b))
print(*sorted(a | b))

한 줄에 세 가지가 겹쳐 있습니다

내놓는 줄 하나가 집합 연산 → 늘어놓기 → 공백으로 띄워 내놓기를 한꺼번에 하고 있습니다. sorted는 늘어놓은 결과를 리스트로 돌려주고, 그 앞의 *는 리스트를 풀어 값 하나하나를 print에 따로 넘깁니다. 그래서 대괄호 없이 공백으로 띄운 값이 찍힙니다. 짧아서 좋지만, 한 줄이 여러 일을 하면 어디가 틀렸는지 찾기 어려워집니다. 처음 만들 때는 줄을 나누어 중간 값을 찍어 보는 편이 낫습니다.

늘어놓기가 왜 필요한가

집합을 그대로 내놓으면 순서를 보장할 수 없습니다. 작은 수 몇 개로 시험하면 우연히 작은 것부터 나오는 것처럼 보이기도 합니다. 그래서 우연히 맞는 것을 보고 「정렬이 필요 없구나」 하고 넘기기 쉽습니다. 요구 사항 4번을 지키려면 반드시 늘어놓아야 합니다.

스스로 확인

늘어놓는 부분을 빼고 실행해 보세요. 답이 맞게 나올 수도 있습니다. 그렇다고 빼도 될까요? 왜 그렇게 생각하나요?

4단계 · 넣어 보고 확인하기

겹침이 없을 때와 전부 겹칠 때를 꼭 넣어 봅니다.

넣은 값 나와야 할 결과 무엇을 보려는 값인가
1 2 3 4 / 3 4 5 6 3 4
1 2 3 4 5 6
일부만 겹치는 흔한 경우
1 2 / 3 4 (빈 줄)
1 2 3 4
겹치는 것이 없는 경우 — 요구 사항 5번
1 2 3 / 1 2 3 1 2 3
1 2 3
완전히 같은 두 집합
1 1 2 2 / 2 2 3 2
1 2 3
같은 수가 여러 번 — 하나로 쳐지는가
9 1 5 / 5 9 7 5 9
1 5 7 9
뒤섞여 들어온 경우 — 순서대로 나오는가

결과가 어긋났다면 여기부터 봅니다

  • 순서가 뒤죽박죽이다 — 늘어놓기가 빠졌습니다
  • 중괄호와 쉼표가 함께 나온다 — 집합을 그대로 내놓고 있습니다. 하나씩 풀어서 내놓아야 합니다
  • 같은 수가 두 번 나온다 — 집합이 아니라 목록으로 담았습니다
  • 교집합과 합집합이 뒤바뀐다 — 두 연산 기호를 바꿔 썼습니다
  • 글자순으로 늘어선다 — 수로 바꾸지 않고 글자인 채로 담았습니다. 10이 9보다 앞에 옵니다

뒤섞여 들어온 줄이 없으면 정렬 빠뜨림을 못 잡습니다

나머지 줄은 이미 작은 순으로 들어온 값들이라 늘어놓기를 빼먹어도 맞게 나올 수 있습니다. 일부러 뒤섞어 넣는 줄이 반드시 하나 있어야 합니다.

스스로 확인

표에 두 자리 수가 섞인 줄을 하나 더 보태 보세요. 9와 10을 함께 넣으면 무엇을 확인할 수 있을까요?

더 해 보기

공짜로 받은 것을 직접 만들어 봅시다.

집합 없이 만들어 본다

목록으로 담고 반복으로 직접 교집합과 합집합을 구해 봅니다. 겹친 것을 걷어내는 일까지 손으로 해야 합니다. 네 줄짜리가 몇 줄이 되는지 세어 보세요.

차집합도 구한다

첫 집합에만 있는 것을 구해 봅니다. 순서를 바꾸면 답이 달라지는 연산이라 교집합·합집합과 성질이 다릅니다.

개수를 함께 내놓는다

교집합과 합집합의 원소 개수도 알려 줍니다. 합집합의 개수 = 두 집합 개수의 합 − 교집합 개수가 정말 맞는지 확인해 보세요.

숫자가 아닌 것도 담는다

낱말을 담아 두 반이 함께 신청한 과목을 구해 봅니다. 「작은 것부터」가 낱말에서는 무슨 뜻이 될지도 정해야 합니다.

스스로 확인

「집합 없이 만들어 본다」를 해 보면 코드가 훨씬 길어집니다. 그렇다면 집합으로 담는 쪽이 언제나 나은 걸까요? 코드의 길이만이 아니라 읽기 쉬움, 처리 시간, 다룰 수 있는 자료의 종류도 함께 비교해 보세요.