djm03178's profile image

djm03178

December 18, 2021 16:35

프로그래밍 대회 출제 체크리스트

개요

PS가 보다 대중에게 친숙해지면서 여러 종류의 프로그래밍 대회도 점점 많아지고 있습니다. 특히 기관이나 기업이 아닌 일반인이 주최하는 대회가 부쩍 늘었는데, 대표적인 것이 대학교 대회라고 할 수 있습니다. 모두에게 친숙한 백준 온라인 저지에서도 BOJ Stack을 통해 손쉽게 대회를 준비할 수 있는 플랫폼을 만들어 어느 정도 PS에 경험이 있다면 누구나 쉽게 대회 개최에 참여할 수 있게 되었습니다.

반면에 아마추어가 쉽게 대회를 열 수 있다는 점은 반대로 준비가 덜 된, 낮은 퀄리티의 문제가 쉽게 양산될 수도 있다는 부작용을 수반합니다. 실제로 몇 년 전에는 이러한 문제가 조금 심각했고, 이것이 출제진 / 검수진의 기준을 도입하게 만드는 계기가 되었습니다. 그러나 출제진이나 검수진 모두 말 그대로 아마추어들이기 때문에, 그리고 학교마다 실력자들이 충분히 있지 않을 수 있기 때문에 이 기준을 과도하게 높이면 대회를 열 수 있는 학교가 몇 안 될 것입니다.

외부 검수자의 경우 출제자나 내부 검수자에 비해 보다 높은 기준을 요구하고 있으나, 단순히 정답 코드를 작성하는 것 외에도 대회 준비에는 여러모로 신경써야 할 것이 많기 때문에 문제를 많이 풀고 실력이 좋다고 해서 반드시 좋은 검수자가 되는 것은 아닙니다. 이 글에서는 대회의 퀄리티를 보장하기 위해 대회의 출제자와 검수자가 신경써야 할 일들이 무엇인지, 그 체크리스트를 알아보겠습니다.

출제자의 역할

출제자는 대회를 주관하는 것부터 문제의 풀이, 지문 및 데이터를 작성하는 총책임자입니다. 대회를 여는 플랫폼에서 지켜야 할 사항(이 글에서는 BOJ에 출제하는 것을 기준으로 하겠습니다)을 숙지하는 것은 기본입니다. 예를 들어 BOJ의 경우 Stack에 문제 안내 가이드라인이 있습니다.

프로그래밍 대회라는 것은 데이터 하나, 조건 하나라도 틀릴 경우 대회 전체에 심각한 영향을 미칠 수 있기 때문에 모든 작업에 있어 신중하고 꼼꼼해야 합니다. 한 글자 단위에 민감한 문제인 만큼 Polygon과 같이 정교하게 설계된 출제 플랫폼의 사용법을 익히는 것이 좋습니다. Polygon의 사용법에 대한 글은 여기에서 읽어볼 수 있습니다.

검수자가 이런 저런 사항을 지적하고 여러 주장을 하더라도 결국 최종 결정을 내리는 것은 출제자입니다. 검수자의 제안이나 조언을 듣고, 어떤 결정이 가장 합리적일지는 직접 판단하고 책임을 져야 합니다.

검수자의 역할

대회를 검수하는 것은 단순히 참가자로서 정답 코드를 하나 만들어내는 것과는 다릅니다. 대회의 검수자는 기본적으로 문제 공개 전까지 출제자와 함께 문제 제작에 참여하는 사람입니다. 따라서 검수자는 문제의 지문과 데이터를 비롯하여 대회 전체의 구성에 어떤 결점이나 개선 가능한 면이 없는지를 꼼꼼하게 확인해야 합니다. 검수자 역시도 대회 플랫폼의 가이드라인 등을 숙지하고 출제 플랫폼의 사용법을 알아두는 것이 좋습니다.

