assert 함수로 검증하는 과정 없이 문제 출제를 하는 것은 상상할 수 없습니다. 특히 복잡한 데이터 제너레이터를 여러 개 구현했다면, 검증하는 과정은 필수로 들어가야 합니다. 대회 set 하는 데 오래 걸리는 이유 중 하나입니다. 여기에서는 assert가 뭘 하는 함수이고, 제가 낸 문제에서 어떤 식으로 검증했는지 간단하게 언급해 보도록 하겠습니다.

 


 아래 예제를 보겠습니다.

 

 assert(1 != 1); 이 있습니다. 안에 조건문 비슷한게 들어갔는데요.

 

 

 실패합니다. 왜냐하면 1 != 1을 만족해야 하는데 1과 1은 같기 때문입니다. 정리하면, assert 함수에는 condition을 넣습니다. 이 조건이 만족하지 않으면 assert는 실패해 버립니다. 그래서, 문제를 만들 때에는, 데이터가 조건에 맞는지 다시 한 번 확인하기 위해서 쓰게 됩니다. 지적 확인 같은 거라고 생각하심 편하겠네요. 교차 확인해서 나쁠 건 하나도 없습니다.

 

 이제, 제가 개최한 코딩 테스트에 나온 문제들을 가지고, assert 함수를 사용해 보도록 하겠습니다.

 


 먼저, 22236번 가희와 비행기 문제를 보겠습니다. 이 문제는 d와 m이 주어지는데요. d는 1보다 크고 4000보다 작거나 같은 짝수이고, m은 소수이고 구간 [2, 20억]에 속하는 정수입니다.

 

 그러면, 문제는 어떤 전제를 만족해야 하나요? 일단 d는 1 <= d이고 d <= 4000이여야 합니다. 7번째 줄에 이 전제 조건을 하나 깔아두었습니다. 다음에 짝수여야 하니까, d % 2 == 0을 만족해야 합니다.

 

 

 d에 7을 넣으면 1 <= d이고 d <= 4000이라는 조건은 만족합니다. 그런데, d가 짝수라는 조건은 만족하지 않아요. 따라서, Assert fail이 뜨고 Aborted가 뜨게 됩니다.

 

 d가 100이면 어떤가요? [1, 4000]에 속하는 자연수이면서 짝수니까 d가 만족해야 하는 전제 조건은 만족합니다. 그런데, 이것만 확인하면 될까요? m이 소수이면서 [2, 20억]이다라는 전제가 또 깔려 있어요. 그래서 m이 전제 조건을 만족하는지 검사하는 로직이 추가 되어야 합니다.

 

 

 먼저 소수인지 검사하는 함수를 하나 추가합시다. is_sosu는 int 인자를 하나 받습니다. 만약에 인자로 받은 것이 소수라면 true를, 아니면 false를 리턴합니다.

 

 

 m이 구간 [2, 20억]에 속하는지 검사하는 것은 19번째 줄에 있고, 소수인지 검사하는 것은 20번째 줄에 있습니다.

 

 

 d가 100이고 m이 15일 때에는 15가 소수가 아니므로, Assrtion은 실패하게 됩니다.

 

 

 m이 37이라면 성공합니다.

 


 이번에는 조금 더 복잡한 예를 들어보겠습니다. 제가 출제한 22234번 가희와 은행 문제에 깔아놓은 전제 조건 중 몇 개를 같이 검증해 봅시다. 고객 n명에 대해, 고객들의 id는 unique 해야 합니다. 그리고 고객의 id는 구간 [1, 10억]에 속해야 합니다. 어떻게 해야 할까요? 조건이 복잡해 졌다면, 함수로 빼는 것 부터 합시다. is_range(v, s, e)는 정수 v가 구간 [s, e]에 속하는지 검사합니다.

 

 

 만약에 tar가 s보다 작거나, e보다 tar가 크다면, range에 속하지 않는 것이니, false를 날려버리면 됩니다. 아니면 true를 떨어트려주면 됩니다.

 

 

 그러면, 입력받은 유저 id가 [1, 10억]에 속하는지는 쉽게 판단할 수 있습니다. is_range(id, 1, 10억)이 false라면 assert가 걸릴 것이기 때문입니다. 17번째 줄에 있는 assert 문이 이 역할을 합니다. 그런데, 입력으로 주어지는 n개의 id가 모두 unique 하다는 전제는 검사하지 못하는데요. 이 때 빌릴 수 있는 것은 set, map입니다.

 

 c++에서 set이나 map은 key 값의 중복을 제거하기 위해 쓰는 대표적인 자료구조입니다. 예를 들어, set에 키 값 1이 들어가고, 또 1이 들어갔다 해 보겠습니다.

 

 set에 1이 들어갔을 때, set 사이즈는 1이 됩니다. 다음에 set에 1을 또 넣으면 1이 이미 있기 때문에, 크기가 변하지 않습니다.

 

 

 그래서, n개의 id를 set에 모두 넣었을 때, 그 중 하나라도 중복되는 게 있다면, set의 크기가 n이 되지 않습니다. 21번째 줄은 그래서, 입력받은 손님들의 id가 unique한지 판단해 줍니다.

 

 

 id가 5인 게 겹쳤으므로, Abort가 되었다는 것을 볼 수 있어요. 저는 unique를 판단할 때 이 방식을 많이 이용하는데요. 잘 안 와닿을 수 있어요. 그냥, 루프를 돌 때 마다 해당 키 값이 있는지 검사하는 게 가독성이 더 좋을 듯 싶습니다.

 

 

 s.find(key)는 s에서 key값을 찾습니다. 만약에 없으면 s.end()를 리턴합니다. 키 값 id를 추가하기 전에, 이미 s에 있지 않아야 하므로, 전제 조건은 s.find(id) == s.end()여야 합니다. 19번째 줄의 어셔선문은, 입력 받은 id 값들이 unique한지 판단합니다.

 

 

 실행 결과를 보면, Aborted가 뜨는데요. 5가 이미 있는 상태에서 5를 입력 받았기 때문입니다. JUnit을 배울 때도 유용하게 써먹을 수 있으니, 알아두시면 좋을 듯 싶습니다.