검수자는 출제자가 아니므로 보통 문제를 직접 수정하지는 않습니다. 하지만 자신의 의견을 확실하게 출제자에게 전하는 것은 중요합니다. 가이드라인에 제시된 사항이나 명확한 오류뿐 아니라 주관적으로도 문제를 더 나은 방향으로 만들 거라고 생각하는 부분이 있다면 출제자에게 의견을 밝히는 것이 좋습니다.

문제별 체크리스트

하나의 문제는 크게 지문 부분과 데이터 부분으로 이루어져 있습니다. 지문은 참가자와 소통하는 부분이고, 데이터는 참가자의 코드와 소통하는 부분이라고 할 수 있습니다. 각 문제를 제작할 때 목표로 해야 하는 것은 지문을 통해 참가자에게 문제의 요구 사항을 명확하게 전달하고, 참가자의 코드가 최대한 공정하게 평가받을 수 있도록 데이터를 견고하게 만드는 일이 될 것입니다.

지문

지문은 글쓰기의 영역입니다. 어떤 알고리즘 문제를 하나 말로 설명하는 것과 좋은 지문을 쓰는 것은 서로 매우 다른 일입니다. 지문을 작성할 때에는 단순히 조건과 제한을 잘 적는 데에 그치는 것이 아니라, 지문 전체를 하나의 짧은 글로서 지문 전체의 논리성, 맞춤법과 오/탈자, 문단의 구성 등을 심도 있게 고려하면서 작성해야 하며 수차례의 퇴고 과정도 밟아야 합니다.1

  • 맞춤법: 맞춤법의 오류가 많은 지문은 결코 잘 쓰인 글로 보이지 않습니다. 심할 경우 문장의 해석 자체에 어려움을 겪게 되며, 문제 자체의 격을 떨어져보이게 만드는 주범입니다. 자주 틀리는 맞춤법, 띄어쓰기 등을 잘 확인해서 문법적으로 올바른 지문을 쓰는 것이 중요합니다.
  • 수식: PS는 수학에 가까운 분야이다 보니 수식을 쓸 일이 많습니다. BOJ의 경우 MathJax를 사용하여 수식을 표현하는데, 조건에 맞는 수식을 사용하고, 일반 텍스트와 사용할 곳을 명확하게 구분하며23, 그 외 가이드라인도 잘 지키고 있는지 확인하며 작성해야 합니다.
  • 문단 구성: 지문은 채팅을 하는 것과는 다릅니다. 모든 문장이 전부 개별적인 문단에 있다거나, 전부 한 문단에 줄글로 붙여서 써서는 안 됩니다. 서로 연관성이 깊은 내용은 한 문단에 써야 하고, 서로 다른 주제는 별개의 문단으로 구분해야 하며, 문단 사이의 연결도 매끄러워야 합니다.
  • 줄거리: 지문에 재미있는 스토리를 넣는 것은 출제자로서는 즐거운 일이 아닐 수 없습니다. 그러나 이 경우에도 무작정 하고 싶은 이야기를 생각나는 대로 적고 끝내서는 안 됩니다. 지문 전체가 하나의 문학 작품으로서의 구성을 갖추도록, 너무 터무니없는 설정이나 등장인물의 행동에 타당성이 부여되지 않는 요소들이 삽입되지 않도록 주의해야 합니다. 나름의 배경과 세계관을 확실히 갖추고 개연성을 부여하는 것이 좋습니다.
  • 간결함: 기본적으로 지문은 문제를 풀기 위한 도구이기에 스토리 구상에 집중한 나머지 문제의 요구사항을 파악하기조차 어려울 정도로 지문이 길고 복잡해져서는 안됩니다. 문제를 푸는 데에 불필요한 정보가 너무 많이 들어가지는 않았는지 확인하는 것이 좋습니다.
  • 단어의 일관성: 지문 전체에서 사용하는 단어에 일관성이 있어야 합니다. 예를 들어 처음에는 ‘도로’라고 표현한 것이 뒤에서는 ‘길’ 또는 ‘간선’이 되거나, ‘도시’였던 것이 갑자기 ‘마을’이나 ‘정점’이 된다거나 하지는 않았는지 확인해야 합니다. 지문은 명확해야 하기에 비슷해 보이는 단어라도 혼용할 경우 참가자에게 혼란을 주기 쉽습니다.

풀이

대회의 참가자는 정답 코드 이외의 코드를 만들어 볼 필요가 없습니다. 하지만 출제자나 검수자는 다릅니다. 어떤 풀이를 정해로 할 것인지 정한 뒤 그 정해 코드를 만들어보는 것은 당연한 것이고, 더 중요한 것은 틀린 풀이들을 만들어보고 그것들이 통과되지 않게끔 하는 것입니다.

  • 정해: 출제자의 경우 모델이 되는 자신의 정해 코드를 작성하는 것은 당연합니다. 기준이 되는 코드가 있어야 그를 중심으로 입력이나 시간/메모리의 제한을 설정할 수 있기 때문입니다. 검수자의 경우 먼저 자신이 문제를 제대로 해석한 것이 맞는지, 그 문제의 실제 체감 난이도가 어떤지 등을 확인하기 위해서라도 가장 편한 언어로 맞는 풀이를 작성해보아야 합니다.
  • 다른 언어의 정해: 대회에는 일반적으로 각 문제마다 통과 가능하게 해주고자 하는 언어들이 있습니다. 예를 들어 이 문제는 Python으로 풀 수 있게 하는 것이 의도라고 한다면, Python으로 정해 코드를 작성하여 잘 통과되는지 확인해보는 과정 역시 중요합니다.4 또한 그 언어의 라이브러리 함수 때문에 의도보다 너무 쉽게 풀 수 있는 것은 아닌지 등도 고려해볼 수 있습니다.
  • 틀린 풀이: 틀린 풀이를 작성하고 저격 데이터를 추가하는 것이야말로 출제 과정의 꽃이라고 할 수 있습니다. 오히려 출제자의 입장에서는 검수자가 많다면 굳이 똑같은 정해 풀이를 다수 추가하는 것보다도 이런 다양한 틀린 풀이가 정말로 오답 판정을 받는지 확인하는 것이 더 도움이 될 수도 있습니다. 올바르다고 착각하기 쉬운 틀린 전략들을 비롯해서, 인위적으로 저격하지 않으면 통과되기 쉬운 코드들, 시간 복잡도가 너무 큰 풀이, 배열 크기를 잘못 잡거나 변수를 뒤바꾸어 쓰는 단순 실수 등 시간적 여유가 되는대로 많이 만들어 테스트 해볼수록 좋습니다. 대회 중 출제자가 의도하지 않은 틀린 풀이가 대거 통과되는 건 유쾌하지도 않고 참가자간의 형평성도 해치게 될 뿐입니다.
  • 뚫는 풀이: 틀린 풀이와 비슷하지만 이쪽은 좀 더 데이터의 취약성을 집요하게 파고드는 것을 말합니다. 즉, 틀린 풀이인 것을 알지만 저격 데이터가 없을 것을 바라면서, 혹은 애매한 시간 제한을 이용해 통과를 의도하지 않은 시간 복잡도의 코드를 통과시키는 것이 목적입니다. 이를 통해 더욱 강한 데이터를 추가하거나 보다 안정적으로 정해의 시간 복잡도까지만 통과시키는 시간 제한을 설정할 수도 있게 됩니다.

데이터

위의 풀이들을 작성했다면 대충 데이터의 강도는 감을 잡을 수 있으나, 그렇다고 해서 데이터가 모두 올바르거나 극단적인 케이스들을 모두 포함하고 있다고 단정할 수는 없습니다. Polygon에서 제공하는 도구들을 적극 활용해서 이들을 확인해볼 수 있습니다. 특히 다음과 같은 사항을 꼼꼼하게 점검해볼 필요가 있습니다.

  • Validator: 데이터 자체를 검증한다는 점에서 반드시 체크해야 하는 요소입니다. 문제에 주어진 입력 조건이 한 글자 단위로 정확하게 validator에 쓰여있는지 면밀한 검사가 필요합니다. 직접 테스트용 입력을 만들어보는 것도 좋습니다.
  • Checker: 스페셜 저지가 있다면 체커 역시 꼭 체크해야 합니다. 특히 체커 자체가 하나의 문제를 푸는 수준으로 복잡하다면 그 로직에 하자가 없는지, 코딩 실수나 undefined behavior 등은 없는지 매우 꼼꼼하게 살펴보아야 할 것입니다.
  • 극단적인 입력의 존재 여부: $N$이 $10^5$ 이하라고 해놓고 실제로는 $99273$ 이하의 수만 들어있다거나 하는 경우가 많습니다. 이는 출제자가 고정으로 $N$을 $10^5$으로 하는 데이터를 만들지 않고 랜덤으로만 $N$을 정했을 때 흔히 나타나는 케이스입니다. 언뜻 보면 일반적으로 크게 문제가 될 것 같지 않아 보이지만, 정확히 $10^5$의 데이터가 있는 것과 $99999$까지만 있는 것은 off-by-one error 때문에 자주 차이가 발생하기 때문에 이런 극단적인 입력이 존재하는지에 대한 여부를 체크하는 것도 중요합니다. 반대로 $N=1$과 같은 곳에서도 특별한 예외 처리가 필요한 경우가 많아 매우 작은 입력들에 대한 테스트도 있어야 합니다. Polygon에서는 invocation을 돌리면 각 변수에 지정해 둔 최소 / 최댓값에 도달한 데이터가 존재하지 않는 경우 경고를 발생시켜줍니다. 또한 정해 풀이에 C / C++의 assert와 같은 조건 체크를 추가해보는 것 등으로도 한계값 검사를 해볼 수 있습니다.

대회 전체에 대한 체크리스트

하나의 문제를 출제할 때에는 위의 목록이면 충분하지만, 대회를 출제할 때에는 대회 전체를 유기적으로 구성하는 것 또한 신경써야 합니다. 대회가 목표로 하는 참가자/난이도의 수준과 대회 시간, 문제의 분야 등이 모두 다르기 때문에 그에 맞는 문제 세트가 완성되었는지 확인해야 합니다.

  • 난이도 분포: 대회의 성격에 맞는 난이도 분포가 만들어졌는지 체크해야 합니다. 참가자들이 대체로 초보자일 것으로 예상되는 대회라면 쉬운 문제 위주로, 실력자들이 많이 참가할 대회라면 어려운 문제 위주로, 넓은 범위의 실력을 가진 사람들이 참가할 대회라면 쉬운 문제부터 어려운 문제까지 골고루 존재하도록 해야 합니다.
  • 문제 분야 분포: 특정 분야를 컨셉으로 한 대회가 아니라면 다양한 분야의 문제를 출제하는 것이 좋습니다. 모든 문제가 수학에 가까운 문제여서 수학을 잘하는 사람에게만 유리하거나, 모든 문제가 구현 문제로만 나와서 타이핑이 빠른 사람이 이기는 대회는 그다지 환영받지 못할 것입니다. PS에서 다루는 다양한 세부 분야와 알고리즘 테크닉을 시험하는 대회가 될 수 있도록 문제들의 분야의 균형을 잡을 수 있도록 조정하는 것이 좋습니다.
  • 대회 시간: 문제의 분포가 적절함에도 시간 내에 다 해결하지 못할 정도로 문제 수가 많거나 너무 여유있어서 모든 문제를 푸는 사람이 너무 많다면 아쉬울 것입니다. 또한 개인 대회와 팀 대회일 때에도 문제를 해결하는 속도가 현저히 다를 수 있다는 점을 고려해야 합니다.
  • 지문의 일관성: 문제마다의 출제자는 서로 다르더라도, 하나의 대회라면 문제들간에 어느 정도의 일관성은 갖추는 것이 좋습니다. 대표적인 것으로 문체가 있는데, 어떤 문제에서는 ‘해라체’를 쓰다가 다른 문제에서는 ‘하십시오체’를 쓴다면 과연 이들이 한 대회에서 나온 문제가 맞나 의심스러울 것입니다. 조금 사소하지만 흔하게 보이는 비일관성의 예시로는, 어떤 문제에서는 ‘~를 구하는 프로그램을 작성하시오’라고 하다가 다른 문제에서는 ‘~를 출력하시오’라고 표현하는 것 등이 있습니다. 또한 같은 등장인물이 여러 문제에 등장하는데 문제마다 성격이 180도 변한다면 이 역시 이상할 것입니다.
  • 데이터의 일관성: 데이터의 포맷팅에도 일관성을 적용할 수 있습니다. 예를 들어 다중 테스트 케이스 문제를 여러 문제에서 낼 수 있습니다. 그런데 한 문제에서는 테스트 케이스 사이에 빈 줄을 하나 삽입했는데, 다른 문제에서는 빈 줄 없이 바로 다음 케이스를 입력하게 할 수 있습니다. 사소하지만 이런 것 역시 하나의 대회로서의 정체성을 확립하는 데에 영향을 줄 수 있는 부분입니다.5

대회 중 체크리스트

대회가 실제로 진행되는 동안에도 계속해서 확인해야 할 사항들이 있습니다. 아무리 철저한 검수 과정을 거쳤다고 해도 차마 잡아내지 못한 실수는 얼마든지 있을 수 있고, 자체적인 결함은 아니더라도 참가자를 곤혹스럽게 할 수 있는 요소들이 존재할 수도 있기 때문입니다. 따라서 출제자 및 검수자는 대회 중 받게 되는 질문들을 꼼꼼하게 읽고, 오해의 여지가 있을 수 있는 부분은 전체 공지로 알리며, 명백한 오류가 발견된 경우 지문 및 데이터의 긴급 수정이나 재채점 등을 할 준비를 항상 하고 있어야 합니다.

또한 스코어보드를 주시하면서 특정 문제가 예상보다 오답률이 너무 높은 것이 보인다면 혹시나 데이터에 문제가 있거나 지문에 잘못 쓰인 부분이 있지는 않은지 등을 확인하고, 실력자의 코드를 열람하여 어떤 부분에 문제가 있는지를 먼저 체크해볼 수도 있습니다.

마치며

규모가 크지 않은 일반인의 대회가 항상 높은 퀄리티를 유지하기는 어렵습니다. 하지만 그래도 모두가 열심히 준비하는 하나의 대회인 만큼 기본적인 것들만이라도 잘 지켜서 견고한 대회를 만들 수 있으면 좋겠습니다. 이 글에 적은 것이 체크리스트의 전부는 아니지만, 결국 상식적인 선에서 확인할 수 있는 부분들을 철저하게 다듬어 누가 보기에도 깔끔한 상태의 문제 셋을 만들어내는 것이 목표라는 점은 공통 사항이 될 것입니다.

  1. 지문의 상태가 심각하면 대회 종류 후 다음과 같은 요청글을 보게 될 수도 있습니다. 지문이 한국어가 아닙니다. 

  2. $1 \le n \le 100, 1 \le n \le 100$에서 가운데의 반점은 수식에서 빠져야 합니다: $1 \le n \le 100$, $1 \le n \le 100$ 

  3. 어떤 수를 $N$으로 표기할 거라면 지문 전체에 걸쳐 수식으로 $N$으로 써야 하며, 중간에 일반 텍스트 N을 섞어쓰지 않아야 합니다. 

  4. Python으로 풀 수 있다고 하면 보통은 PyPy로 되는지만 보면 됩니다. 

  5. 일관적인 예시로 Google Code Jam에는 다중 테스트 케이스 문제에서 항상 출력에 각 케이스마다 “Case #x: “를 삽입하게 하는 전통(?)이 있습니다. 그래서 이것만 보고도 이 문제의 출처가 어디인지 짐작할 수 있습니다.