[{"content":"계속 \u0026ldquo;이직 후기 써야지\u0026hellip;\u0026rdquo; 라고 생각만 하고 미루다가 결국 이직 1주차가 지난 지금에서야 작성하는 후기(?)\n사실 About 페이지나 그간의 대회 후기에서나 반쯤은 드러내놓고 써서 새삼스럽긴 하지만, 지난주, 정확히는 2025년 5월 31일을 기준으로 7년간의 군생활을 마치고 공군 대위에서 예비역 공군 대위로 칭호가 바뀌었다. 전역하게 되면 뭔가 심정적으로도 크게 동요할 줄 알았는데 그새 1주일이 지나서인지 나름 익숙해진 것 같다. 올해부터 군 전직지원기간이 늘어서 지난 2월부터 출근 안 하고 거의 4개월 휴가를 보냈던 탓도 있는거같음\n결론부터 말하면, 지난 6월 2일부터 쿼라(Quora)에서 Software Engineer로 일을 시작했다. 쿼라에 대한 소개는 잘 정리된 다른 글이 있어 이로 대체한다. 기본적으로 100% 재택근무에 원하는 경우 사당 공유오피스를 이용할 수 있다(출근하면 무료 간식/커피와 점심 식대가 지원된다). 지금은 본격적인 업무 시작 전 온보딩 과정중에 있어 회의/화상교육이 있을 때는 재택, 아닌 때는 오피스에 나와 다른 분들께 다양한 업무 관련 조언을 구하고 있다.\n구직과정 전역을 앞두고 작년 말 ~ 올해 초부터 지원을 넣기 시작했고, 아래 차트에 정리한 과정을 거쳐 최종적으로 3개 회사에 합격했다. 3곳 모두 각각의 특색이 있는 곳이라 많은 고민을 했고, 고민 끝에 쿼라로의 이직을 결정했다. 삼성의 경우 차트를 만들 당시 서류통과 후 면접일정을 잡던 중이었는데, 결국 다른 곳들 결과가 모두 나온 후에야 일정이 나와 면접 전 중도포기 메일을 보내고 전형을 중지했다. 그밖에도 개발과 보안이라는 양쪽 분야에 모두 발을 걸친 만큼 관심있던 곳들은 많았지만 시계 제작으로 유명한 모 공공기관을 포함해 공고 타이밍이 어긋나거나 다른 이런저런 사유들로 인해 모두 지원까지 이어지지는 못했다.\n군에서 7년간 사이버보안/개발업무를 수행했다보니 \u0026ldquo;군 경력\u0026rdquo;, \u0026ldquo;사이버보안 경력\u0026rdquo;, \u0026ldquo;개발경력\u0026rdquo; 이 혼재되어있어 회사마다, 혹은 직무마다 연차 인정 등에서 약간씩 차이를 보였다. 다만 요즘은 회사들도 일부 대기업을 제외하면 연차에 큰 의미를 두지 않는 경우가 많아 나 역시도 연차 인정에 크게 신경쓰지는 않았다. 앞서 설명한 삼성을 제외한 다른 곳들에 대해 내용이 짧은(=더 일찍 탈락한) 순서대로 간단한 후기를 정리했다. 아래 후기에서 코드만 제출하는 코딩테스트는 온라인 코딩테스트로, 화면공유를 통해 실시간으로 설명을 진행하며 인터뷰어와 소통하는 방식의 인터뷰는 라이브 코딩테스트로 구분해 작성했다.\n팔란티어(Palantir) 직무: Forward Deployed Software Engineer 결과: Recruiter Call 이후 탈락 최근 매우 유명해진 곳으로, 정부나 기업 고객을 대상으로 고담(Gotham)과 파운드리(Foundry)라는 데이터 기반 온톨로지(Ontology) 플랫폼을 제공하는 회사이다. 작년 말 팔란티어 한국지사에서 FDSE 직무를 모집한다는 공고를 보고 온라인으로 지원했다. 간단한 업무경험과 개발, 사이버보안 관련 수상실적을 정리한 이력서를 제출했고 며칠 지나지 않아 Recruiter Call 관련 메일을 받을 수 있었다.\nRecruiter Call은 영어로 진행되었으며, 지원동기를 비롯해 15-20분 정도 대화를 진행했으나 며칠 후 불합격이라는 안내를 받았다. 개인적으로 업무경험(군 특수부서)도 그렇고 사이버보안, 알고리즘 양쪽 모두에서 나름대로 실적이 있어 \u0026ldquo;그래도 기술면접까지는 가지 않을까\u0026quot;라고 생각했기에 다소 충격적인 결과였지만 나중에 링크드인을 통해 현직자 커피챗을 진행한 결과 어느정도 결과를 받아들일 수 있었다.1\n프레스토랩스(Presto Labs) 직무: Algorithmic Trader 결과: 대면면접 이후 탈락 싱가폴 기반의 퀀트 트레이딩 회사로, HFT(High-Frequency Trading, 고빈도매매)를 주력으로 한다. 자동화된 프로그램 매매 전략을 수립하고 이를 기반으로 가상자산, 증권 등에 투자해 수익을 내는 회사라고 할 수 있다. Algorithmic Trader 직무로 지원했으며, 온라인 코딩테스트 후 여의도 사무실에서의 대면면접이 잡혔다. 개인적으로는 온라인 코딩테스트를 진행했던 다른 두 곳(토스, 몰로코)에 비해 가장 난이도가 높았고, 온라인 코딩테스트와 함께 온라인 수학테스트(?)가 있다는 점이 신선했다.\n대면면접은 말 그대로 폭망했다(\u0026hellip;) 면접장에 가니 A4용지와 펜이 준비되어 있었고, 일종의 수학 퍼즐 질문을 받은 후 그자리에서 풀이를 설명하는 식으로 진행되었다. 면접에 들어가기 전 어느정도 알고리즘 관련 문제나 혹은 수학 관련 라이브코딩을 예상했었기에 조금 당황했고, 주어진 질문들에 반 가까이 제대로 답하지 못하면서 탈락을 직감했다. 역시나 결과는 불합격이었고, 면접장을 나오는 순간 예상했던 결과라 담담히 받아들일 수 있었다. 비록 떨어졌지만 이런 스타일의 문제들을 평소에 꾸준히 접하는 것도 나름 재미있을 것 같다.2\nUN개발계획(UNDP) 직무: Digital Solutions Analyst (Fullstack Engineer) 결과: 과제전형 중 일정 문제로 중도포기 우리가 아는 그 UN의 산하기관으로, 전 세계의 지속가능한 개발, 빈곤퇴치, 기후변화 대응 등을 지원하는 곳이다. SNS를 보던 중 \u0026ldquo;이런 공고도 있어?\u0026rdquo; 라는 생각에 좀더 찾아봤고, 생각보다 흥미로워 지원했다(UNDP 서울정책센터가 고려대에 있다는 걸 이때 처음 알았다). 직무 설명을 읽어보니 많은 업무들이 개별 프로젝트 단위로 이루어지고, 각각은 사실상 대시보드 생성이나 SI 업무라는 예상이 들었지만 그럼에도 불구하고 그 업무들 하나하나에서 큰 보람을 느낄 수 있겠다는 것이 느껴졌다. UNDP 서울정책센터의 최근 업무를 예로 들면\n코소보의 디지털 부패 신고 시스템 개선 페루의 전통시장 디지털 전환 지원 코트디부아르 경찰청의 기술매개 젠더기반폭력(사이버성범죄) 대응역량 강화 지원 같은 것들이 있다. 살면서 한번쯤 해보고 싶은 업무였지만 직무 특성상 1년 계약직이라는 한계가 계속 마음에 걸렸고, 이런 한계로 인해 결국 다른 곳들 면접일정과 과제전형이 겹치자 아쉬운 마음을 뒤로 한 채 중도포기를 결정했다.\n비록 중간에 포기했지만 과제전형 방식은 조금 신선했는데, AI 도구 사용을 권장하며 어떤 프롬프트를 사용했는지 명시할 것이라는 조건 하에 간단한 대시보드가 포함된 풀스택 서비스를 템플릿 제공 없이 처음부터 구현하라는 내용이었다. 요즘 AI 도구의 발전으로 코딩테스트 무용론이 고개를 들고 있는데, 아예 이렇게 AI를 허용하는 대신 \u0026ldquo;어떻게 사용하는지\u0026quot;까지 함께 고려하는 것도 괜찮을 것 같다.\n몰로코(Moloco) 직무: Backend Software Engineer I/II 결과: 팀매칭 무산(25년도 1분기 TO 관련) 머신러닝 기반 광고 자동화 플랫폼을 제공하는 실리콘밸리 기업으로, 한국지사가 상당히 큰 지분을 차지하고 있다. 한국에서는 몰두센(몰로코, 두나무, 센드버드)이라는 약어의 한 축으로도 잘 알려져 있다. 처음 전형을 진행하기 앞서 인사팀에서 *\u0026ldquo;1분기에는 TO 문제가 있을 수 있는데, 2분기에 진행하는 것은 어떤지\u0026rdquo;*라는 안내를 해줬지만, 지원한 다른 회사들과 일정을 맞추기 위해 1분기에 그대로 진행을 희망한다는 답변을 보냈다.\n온라인 코딩테스트의 난이도는 평이했고 약 15분만에 주어진 4문제를 모두 해결할 수 있었다. 며칠 후 진행된 라이브 코딩테스트에선 화면공유를 통해 내 풀이를 설명하면서 코딩을 진행했는데, 코드 작성 → 제출 → 채점결과 확인으로 이어지는 방식이 아닌 사실상 메모장에 가까운 환경에서 \u0026ldquo;왜 이렇게 코드를 작성했는지\u0026rdquo;, \u0026ldquo;이 코드는 예시 입력이 들어왔을 때 어떻게 동작하는지\u0026rdquo;, **\u0026ldquo;내가 어떤 엣지 케이스를 고려했는지\u0026rdquo;**를 하나씩 설명하면서 진행하는 방식이었다.\n다만 코딩테스트 이후 팀매칭 단계에서 더 이상 진행되지는 않았는데, (문제는 나름 잘 해결했다고 생각했기에) 부족한 점에 대한 피드백을 요청한 결과 인터뷰 퍼포먼스의 문제가 아닌 해당 시점(25년 1분기) 기준 내가 지원한 레벨에 대한 TO가 닫힌 상황이라는 답변을 받았다. 처음엔 \u0026ldquo;조금 기다렸다가 2분기에 진행할 걸 그랬나\u0026quot;라는 생각도 했지만 2분기가 거의 끝난 지금까지도 Machine Learning 직무만 열리고 Backend 직무는 계속 닫혀있는 걸 보면 타이밍에 대한 아쉬움은 접어둬도 될 것 같다.\n여담으로 지인을 통해 처음 원서를 넣은 날이 12월 3일이었는데, 의도하진 않았지만 이로 인해 해당 지인에게 명확한 알리바이를 제시할 수 있었다(\u0026hellip;)\nEY컨설팅(Ernst \u0026amp; Young) 직무: Security Consultant 결과: 합격 글로벌 회계 및 컨설팅 기업인 EY(Ernst \u0026amp; Young) 산하에서 전략, 디지털 전환, 리스크 관리를 비롯한 다양한 비즈니스 컨설팅을 제공하는 곳으로 흔히 빅4라고 불리는 글로벌 4대 회계법인(딜로이트, KPMG, PwC, EY)의 일원이다. 한국에서는 각각 딜로이트안진, 삼정KPMG, 삼일PwC, EY한영이라는 이름으로 국내 법인과 파트너십을 맺고있으며 이중 EY의 경우 현재로서는 유일하게 파트너 관계가 아닌 글로벌 본사에서 직접 지사를 운영하는 구조로 되어있다.\n링크드인을 통해 사이버보안 컨설턴트로의 제안을 받았고, 2-3차례의 면접을 거쳐 최종 합격안내를 받을 수 있었다. 처음 컨설팅이라는 단어만 들었을 때 \u0026ldquo;기술적으로 성장하기는 어렵지 않을까\u0026rdquo; 라는 우려를 했는데, 해당 부서에서 근무하시는 분께 직접 설명을 듣고 나자 해당 직무가 기술적으로도 상당히 흥미롭다는 인상과 함께 굉장히 많은 분야에 걸쳐 경험을 쌓을 수 있겠다는 생각이 들었다. 다만 개발자로서의 경험을 좀더 쌓아보고 싶다는 욕심이 남아 고민 끝에 아쉬운 마음으로 입사 거절메일을 보냈다. 보안업계가 워낙 좁다보니 얼마 전 세미나에서 이분을 다시 만나 조금 머쓱한 상황이 연출되기도 했다\n토스(toss) 직무: Server Developer / Security Researcher 결과: 합격 토스가 어떤 회사인지에 대한 설명은 생략해도 된다고 믿는다. 작년 말 진행된 토스 NEXT 개발자 챌린지를 통해 지원했다. 개발경력 3년(합산 3년 11개월) 미만이라는 제한이 있었기에 약간 애매했지만 *\u0026ldquo;비개발 이력을 제외하고 3년이면 기준을 만족한다\u0026rdquo;*라는 안내가 있어 이력서에 기간별 직무에 대한 설명을 첨부하고 인사팀 판단에 따르기로 했다. 다행히 기준에 벗어나지는 않는다고 생각했는지 이후 코딩테스트, 면접까지 이어질 수 있었다.\n온라인 코딩테스트 이후 면접까지 이어졌고, 기술면접과 컬쳐핏 면접까지 끝난 후 Server Developer 직무에서 최종 합격안내를 받을 수 있었다. 입사까지 기간이 남아있어 처우 관련 협의를 진행했으나 당시 군인이라는 특성상 인사팀에서도 근거로 삼을 데이터가 적어 일부 난항을 겪었고, 그러던 중 보안팀에서 연락이 와 Security Researcher로의 재지원을 권유받았다. 개발과 보안 양쪽에 관심이 있었고, 설명을 들어본 후 보안팀에서 좀더 재미있게 일할 수 있을 것 같았기에 이를 진행했다.\n컬쳐핏 면접은 가치관에 대한 내용인 만큼 이미 Server Developer 지원에서 합격했기에 중복해서 다시 볼 필요는 없었지만, Security Researcher로 직무가 달라진 만큼 기술면접은 보안과 관련된 내용으로 다시 진행했다. 기술면접을 통과한 후 기존 Server Developer에서보다 조금 더 높아진 처우로 Security Researcher 합격안내를 받을 수 있었다.\n다음에 나올 쿼라와 함께 끝까지 고민했던 회사로, 마지막 거절메일을 보내는 순간까지 고민이 끊이지 않았다. 각 회사가 각자 장점이 뚜렷했고 하나가 다른 하나의 상위호환이 아닌 만큼 어느 쪽을 택해도 아쉬움이 남을 수밖에 없는 상황에서, 개발자로서 실리콘밸리 업무를 경험해보고 싶다는 생각과 다른 몇몇 이유들로 인해 최종적으로 쿼라로의 이직을 선택하게 되었다.\n쿼라(Quora) 직무: Software Engineer 결과: 합격(최종 결정) 영미권에서는 잘 알려진 Quora라는 QnA 커뮤니티와 상대적으로 최근 출시된 Poe라는 AI 챗봇 서비스를 개발하는 회사이다. 전세계 Full-Remote 제도를 운영하고 있어 100% 재택근무가 적용되며, 한국의 경우 사당에 공유오피스가 있어 자율 출퇴근이 가능하다. 직원들이 세계 곳곳에 퍼진 만큼 Coordination Hours는 본사인 미국 태평양 표준시에 맞춰져 있어 한국 기준 근무일은 화요일 ~ 토요일이 된다는 특징이 있다. 전체 인터뷰 과정은 마지막 하나를 제외하면 모두 영어로 진행되었다.\nCore Infrastructure 팀으로 지원했고, 서류 통과 후 Recruiter Call을 거쳐 라이브코딩 인터뷰를 진행했다. 라이브코딩은 몰로코에서와 유사하게 채점이 아닌 메모장에 가까운 형식이었으나, 쿼라에서는 내가 입력한 코드를 실행해보는(+면접관에게 이를 보여주면서 설명하는) 것까지는 가능한 환경이었다. \u0026ldquo;1초 내에 정답이 나오기만 하면 되는\u0026rdquo; 대부분의 알고리즘 코딩테스트와는 달리, 풀이를 제시하고 이를 코드로 구현한 후에도 \u0026ldquo;조금 더 일반화할 수 있을까?\u0026rdquo;, \u0026ldquo;혹시 (실행 시간에는 영향이 없지만) 메모리를 O(N)이 아닌 O(1)만 더 쓰는 풀이도 가능할까?\u0026rdquo; 같은 추가 질문들이 들어왔고 이를 모두 대답하자 두 번째 문제가 나왔다. 시간 문제로 두 번째 문제는 빠르게 풀이를 설명하고 코드를 작성했고, 며칠 후 통과했다는 메일을 받을 수 있었다.\n다음으로는 Language Practical Interview라는 조금은 독특한 방식의 인터뷰를 진행했다. 라이브코딩과 비슷하게 웹 IDE 화면을 공유하지만 빈 파일에서 시작하는 대신 이미 존재하는 프로젝트 코드베이스에서 시작해 제시된 추가 기능을 구현하는 방식으로, 주어진 프로젝트 폴더에서 \u0026ldquo;많은 코드파일 중 어떤 걸 봐야 하는지\u0026rdquo;, \u0026ldquo;모든 코드를 전부 이해하지 않고도 핵심을 파악할 수 있는지\u0026rdquo;, 써보지 않은 라이브러리도 주변 코드나 에러메시지를 보고 적절히 활용할 수 있는지\u0026rdquo; 와 같은 실무감각을 평가하는 데 좋은 방식이라는 인상을 받았다. 면접을 진행한 분도 \u0026ldquo;실제로 task를 시간 내에 해결하는지 여부보다는 에러메시지를 보고 어떻게 대처하는지, 코드의 다른 함수를 보고 어떻게 참고하는지와 같은 사고과정을 본다\u0026rdquo; 라는 말을 하셨기에, 막혔을 때 가만히 고민하는 대신 내가 지금 코드의 어디를 보고 있는지 드래그로 표시하거나 코드 동작에 대해 내가 어떤 가설을 세우고 있는지 계속 설명하는 등 열심히 어필했고(?) 그게 어느정도 유효하게 작용했던 것 같다.\nPractical Interview를 진행한 같은 날 Engineering Value Interview와 Hiring Manager Interview가 이어서 진행되었다. 업무경험 관련 면접과 함께 일종의 컬쳐핏 면접이라고도 볼 수 있는데, 쿼라가 중요하게 생각하는 가치와 본인의 지난 업무경험을 어느정도 일치시킨 사례를 드는 식으로 진행되었다. 많은 한국인들이 그렇듯이 영어로 들어오는 질문은 모두 이해했고 그에 대해 답하고 싶은 내용도 있지만, 막상 영어로 말하려니 문장이 온전히 나오지 않을 때가 있어 조금 버벅거린 끝에 어찌저찌 마무리할 수 있었다. 마지막 Hiring Manager Interview는 한국어로 진행되었으며, 앞서와 비슷한 성격의 질문들이었지만 한국어로 진행된 만큼 말하고 싶은 내용들을 한층 편하게 표현할 수 있었다.\n이후 최종 합격 안내와 함께 오퍼레터를 받을 수 있었다.\n구직 외 근황 이사 군에서 나오면서 가장 큰 고민은 역시 주거 문제였다. 이전까지 숙소를 제공해주던 직장에 있었다보니 자취 경험은 대학생 시절이 마지막이고, 대학생 시절에는 \u0026ldquo;학교까지 걸어서 O분 이내\u0026rdquo; 라는 거의 유일하다시피 한 조건으로도 후보군을 좁힐 수 있었지만 이제는 \u0026ldquo;어느 지역에 살것인지\u0026rdquo; 부터 결정해야 했기에 고민의 정도가 달라질 수밖에 없었다. 여러 앱을 다운받고 부동산을 들릴 때마다 군 숙소가 얼마나 큰 복지인지 새삼 체감할 수 있었다. 서울에서 침대 \u0026amp; 생활공간 포함된 숙소를 전기/가스 포함 월 O만원에 쓰다가 이젠 그 10배 이상을 낸다.. 돌아다닌 끝에\n주변 생활시설 회사까지의 거리/교통편 방 퀄리티 셋 모두를 나름 만족하는 곳을 찾아 이사했다.\n화이트햇 스쿨(WHS) 멘토 취임 올해부터 화이트햇 스쿨3에서 멘토로 활동하고 있다. 나 스스로가 BoB 수료생이기도 하고 작년 DEFCON 출전도 BoB에서의 연이 이어져서 달성할 수 있었던 만큼, 멘토로 활동하면서 후배 멘티들에게도 좋은 영향을 끼칠 수 있도록 노력중이다. 전역 직후 새직장 온보딩과 기간이 맞물려 약간 바빠지긴 했지만 끝까지 좋은 결과로 이어지면 좋겠다.\n자세한 설명은 어렵지만 내가 지원동기로 어필했던 사업부 특성상 (미국 기준) 외국인의 진입이 어려운 편이라고 추정된다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n혹 관심있는 사람은 \u0026ldquo;Quant Math Puzzle\u0026rdquo; 같은 키워드로 검색해보면 예제를 찾을 수 있다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nKITRI에서 BoB와 함께 운영하는 정보보안 인재양성 프로그램으로 공식 홈페이지에서 자세한 내용을 확인할 수 있다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2025년 6월 8일","externalUrl":null,"permalink":"/blog/job-transition-after-7-years/","section":"Posts","summary":"계속 “이직 후기 써야지…” 라고 생각만 하고 미루다가 결국 이직 1주차가 지난 지금에서야 작성하는 후기(?)\n사실 About 페이지나 그간의 대회 후기에서나 반쯤은 드러내놓고 써서 새삼스럽긴 하지만, 지난주, 정확히는 2025년 5월 31일을 기준으로 7년간의 군생활을 마치고 공군 대위에서 예비역 공군 대위로 칭호가 바뀌었다. 전역하게 되면 뭔가 심정적으로도 크게 동요할 줄 알았는데 그새 1주일이 지나서인지 나름 익숙해진 것 같다. 올해부터 군 전직지원기간이 늘어서 지난 2월부터 출근 안 하고 거의 4개월 휴가를 보냈던 탓도 있는거같음\n","title":"7년 근속(?) 후 이직 후기","type":"posts"},{"content":"","date":"2025년 6월 8일","externalUrl":null,"permalink":"/category/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"2025년 6월 8일","externalUrl":null,"permalink":"/","section":"Nyan101 Blog","summary":"","title":"Nyan101 Blog","type":"page"},{"content":"","date":"2025년 6월 8일","externalUrl":null,"permalink":"/blog/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"2025년 6월 8일","externalUrl":null,"permalink":"/tag/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"요새는 이렇게 살고 있습니다\n","date":"2025년 6월 8일","externalUrl":null,"permalink":"/category/%EA%B7%BC%ED%99%A9/","section":"Categories","summary":"요새는 이렇게 살고 있습니다\n","title":"근황","type":"categories"},{"content":"","date":"2025년 6월 8일","externalUrl":null,"permalink":"/tag/%EC%9D%B4%EC%A7%81/","section":"Tags","summary":"","title":"이직","type":"tags"},{"content":"올해는 알고리즘 대회 하나(메타 해커컵), AI 대회 하나(국방 AI 경진대회)를 제외하면 다 보안 관련 대회들이라 밸런스가 안 맞다는 이유로, 이것저것 다양하게 해서 CTF 편, PS/알고리즘 편, 기타 편을 따로 작성했던 2023년과는 달리 글 하나에 몰아 작성하기로 했다. 이제 나갈 수 있는 대회들이 점점 줄어간다\u0026hellip;\nDEFCON CTF \u0026ldquo;살면서 데프콘 본선도 와보고\u0026hellip;\u0026rdquo;\n일시 : 8.7. ~ 14. (출국일정 기준) 주최/주관 : DEFCON Conference 결과 : 세계 9위 같은 직장 동기/후배들, BoB 지인들과 Cold Fusion 이라는 이름으로 여러 CTF를 나갔고, 예선에서 다같이 힘을 합친 끝에 DEFCON 본선에도 진출했다. 한국에서 온라인으로 참가할지, 이왕 진출한거 미국까지 갈지 고민 끝에 개인 휴가를 털어가며 라스베가스행 비행기표를 끊었다. 약간의 우여곡절도 있었지만, 1인분을 했다고 자신있게 말하긴 어려워도 나름 문제풀이에 기여도 하면서 버스 탑승료 정도는 냈다고(?) 생각한다.\n매번 국내 대회만 나가면서 세계대회 본선 참가1는 처음 겪어보는데, 그래도 나름 보안 분야에서 일하면서 버킷리스트 하나는 이룬 것 같다. 자세한 후기에 대해서는 라스베가스 여행기와 합쳐 나중에 다시 글을 적어볼 생각이다. 아마도 언젠가는 쓰겠지\nCCE 사이버공격 방어대회 \u0026ldquo;솔직히 2명이서 4등했으면 가산점 줘야하는거 아님??\u0026rdquo;\n일시 : 8.3.(예선), 9.11.(본선) 주최/주관 : 국가정보원 / 국가보안기술연구소 결과 : 공공기관 4위 작년에 이어 올해도 출전한 국정원 주관 해킹대회인 CCE이다. 군인 4명이서 팀을 꾸려 나갔는데, 부대 사정으로 2명이 본선 참가를 못 하게 되면서 2명이서 몸을 비틀어가며 대회를 진행했다. 올해는 CSK라는 국제 사이버훈련과 함께 진행되면서 대회 도중 VIP가 직접 방문하는 시간이 있기도 했고, 초반 2등까지 올라가면서 나름 기대를 했지만 결국 마지막 30분을 남겨두고 4등으로 떨어지면서 수상이 좌절됐다. 2022년에도 4등으로 아쉽게 수상하지 못한 적이 있었던 만큼, 3등까지 수상권인 대회에 4등만 두번 겪다보니 대회가 끝나고도 멘탈을 회복하는 데 시간이 좀 걸렸다(\u0026hellip;)\n화이트햇 콘테스트 \u0026ldquo;다행히 여기선 3등함\u0026rdquo;\n일시 : 10.19.(예선), 11.19.(본선) 주최/주관 : 국방부 / 사이버작전사령부 결과 : 간부트랙 3위(사이버작전사령관상) 사이버사령부에서 주관하는 CTF이다. 올해는 일반부가 없어진 대신 청소년 / 군(용사) / 군(간부)의 3가지 트랙으로 변경되면서 상금이 늘어났다. CCE 때와는 달리 4명 팀으로 무사히(?) 본선까지 참가할 수 있었고, 마지막에 조금 아슬아슬했지만 다행히 최종 3위로 수상권에 들 수 있었다. 2024년 마지막 사이버보안 관련 대회였는데 다행히 나름의 성과를 거두며 마무리할 수 있어 만족스러운 대회였다.\nMAICON 국방 AI 경진대회 \u0026ldquo;이제 국방해커톤 대신 AI 대회 합니다\u0026rdquo;\n일시 : 11.27. ~ 29.(본선) 주최/주관 : 국방부 / 국방오픈소스아카데미 결과 : 우수상(공군참모총장상) 기존 국방 해커톤이 더 이상 열리지 않고 예산을 국방 AI 경진대회에 올인한 모양이다. 예선은 코딩테스트였는데 일반적인 알고리즘 방식의 문제가 아니라 pandas, pytorch를 다루는 문제가 나왔고, 이를 통과한 사람들끼리 5인 팀을 꾸려 본선에 참가하는 방식이었다. 본선은 주어진 자율주행 로봇을 코딩해 주어진 미션(피아인식, 적외선 레이저 발사)을 수행하는 방식으로, 처음 겪어보는 방식이었지만 세트장 구성이나 진행방식 등 여러모로 신경을 쓴 게 느껴지는 대회였다.\n본선 주행 기회가 2번밖에 주어지지 않는 점이 약간 아쉬웠는데, 나름 준비가 철저하다고 생각했음에도 첫 주행에서 미스가 나는 바람에 두 번째 주행에서 간절한 마음으로 미션 성공을 기도했다. 결과적으로 우수상을 받긴 했지만 1등과의 차이가 얼마 나지 않는 만큼 만족스러움과 아쉬움이 함께하는 대회였다. 20대 초반 용사들은 밤샘도 잘 하던데 30살 넘은 대위들한테 밤샘 두번은 좀 힘들긴 했음\n메타 해커컵 \u0026ldquo;보급티셔츠(?) 하나 더 추가\u0026rdquo;\n주최/주관 : 메타(Meta) 결과 : Round 2 진출 + 티셔츠 획득 페이스북 운영사인 메타(Meta)에서 주관하는 프로그래밍 대회이다. Round 1에서 World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다. 어딘가 익숙한 느낌이 든다면 2023년도 설명 복붙 맞습니다. 올해는 기존의 대회를 Human Track 으로, NeurIPS 학회와 함께 AI가 전체 코드를 작성하는 별도의 트랙을 AI Track 으로 구분해 진행했다. 대회 당시만 해도 AI가 점차 성능이 좋아지는 걸 느꼈는데, 이 글을 쓰는 12월에는 GPT o3가 코드포스 기준 2727점을 달성했다는 소식이 나왔다. 과연 인간 코더의 설 자리는\u0026hellip;\nRound 1 절대평가로 참가자들 중 상위 5000명이 Round 3에 진출 결과 : 585위로 Round 2 진출 Round 2 Round 2 참가자들 중 상위 500명이 Round 3에 진출 1문제 이상을 해결한 상위 2000명의 참가자들에게 티셔츠 제공 결과 : 859위로 마감 Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 결과적으로 올해도 티셔츠를 얻는 데 성공했다. 작년이랑 거의 동일한 디자인에 색조만 살짝 옅어진 형태로 소매의 Meta 까지 동일하다.\n어느새 2025 전역의 해가 밝았다. 어떻게 보면 지금까지는 주어진 틀 안에서 객관식의 선택지2가 주어졌다면, 이젠 진로에 대해 주관식으로 답안을 써 내야 하는 만큼 여러 고민을 하는 중이다.\n세계대회 본선에 진출했어도 참가하지 못한 [검열됨]같은 대회는 있지만\u0026hellip;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n그나마도 선택권이 항상 주어지지는 않는\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2024년 12월 31일","externalUrl":null,"permalink":"/blog/contests-in-2024-review/","section":"Posts","summary":"올해는 알고리즘 대회 하나(메타 해커컵), AI 대회 하나(국방 AI 경진대회)를 제외하면 다 보안 관련 대회들이라 밸런스가 안 맞다는 이유로, 이것저것 다양하게 해서 CTF 편, PS/알고리즘 편, 기타 편을 따로 작성했던 2023년과는 달리 글 하나에 몰아 작성하기로 했다. 이제 나갈 수 있는 대회들이 점점 줄어간다…\nDEFCON CTF “살면서 데프콘 본선도 와보고…”\n","title":"2024년도 대회 후기","type":"posts"},{"content":"","date":"2024년 12월 31일","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"2024년 12월 31일","externalUrl":null,"permalink":"/tag/%EB%8C%80%ED%9A%8C/","section":"Tags","summary":"","title":"대회","type":"tags"},{"content":"","date":"2024년 12월 31일","externalUrl":null,"permalink":"/series/%EC%97%B0%EB%A7%90-%ED%9B%84%EA%B8%B0/","section":"Series","summary":"","title":"연말 후기","type":"series"},{"content":"CTF 같은 해킹대회나 PS 알고리즘 대회는 아니지만, 소소한 다른 것들을 정리할 포스트도 필요하다고 생각해 따로 작성했다. 막상 제대로 적을 건 둘밖에 없긴 하지만\n정보보안기사 자격증 취득 \u0026ldquo;3트만에 결국\u0026hellip;! 이제 쌍기사입니다\u0026rdquo;\n올해 5월 정보보안기사 실기에 합격해 보안기사 자격증을 취득했다. 작년 필기에 합격하고 실기를 떨어졌었는데, 5월에 응시한 실기시험의 서술형 항목으로 SQL injection과 같이 날로 먹을 수 있는 익숙한 분야에서의 고배점 문항이 출제되어 합격의 기쁨을 얻을 수 있었다.\n+ 이제 정보처리기사, 정보보안기사 둘 다 있으니 Information Knight 드립 칠 수 있다(?)\nTOEIC 어학성적 취득 \u0026ldquo;말해보카와 넷플릭스에 이 영광을 바칩니다\u0026rdquo;\n7월 말 토익시험을 봤다. 이전에 본 성적이 2년이 지나 만료되기도 했고, 당시 말해보카라는 영어공부 앱을 설치해 한창 사용하던 시기여서 한번쯤 영어 어학시험을 다시 봐야겠다는 생각을 했었다. 어차피 군인할인 적용하면 응시료도 반값이다보니 치킨 1마리 값으로 응시할 수 있었는데, 생각보다 높은 점수가 나와 한동안 혼자 뿌듯해했던 기억이 있다.\n기타 사실 이외에도 직장에서 받은 업무 관련 표창장이나 교육우수 상장, 우수제안 수상 등 소소한 성과들이 있지만 아무래도 공개된 장소에 올리기엔 조금 어려운 내용들이다보니 아쉬움이 있다. 이제 전역도 1년 반 정도밖에 남지 않았는데 앞으로 남은 기간 연구도 좀 하고 열심히 준비해야겠다.\n","date":"2023년 12월 31일","externalUrl":null,"permalink":"/blog/others-in-2023-review/","section":"Posts","summary":"CTF 같은 해킹대회나 PS 알고리즘 대회는 아니지만, 소소한 다른 것들을 정리할 포스트도 필요하다고 생각해 따로 작성했다. 막상 제대로 적을 건 둘밖에 없긴 하지만\n정보보안기사 자격증 취득 “3트만에 결국…! 이제 쌍기사입니다”\n","title":"2023년도 후기 - 기타(ETC) 편","type":"posts"},{"content":"2023년 올해도 CTF 편에 이은 알고리즘(PS) 편이다. 올해는 페이스북 메타 해커컵 티셔츠, SCPC 성불 모두 이룬 나름 의미있는 해였다.\n구글 코드잼(Google Code Jam) \u0026ldquo;이제는 역사의 흔적이 되어버린\u0026hellip;\u0026rdquo;\n주최/주관 : 구글(Google) 결과 : 운영 종료 매년 구글에서 진행하는했던 프로그래밍 대회이다. 지난 글에서 썼듯이, 올해부터는 더 이상 열리지 않는다.\n메타 해커컵(Meta Hacker Cup) \u0026ldquo;올해 티셔츠는 구글 대신 메타에서 받았습니다\u0026rdquo;\n주최/주관 : 메타(Meta) 결과 : Round 2 진출 + 티셔츠 획득 페이스북 운영사인 메타(Meta)에서 주관하는 프로그래밍 대회이다. Round 1에서 World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다.\nPractice Round 이전 Qualification Round와 달리, 대회에 영향을 주지 않는 연습 라운드이다. 결과 : 미참가 Round 1 절대평가로 일정 점수 이상을 해결하면 Round 1로 진출 1문제 이상을 해결하면 Round 1 진출1 결과 : 44/100으로 Round 2 진출 Round 2 Round 2 참가자들 중 상위 500명이 Round 3에 진출 1문제 이상을 해결한 상위 2000명의 참가자들에게 티셔츠 제공 결과 : 768위로 마감 Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 World Final 천상계(\u0026hellip;) 올해는 코드잼도 없어 이번에야말로 해커컵 티셔츠를 받겠다는 마음으로 대회에 참가했다. Round 1 진행 중 갑자기 답안 제출이 안 되는 문제가 발생했고, 빠르게 SNS를 열어보니 나뿐만이 아닌 많은 사람들이 동일한 문제를 겪고있다는 걸 확인할 수 있었다. 한밤중이었던 만큼 *\u0026ldquo;서버 오류면 다음에 다시 하던지 따로 공지를 올려주겠지\u0026rdquo;*라는 생각으로 자러 들어갔고, 결과적으로 한 문제 이상 푼 사람 모두를 Round 2로 진출시키겠다는 공지가 올라왔다.\n그렇게 Round 2 대회날이 다가왔고, 나와 해커컵 사이의 유구한 역사에 따라 올해도 다른 일정과 겹쳤다(\u0026hellip;) 데이트 약속이 있던 날로, 데이트가 끝난 후 들고 간 노트북을 이용해 대회에 참여했다. 대회를 진행하면서 \u0026ldquo;좀더 해서 Round 3 진출을 노려볼까\u0026hellip;\u0026rdquo; 라는 생각도 들었으나, 피로가 누적된 몸은 *\u0026ldquo;World Final이 아니면 어차피 티셔츠 하나 받는 건 똑같지 않나?\u0026quot;*라는 마음속 속삭임과 타협해 적당한 선에서 잠을 청했다.\n결국 티셔츠 등수(2,000등)와 Round 3 진출등수(500등) 사이인 768등으로 마감했고, 티셔츠를 받았다. 예전 티셔츠와 거의 동일한 디자인이지만 이번엔 어깨에 FACEBOOK이 아닌 ∞ Meta라고 적혀있다는 차이가 있다.\nWoori 코딩 페스티벌 성인부 \u0026ldquo;시상식에서 열린 의문의 동문회(?)\u0026rdquo;\n일시 : 5.13.(예선), 5.27.(본선) 주최/주관 : 우리FIS 결과 : 장려상(언어별 3~4위) 우리FIS에서 진행한 코딩대회로 YBM센터에서 대회를 진행했다는 게 인상적이었다. C, Java, Python의 세 가지 언어 중 하나를 택해 응시할 수 있었으며, 개인적으로 익숙한 Python을 선택했다. 각 언어별로 1~4등까지 총 12명에게 상장과 상금이 제공되는 비교적 작은 규모로 진행되었다.\n언어별 구분을 해 실질적으로 1~4등까지만 수상 가능한데다가, 대회 중에는 제출한 코드의 정답 여부를 알려주지 않다보니 결과를 예상할 수 없는 대회였다. 그랬던 만큼 *\u0026ldquo;어차피 4등 안에 못 들면 5등이나 100등이나 차이 없는 거 아닌가?\u0026quot;*라는 생각과 함께 약간의 도박을 했고2, 어느 정도 통했는지 장려상을 수상했다는 말을 들을 수 있었다.\n시상식에서 해군 대위로 복무중인 대학 1년 선배를 만났다. 둘 모두 Python을 선택했는데 선배가 우수상, 내가 장려상으로 1~4위 중 현역 군인이 2명이라는 수상자 대상 입사설명회를 준비한 우리FIS로서는 아쉽게도 인상적인 결과였다.\n삼성 대학(원)생 프로그래밍 경진대회(SCPC) \u0026ldquo;기나긴 여정 끝에 수상 2회 달성\u0026rdquo;\n일시 : 5.13.(예선), 5.27.(본선) 주최/주관 : 삼성전자 결과 : 5등상 학부 시절부터 매년 참가하는 대회로, 국내 알고리즘 대회 중에서는 ICPC에 버금가는 인지도를 가진 대회이다. 매년 본선에 꾸준히 진출하면서도 막상 수상에선 아쉽게 미끄러지곤 했는데, 올해는 5등상으로 대회를 마무리할 수 있었다.\n올해에도 대회가 끝나고서야 발견한 실수들이 있어 \u0026ldquo;올해도 수상은 내년을 기약해야 하나\u0026hellip;\u0026rdquo; 라며 아쉬워했는데, 5등상이라는 결과를 받으니 뭔가 생각지도 못한 선물을 받은 기분이다.\n그런데 이러면 \u0026ldquo;실수만 안했으면 4등상이었던거 아니야?\u0026rdquo; 라는 생각이 드는 게 또 사람 마음이다.\n온라인으로 진행된 만큼 상장과 상패는 택배를 통해 따로 전달되었다. 이제 수상 2회를 달성해 현대모비스 대회에 이어 SCPC도 더이상 못 나간다(\u0026hellip;)\nRound 1 진행 중, 서버 오류가 생기면서 양수 점수를 얻은 사람 모두를 Round 2로 진출시켰다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n거창하게 썼지만, 그냥 코드 검토 없이 스스로를 믿고(?) 초안 작성하자마자 바로 제출버튼 눌렀다는 뜻이다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023년 12월 30일","externalUrl":null,"permalink":"/blog/PS-contests-in-2023-review/","section":"Posts","summary":"2023년 올해도 CTF 편에 이은 알고리즘(PS) 편이다. 올해는 페이스북 메타 해커컵 티셔츠, SCPC 성불 모두 이룬 나름 의미있는 해였다.\n구글 코드잼(Google Code Jam) “이제는 역사의 흔적이 되어버린…”\n","title":"2023년도 대회 후기 - 알고리즘(PS) 편","type":"posts"},{"content":"작년에 이어 올해도 대회는 꾸준히 나갔지만 후기는 연말에 몰아서 쓰게 됐다(\u0026hellip;)\n올해는 작년과 달리 4인 CTF뿐만이 아니라, 사이버보안 해커톤, 온라인 국제대회 등 다양한 종류의 대회에 나가보았다.\nCCE 사이버공격 방어대회 일시 : 6.10.(예선), 7.12.(본선) 주최/주관 : 국가정보원 / 국가보안기술연구소 결과 : 공공기관 7위 국가정보원에서 주관하는 CTF로 일반인들을 위한 일반분야와 공공기관 종사자들을 위한 공공분야로 나눠 진행된다. 예선은 일반적인 Jeopardy(문제풀이) 방식, 본선은 Jeopardy와 함께 공격받는 Live 서버를 실시간으로 방어하는 방식으로 진행되었다. 작년이랑 설명이 똑같은 것 같다고요? 기분 탓입니다\n본선은 서울 롯데호텔에서 진행되었다.\n4인 대회에 3인 팀을 구성해 나간 만큼 다양한 다과와 퀄리티 높은 식사를대회의 분위기를 즐기는 데 집중했다. 그렇게 일부 문제를 해결했지만 약간의 아쉬움과 함께 공공분야 7등이라는 성적으로 대회를 마칠 수 있었다.\n화이트햇 콘테스트 일시 : 9.16.(예선), 10.26.(본선) 주최/주관 : 대한민국 국방부 / 사이버작전사령부 결과 : 참가에 의의를(\u0026hellip;) 사이버작전사령부에서 주관하는 CTF로 작년에 국방트랙 2등을 했던 대회이다. 올해는 화이트햇 예선과 SCPC(삼성 대학생/대학원생 프로그래밍 경진대회) 본선이 겹치면서 예선 문제를 구경하는 데 의의를 뒀다. 그래도 SCPC는 5등상 받았으니 만족합니다\nCTFZone 일시 : 8.12.(예선), 11.25.(본선) 주최/주관 : BI.ZONE(러시아 보안업체) 결과 : 국제 8위 러시아 보안업체인 BI.ZONE에서 운영하는 국제 CTF 대회이다. 올해 BoB 멘토님과 함께 현직 군 소속 인원들끼리 Call of Duty라는 CTF 팀을 구성해 이런저런 CTF들에 참가해오고 있었는데, 그중 본선까지 진출해 괜찮은 실적을 냈던 대회이다.\n훌륭한 팀원들의 버스에 실려 예선 4위라는 성적으로 본선에 진출했다.\n국제대회인 만큼 본선 역시 온라인으로 진행되었으며, 가산 BoB센터에 모여 밤샘 대회를 진행했다. 최종 8위로 대회를 마쳤다.\n군 사이버보안 전문인력 양성 해커톤 일시 : 12.9. 주최/주관 : 대한민국 국방부, 과학기술정보통신부 / 한국인터넷진흥원(KISA) 결과 : 최우수(1위) 국방부, 과학기술정보통신부에서 주최한 군 사이버보안 전문인력(사이버탈피오트) 양성 해커톤이다. 이제는 익숙해진 사이버 탈피오트라는 슬로건을 내걸고 상당히 큰 규모의 상금이 함께한 대회였다.\n기획서로 평가하는 예선과 현장에서 시제품을 완성해 발표하는 본선으로, 진행되었으며 동기, 후배들과 함께 5인 팀을 구성해 나갔다.\n개발시간이 한정된 만큼 AI나 블록체인 대신 현실성에 집중해 아이디어를 냈는데, 발표에서 이런 부분을 강조했던 점이 높은 점수를 받았는지 최종 1위라는 높은 성적을 받을 수 있었다.\n대회를 마친 다음주, COEX에서 시상식과 함께 군 사이버전문인력 양성 세미나가 진행되었다.\n","date":"2023년 12월 30일","externalUrl":null,"permalink":"/blog/CTF-contests-in-2023-review/","section":"Posts","summary":"작년에 이어 올해도 대회는 꾸준히 나갔지만 후기는 연말에 몰아서 쓰게 됐다(…)\n올해는 작년과 달리 4인 CTF뿐만이 아니라, 사이버보안 해커톤, 온라인 국제대회 등 다양한 종류의 대회에 나가보았다.\nCCE 사이버공격 방어대회 일시 : 6.10.(예선), 7.12.(본선) 주최/주관 : 국가정보원 / 국가보안기술연구소 결과 : 공공기관 7위 국가정보원에서 주관하는 CTF로 일반인들을 위한 일반분야와 공공기관 종사자들을 위한 공공분야로 나눠 진행된다. 예선은 일반적인 Jeopardy(문제풀이) 방식, 본선은 Jeopardy와 함께 공격받는 Live 서버를 실시간으로 방어하는 방식으로 진행되었다. 작년이랑 설명이 똑같은 것 같다고요? 기분 탓입니다\n","title":"2023년도 대회 후기 - 해킹(CTF) 편","type":"posts"},{"content":"매년 참가해오던 구글 코드잼이 올해를 끝으로 더 이상 운영되지 않는다는 공지를 내걸었다.\n전역까지 매년 티셔츠 모으려고 했는데ㅠ 들리는 소문에 따르면 구글 코드잼 운영부서가 통으로 구조조정당했다는 말이 있던데\u0026hellip; 코드잼과 함께 킥스타트, 해시코드 같은 다양한 대회들도 함께 종료되면서 매년 해오던 연례행사가 하나 사라진 기분이다. 되돌아보니 거의 5,6년간 해오던 대회였던 만큼 기록이 사라지기 전에 백업해두기로 했다.\n구글 코드잼(Google Code Jam) 대학교 3학년이던 2016년부터 시작해 군 훈련소에 있던 2018년을 제외하면 매년 참가했다. 처음엔 라운드 3 진출도 못 하다가 대학 졸업 이후(2019) 부터는 매년 티셔츠를 받아오는 데 성공했다. 전역까지 티셔츠만 7벌 모으고 싶었는데 이번 일로 4벌에서 단종되면서 도전과제가 강제 종료됐다..\n2016년 첫 참가이다. Qual라운드, Round 1까지 진출하고 2도 못 갔었다. 뉴비 시절\n2017년 마찬가지로 Round 1까지 기록이 있다. 등수로 보면 Round 2는 갔을거같은데 왜 안봤었는지는 모르겠다.\n2018년 민간인 → 소위 전직퀘스트 중이어서 참가 못함\n2019년 첫 티셔츠를 얻은 대회이다. 처음인 만큼 기쁜 마음에 후기 글도 작성했었다.\n2020년 두 번째 티셔츠를 얻은 대회이다. 이 때도 후기 글은 작성했다.\n2021년 세 번째 티셔츠라 그런지 슬슬 후기에서도 배가 부른 모습을 보여준다(\u0026hellip;)\n2022년 이 글이 마지막이 될 줄은 몰랐는데\u0026hellip;\n구글 킥스타트(Google KickStart) 킥스타트는 2017년부터 참가헀다. 일정으로 인해 매번 참가하지는 못했지만, 가끔 좋은 결과를 얻었을 때 실제로 구글로부터 채용 관련 메일이 오는 등1 여러 신기한 경험이 함께했던 대회였다.\n2017년 첫 참가에서 운이 좋았는지 15등을 하고 채용 메일을 받았다. 이 때는 군필이 아니어서 눈물을 머금고 거절메일을 작성했다.\n2018~2022년 슬슬 귀찮아지니 그냥 사진만 올리는 걸로(\u0026hellip;)\n이제 나갈만한 글로벌 대기업 대회는 페이스북메타 해커컵 정도인데, 매년 참가는 하겠지만 굳이 준비를 따로 할것같진 않다.\n한국 국적 남성이실 경우 군필, 또는 면제이셔야 합니다 라는 조건이 붙어있어 실제 채용까지 이어지지는 못함\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023년 6월 6일","externalUrl":null,"permalink":"/blog/farewell-codejam/","section":"Posts","summary":"매년 참가해오던 구글 코드잼이 올해를 끝으로 더 이상 운영되지 않는다는 공지를 내걸었다.\n전역까지 매년 티셔츠 모으려고 했는데ㅠ 들리는 소문에 따르면 구글 코드잼 운영부서가 통으로 구조조정당했다는 말이 있던데… 코드잼과 함께 킥스타트, 해시코드 같은 다양한 대회들도 함께 종료되면서 매년 해오던 연례행사가 하나 사라진 기분이다. 되돌아보니 거의 5,6년간 해오던 대회였던 만큼 기록이 사라지기 전에 백업해두기로 했다.\n","title":"구글, \"코드잼 서비스를 종료합니다\"","type":"posts"},{"content":"2022년도 대회 후기 - 해킹(CTF) 편에 이은 알고리즘(PS) 편이다. 어떻게 2023년 오기 전에 다 쓰기는 하네 현대모비스 대회에서 예상치 못한 소득(feat. 500만원)과 함께 공군해커톤 등 나름 성과가 있었고, 올해도 SCPC 성불은 하지 못했다(\u0026hellip;)\n예선/본선 일자가 명확했던 CTF와 달리 이번에는 기간을 두고 진행한 온라인 대회들이 많아 일시는 생략했다.\n구글 코드잼(Google Code Jam) \u0026ldquo;이제 코드잼 티셔츠만 4장(2019, 2020, 2021, 2022)이 되었습니다\u0026rdquo;\n주최/주관 : 구글(Google) 결과 : Round 3 진출 매년 구글에서 진행하는 프로그래밍 대회이다. Qualification Round → Round 1 → Round 2 → Round 3 → World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다.\nQualification Round 절대평가로 30점 이상의 점수를 얻으면 Round 1로 진출 결과 : 71/100으로 Round 1 진출 Round 1 3개의 라운드(1A, 1B, 1C) 중 하나에서 1500위 이상을 달성하면 Round 2로 진출 결과 : Round 1A에서 357위로 Round 2 진출 Round 2 Round 2 참가자들 중 상위 1000명이 Round 3에 진출 결과 : 502위로 Round 3 진출 Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 Round 3 진출자 전원에게 코드잼 티셔츠 증정 결과 : 최종 459위로 종료 World Final 여긴 논외로 치자 실력 유지를 위한 암묵적인 선으로 Round 3 상위 500명 이내를 목표로 잡았는데, 2019년, 2020년, 2021년에 이어 올해도 목표를 달성해 새 티셔츠를 얻을 수 있었다.\n페이스북메타 해커컵(Meta Hacker Cup) \u0026ldquo;이상하게 해커컵 티셔츠랑은 연이 안 이어지네요\u0026hellip;\u0026rdquo;\n주최/주관 : 페이스북(Facebook) 메타(Meta) 결과 : Round 2 진출 이번에도 티셔츠는 못 받았다 이제 주관사 명칭이 바뀐(?) 프로그래밍 대회이다. 코드잼과 유사하게 Qualification Round에서 World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다.\nQualification Round 절대평가로 1문제 이상을 해결하면 Round 1로 진출 결과 : 50/100으로 Round 1 진출 Round 1 절대평가로 24점 이상의 점수를 얻으면 Round 2로 진출 결과 : 40/100으로 Round 1 진출 Round 2 Round 2 참가자들 중 상위 500명이 Round 3에 진출 1문제 이상을 해결한 상위 2000명의 참가자들에게 티셔츠 제공 결과 : 2817위로 마감 Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 World Final 코드잼과 동일 작년에는 자느라 못 했지만 이번에는 티셔츠를 받겠다는 마음으로 졸린 몸을 이끌었다. 어떻게 한문제만 풀면 티셔츠를 받을 수 있겠다는 계산에 A번 문제를 풀고 다시 잠들었고, 대회 종료 후 결과를 보니 Small만 통과하고 Large 케이스에서 오답 판정이 나왔다(\u0026hellip;) 공식 풀이와 모든 면에서 동일했지만 해시함수를 만들기 귀찮은 나머지 삼각함수($sin(x)$)를 이용해 간단히 만들었는데, 작은 값에서 가까운 $sin(x)$ 값들끼리 서로 유사한 결과를 내게 되면서 해시 충돌이 발생했던 것이다. 커스텀 해시함수 부분만 고치면 바로 정답이 되는 코드여서 더 아쉬웠다. 코드잼 티셔츠는 많은데 왜 해커컵은\u0026hellip;\nMOBIS 알고리즘 경진대회 \u0026ldquo;더이상 MOBIS 대회는 못 나가는 몸이 되었습니다\u0026rdquo;\n주최/주관 : 현대모비스 결과 : 동상(3위) 현대모비스에서 개최한 알고리즘 경진대회로, 운이 좋았는지 500만원이라는 상금을 획득할 수 있었다.\n이건 시상식 직후에 별도의 글을 작성한 적이 있어, 여기서는 자세한 설명을 생략한다. 이때 받은 상금으로 아이패드 프로를 사서 잘 쓰고있다.\n+ 올해부터 3등 이내 수상자는 차후 대회에 출전하지 못 하도록 제약이 생겨 이제 모비스 대회는 더이상 못 나간다(\u0026hellip;)\n군장병 코딩경진대회 \u0026ldquo;어쩌다보니 맥북 프로, 아이패드 프로, 에어팟 프로를 모두 대회로 마련하게 된\u0026hellip;\u0026rdquo;\n주최/주관 : 국방부, 국방오픈소스아카데미 결과 : 3위 매년 국방오픈소스아카데미에서 진행하는 \u0026lt;군장병 공개 SW 해커톤\u0026gt; 대회의 선발과정으로 이뤄지는 코딩경진대회이다. 다른 알고리즘 대회와 유사하게 문제풀이 방식으로 구성되었으며 프로그래머스 플랫폼을 통해 진행되었다.\n앞 3개는 비교적 평이한 코딩테스트 문제들이었는데 마지막 문제에서 SOS(Sum of Subsets) DP를 이용해야 하는 문제가 나왔다. \u0026ldquo;이런 게 나온다고??\u0026rdquo; 라는 생각과 함께 겨우 시간 내에 해결해 제출했고, 그렇게 모든 문제를 풀어 400점 만점을 받았다. 전체 참가자들 중 만점자는 3명뿐이었지만 아쉽게도 제출시간의 차이로 인해 3등상(에어팟 프로)을 받았다. 이미 버즈 라이브를 잘 쓰고있어 에어팟은 당근마켓으로 보내졌다.\n공군 해커톤 \u0026ldquo;함께한 후임들에게 이 모든 영광을 돌립니다\u0026rdquo;\n주최/주관 : 공군본부 결과 : 장려상 공군본부에서 주관하는 아이디어 경연대회이다. 실제 코딩을 진행하지는 않고 아이디어를 제안하고 기획서와 발표를 통해 수상팀을 결정한다. 사실 작년에도 나갔었는데 예선부터 떨어져서 작년 후기에는 포함을 안 시켰다.\n올해는 같은 부대에서 근무하는 후임들과 3인 팀을 이뤄 참가했다. 작년의 탈락을 거울삼아 좀더 신경써서 기획서를 작성했고, 팀원들이 애써준 덕인지 작년과는 달리 본선까지 진출해 최종 수상을 할 수 있었다.\n삼성 대학(원)생 프로그래밍 경진대회(SCPC) \u0026ldquo;이거 점점 어려워지는데(\u0026hellip;)\u0026rdquo;\n주관 : 삼성전자 결과 : 본선 진출 재작년, 작년에 이어 올해도 1,2차 예선, 본선 모두 온라인으로 진행되었다. 결과만 먼저 밝히면 올해도 본선은 나갔지만 성불(수상)은 하지 못했다(\u0026hellip;) 문제가 점점 어려워지는 느낌인데 2023년에는 수상해서 성불하길\u0026hellip;\n","date":"2022년 12월 31일","externalUrl":null,"permalink":"/blog/PS-contests-in-2022-review/","section":"Posts","summary":"2022년도 대회 후기 - 해킹(CTF) 편에 이은 알고리즘(PS) 편이다. 어떻게 2023년 오기 전에 다 쓰기는 하네 현대모비스 대회에서 예상치 못한 소득(feat. 500만원)과 함께 공군해커톤 등 나름 성과가 있었고, 올해도 SCPC 성불은 하지 못했다(…)\n예선/본선 일자가 명확했던 CTF와 달리 이번에는 기간을 두고 진행한 온라인 대회들이 많아 일시는 생략했다.\n","title":"2022년도 대회 후기 - 알고리즘(PS) 편","type":"posts"},{"content":"2022년에는 대회 하나씩 끝날 때마다 후기를 쓰겠다고 다짐했지만 이번에도 결국 연말에 몰아서 쓰게 됐다(\u0026hellip;)\n올해는 작년과는 달리 CTF에서도 나름의 성과가 있던 해였다. 아직 CVE를 찾거나 국방/공공분야라는 울타리를 벗어나지는 못했지만, 그래도 조금씩 경험이 쌓여가는 것 같아 나름 보람있게 보낸 느낌이다. 그런데 논문은 대체 언제 쓰지\u0026hellip;\n국방 사이버보안 경진대회 일시 : 9.15.(본선) 주관 : 군사안보지원사령부(現 국군방첩사령부) 결과 : 우수(2위) 군사안보지원사령부에서 주관하는 전군(육+해+공+국직) 대상 경진대회이다. 작년엔 코로나로 대회가 취소되는 탓에 출전하지 못했지만 다행히 작년 성적을 인정받아 올해 공군 대표단에 포함되어 나갈 수 있었다. 필기와 실기가 합쳐진 형식이었으며, 필기는 보안규정이나 정보보안 관련 지식을 묻는 지필고사 형식, 실기는 Jeopary 형식의 CTF로 진행되었다.\n본선장에 가니 전부 학과 선배, 동기들이어서 \u0026ldquo;이정도면 사실상 집안싸움 아닌가\u0026quot;라는 생각이 자연스럽게 들었는데 실제로도 순위표 까보니 1,2,3등이 전부 사이버국ㅂ\u0026hellip; 결과적으로 해군 대표로 나왔던 동기와 필기 \u0026amp; 실기 모두 동점으로 1위를 다투게 되었다. 모든 점수가 동점인 관계로 실기 CTF의 최종 제출시각을 기준으로 순위를 결정하기로 했고 우리 팀의 제출 시점이 조금 더 늦어 동기가 1위(국방부장관상), 우리 팀이 2위(안보지원사령관상)를 차지했다.\n+ 여담이지만 대회를 한달만 더 늦게 했어도 \u0026ldquo;군사안보지원사령관\u0026rdquo; 대신 \u0026ldquo;국군방첩사령관\u0026rdquo; 직인 찍힌 상장을 받았을 텐데 아슬아슬하게(?) 안보지원사령부 시절 마지막 상장을 가져가게 됐다.\n화이트햇 콘테스트 일시 : 10.15.(예선), 11.19.(본선) 주최/주관 : 대한민국 국방부 / 사이버작전사령부 결과 : 국방트랙 우수(2위) 사이버작전사령부에서 주관하는 CTF로, 일반 / 청소년 / 국방트랙이 별도로 분리되어 진행된다.\n작년에 아쉽게 5위를 했던 기억이 있어 다들 마음을 다잡고 대회에 임했다. 지금까지 모든 대회에서 그랬듯이 본선 초반 빠르게 문제를 풀어 중간 1위를 달성했지만, 중반 이후 더 문제가 풀리지 않아 점차 추격해오는 다른 팀들을 긴장어린 눈으로 바라봤다. 막판에 추가된 문제들 중 암호학 관련 문제들이 포함되어있어 빠르게 추가 점수를 얻었고, 그 덕분인지 대회 마지막까지 최종 2위(합참의장상) 자리를 지켜낼 수 있었다.\n작년에는 결국 수상권 안에 들지 못해 아쉬움이 많이 남았던 대회였는데, 올해는 운이 따랐는지 스타포스(?) +4성에 시상식까지 참석하면서 나름 인상적인 경험을 할 수 있었다.\nCCE 사이버공격 방어대회 일시 : 9.24.(예선), 10.27.(본선) 주최/주관 : 국가정보원 / 국가보안기술연구소 결과 : 전체 15위, 공공기관 4위 1 국가정보원에서 주관하는 CTF로 일반인들을 위한 일반분야와 공공기관 종사자들을 위한 공공분야로 나눠 진행된다. 예선은 일반적인 Jeopardy(문제풀이) 방식, 본선은 Jeopardy와 함께 공격받는 Live 서버를 실시간으로 방어하는 방식으로 진행되었다.\n온라인이었던 작년과 달리, 대구 EXCO에서 오프라인으로 본선을 진행했다.\n작년 실시간 방어 분야에서 헤멨던 만큼 올해는 나름 준비를 해갔지만, 웹서버 패치 방식이었던 작년과 달리 바이너리 패치 방식이어서 거의 손을 대지 못했다. NodeJS서버에 바이너리 실행파일이 올라가있고 이를 패치해 업로드하는 형식이었는데, 바이너리라는 특성상 오류가 나면 디버깅을 하지 못한다는 생각에 팀원들끼리 상의 후에 \u0026ldquo;괜히 잘못 건드려서 SLA Fail 패널티 받느니 그냥 기본점수만 받고 들어가자\u0026rdquo; 라는 합의가 진행됐다.\n여기에 Jeopary 방식은 역대급 난이도를 자랑했다(\u0026hellip;) 우리 팀은 암호학 관련 문제를 겨우 풀었는데 그나마도 해당 주제에 대한 배경지식이 부족해 대회 시간 내내 인터넷에서 논문을 찾아가며 공부해야 풀리는 문제였다. 2020년대에 나온 논문을 참조해가며 Sage 코드를 작성했고, 몇 번의 시행착오 끝에 플래그를 획득할 수 있었다. 당시 중간 스코어보드를 확인해보니 공공분야 3위여서 혹시나 하는 마음으로 기도했지만 13시간 대회 중 종료 1시간 미만을 남겨두고 역전당해 최종 순위는 4위로 내려갔다.후임, \u0026ldquo;매년 아깝게 그럴거면 차라리 그냥 20등을 하세요\u0026hellip;\u0026quot;\n대회가 끝난 후 확인해보니 공공분야 본선에 진출했던 20개 팀들 중 과반이 넘는 12개 팀이 대회 시간동안 1문제도 해결하지 못했다는 것을 알 수 있었다. 조금이라도 풀었으니 그보다는 낫다고 위안삼을 수도 있지만 일반부 스코어보드의 기러기목 오리과의 모 동물이름을 쓰는 괴수집단들을 보면 아직 갈길이 멀다는 걸 새삼 깨닫게 된다.\n대회가 끝난 후 Theori 주관으로 참가자들 간 애프터파티가 열렸다. 오랜만에 보는 낮익은 얼굴들과 즐거운 시간을 보낼 수 있었다.\nDFC 디지털 포렌식 챌린지 일시 : 5.1. ~ 9.30. 주최/주관 : 한국정보보호학회 결과 : 장려상(7위) 한국정보보호학회에서 주관하는 대회로, 매달 다양한 주제의 포렌식 문제가 공개되고 이를 해결해 보고서를 제출하는 형식의 대회이다. 학과 선배, 동기들과 함께 6인 팀을 구성해 나갔으며, 주기적으로 온라인 회의를 통해 각자의 진행상황을 공유했다. 장장 5개월에 걸쳐 진행되는 만큼 페이스 조절이 어려운 대회였다.\n그렇게 긴 여정이 끝나고 최종 7위라는 결과를 얻었다. 6위 팀까지 장려상이 수여된다고 알고있어 다들 낙심하던 중, 해외 팀들의 참여 부족으로 국내 수상팀 숫자에 TO가 하나 늘어나면서 아슬아슬하게 수상 막차를 탈 수 있었다.\n번외로, 상패에 영문 수상명이 \u0026ldquo;Participation Prize\u0026quot;로 되어있어 팀원들끼리 농담조로 \u0026ldquo;이거 참가상 아니야?\u0026rdquo; 라는 말을 했는데 구글검색 결과 장려상이 영어로 Participation Prize라는 사실을 알게 되었다.그냥 내가 영알못이었던 걸로\n분야별 3위까지 상이 수여된다\u0026hellip;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022년 12월 31일","externalUrl":null,"permalink":"/blog/CTF-contests-in-2022-review/","section":"Posts","summary":"2022년에는 대회 하나씩 끝날 때마다 후기를 쓰겠다고 다짐했지만 이번에도 결국 연말에 몰아서 쓰게 됐다(…)\n올해는 작년과는 달리 CTF에서도 나름의 성과가 있던 해였다. 아직 CVE를 찾거나 국방/공공분야라는 울타리를 벗어나지는 못했지만, 그래도 조금씩 경험이 쌓여가는 것 같아 나름 보람있게 보낸 느낌이다. 그런데 논문은 대체 언제 쓰지…\n","title":"2022년도 대회 후기 - 해킹(CTF) 편","type":"posts"},{"content":"TL;DR : 상금 500만원 감사합니다 \u0026gt;_\u0026lt;\n작년에 이어 올해도 현대모비스에서 알고리즘 경진대회를 개최했다.\n이번에도 1등상품으로 최신형 자동차를 내걸었다는 상징성으로 인해 온라인에서 화제가 됐다. 올해는 작년과는 다르게 학생부 / 일반부를 분리해 운영했는데, 직업적인 특성(\u0026hellip;)으로 인해 다소 애매한 포지션이었던 관계로 주최측 문의 후 안내에 따라 일반부로 지원했다.\n예선 작년과 올해 대회를 모두 참가해본 입장에서 꽤 많은 부분이 바뀐 것을 느낄 수 있었다. 대표적인 것들을 뽑아보면 다음과 같다.\n작년(2021)\n프로그래머스 플랫폼 사용 모든 테스트케이스에 대한 정답 여부 공개 실시간 스코어보드 제공 올해(2022)\n구름 플랫폼 사용 정답 여부 비공개(본선에서는 일부 정보 공개) 스코어보드 미제공 정답 여부가 비공개라는 점으로 인해 답안을 제출한 이후에도 \u0026ldquo;과연 제대로 푼 게 맞나\u0026quot;라는 의혹을 떨치기 힘들었고, 그렇게 스스로에 대한 믿음과 의심(?) 속에서 대회를 진행했다. 1~4번 문제를 해결한 후 마지막 문제를 고민했지만 정해가 떠오르지 않았고, 결국 고민 끝에 정해를 찾는 대신 나름대로의 휴리스틱 알고리즘을 설계해 구현했다.\n대회종료 후 결과를 보니 생각했던 휴리스틱이 나름 잘 작동한 것 같아 만족스러웠지만 당연히(?) 맞았다고 생각했던 2번 문제에서 66/150이라는 낮은 점수를 받았다. 후문에 따르면 구름 플랫폼의 스택 메모리 제한이 생각보다 적었고, 이로 인해 재귀함수의 호출 깊이에 한계가 존재했다고 한다(\u0026hellip;)\n그렇게 예선 4등으로 본선에 진출했다. 사실 처음엔 4등~30등까지 제공되는 아이패드 에어를 보고 참가했지만 막상 이렇게 되니 \u0026ldquo;이거 잘하면 3등 안에 들 수도 있겠는데?\u0026rdquo; 라는 생각이 피어올랐다.\n본선 예선에서 수많은 참가자들의 항의 끝에 본선에서는 예선에 비해 일부 규칙이 바뀌었다.\n모든 테스트케이스를 통과한 경우 정답임을 알려주었다 각 테스트케이스에 대해 수행시간 / 사용 메모리를 알려주었다 정답인 경우 확실히 정답임을 알려준다는 점에 힘입어 예선에서 겪었던 많은 부담감이 사라졌고, 한결 편한 마음으로 참가할 수 있었다. 다만 대회를 진행하는 도중 3번 문제의 지문이 다소 모호하게 서술되었는데, 문장의 해석에 대한 모든 질문에 \u0026ldquo;대회의 공정성을 해칠 수 있어 답변하지 않는다\u0026rdquo; 라는 기계적인 답변만이 돌아온 것은 다소 아쉽게 느껴졌다. 풀이에 대한 힌트가 아닌 문제 자체의 조건을 이해하는 데 있어서의 안내는 전체공지 등을 통해 가능하지 않을까라는 생각이었지만, 결국 대회 도중에는 이에 대한 추가적인 답변을 받지 못한 상태에서 본선이 종료되었다.\n예상했던 대로 3번 컨셉카 전시 문제에서 그렇게 높은 점수를 받지는 못했지만 4번에서 생각보다 높은 점수를 받았기에 약간의 기대를 가지고 최종 순위가 공개되기를 기다렸다.\n본선이 끝난 다음주 월요일 순위가 공개되었고, 일반부 최종 3등이라는 결과를 얻었다. 원래 예상했던 아이패드에 비해 무척 높은 액수의 상금도 받고, 별도 시상식에도 초대되는 등 높은 성취긴 하지만 사람 마음이라는게 워낙 간사해서 \u0026ldquo;만일 3번 문제에서 조금만 더 높은 점수를 받았더라면 상금이 2배인데\u0026rdquo; 라는 약간의 아쉬움이 함께하는 결과였다.\n시상식 그렇게 역삼역 현대모비스 본사에서 열리는 시상식에 초대되었다. 학생부 / 일반부 각 1~3등까지 총 6명의 수상자와 대회 운영진 분들이 함께했다.\n예상을 안 헀던 건 아니지만 대부분이 다른 대회들에서 자주 봐 친숙했던 분들이었고, 오랜만에 만나 안부를 전할 수 있었다.\n상금에서 기타소득세 4.4%가 공제된 478만원이 입금된다는 안내와 함께 기념 상패를 받았다.\n","date":"2022년 7월 17일","externalUrl":null,"permalink":"/blog/mobis-algorithm-contest-2022-review/","section":"Posts","summary":"TL;DR : 상금 500만원 감사합니다 \u003e_\u003c\n작년에 이어 올해도 현대모비스에서 알고리즘 경진대회를 개최했다.\n이번에도 1등상품으로 최신형 자동차를 내걸었다는 상징성으로 인해 온라인에서 화제가 됐다. 올해는 작년과는 다르게 학생부 / 일반부를 분리해 운영했는데, 직업적인 특성(…)으로 인해 다소 애매한 포지션이었던 관계로 주최측 문의 후 안내에 따라 일반부로 지원했다.\n","title":"2022 현대모비스 알고리즘 경진대회 후기","type":"posts"},{"content":" 학습 진행상황 기록하기 이전 글에서 작성한 코드는 tqdm 라이브러리를 통해 \u0026ldquo;학습이 얼마나 진행되었는가\u0026quot;를 실시간으로 출력했다. 이를 통해 실행이 끝나기까지 막연히 기다리기만 하는 일반적인 코드와는 달리, 각 epoch당 남은 예상시간과 epoch별 train/val loss, accuracy를 출력해 학습이 진행됨에 따라 정확도가 높아지는 모습을 로그로 남길 수 있었다.\n지난번 코드의 출력 일부를 아래에 옮겼다.\nUsing device: cuda ----------------- \u0026gt; Epoch 1/5 100%|██████████| 200/200 [00:05\u0026lt;00:00, 34.14it/s] val Loss: 2.3033 Acc: 0.1043 (1043/10000) ----------------- \u0026gt; Epoch 2/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.52it/s] train Loss: 1.4675 Acc: 0.4736 (18944/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.41it/s] val Loss: 1.2823 Acc: 0.5465 (5465/10000) ----------------- \u0026gt; Epoch 3/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.53it/s] train Loss: 1.0625 Acc: 0.6243 (24970/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.35it/s] val Loss: 1.1661 Acc: 0.5980 (5980/10000) ... 그런데 단순히 텍스트만 출력하지 않고, 학습 진행상황에 따른 지표를 그래프로 보고 싶다면 어떻게 해야 할까? 각 step별 loss, acc값을 리스트에 저장하고 matplotlib를 사용하는 것도 한 가지 방법이다. 그러나 이런 방법은 학습이 다 끝난 후에야 결과를 확인할 수 있고, 매 학습에서의 결과를 통합해 관리하기 어렵다는 단점이 있다. 이번 글에서는 wandb를 통해 모델의 학습 진행상황을 추적하고 이를 관리하는 법에 대해 알아보자.\nwandb란 무엇인가 \u0026amp; 가입 및 초기 설정 wandb는 Wan DB가 아니라 Weight \u0026amp; Bias의 약자로, 모델의 학습 진행상황을 추적하고 자동화된 파라미터 튜닝을 지원하는 웹 도구이다. 공식 홈페이지(https://wandb.ai/)에서 github나 gmail 계정을 이용해 쉽게 가입이 가능하며, 처음 로그인하면 다음과 같은 화면을 볼 수 있다.\n터미널에서 다음 명령어를 통해 wandb를 설치하고 웹에 표시된 API Key를 입력하자.\npip install wandb wandb login 이제 파이썬에서 wandb를 사용할 수 있다.\nwandb를 이용해 학습 진행상황 로깅하기 wandb의 주요 기능은 다음과 같다.\n진행상황 추적을 위한 세션 관리(Worksplace, Table) 매 실험(run)마다 사용했던 설정값(config) 기록 실험이 진행되는 동안 주요 값(loss, acc 등) 추적 실험이 진행되는 동안 CPU, GPU, 메모리 사용량 등 추적 실험이 진행되는 동안 모델 파라미터들의 그래디언트, 파라미터값 추적 보고서 관리(Reports) 자동화된 하이퍼파라미터 튜닝(Sweeps) 모델 버전 관리(Artifacts) 여기서는 wandb의 로깅 기능에 집중하자. 앞서 wandb를 설치하고 API Key를 입력했다면 아래와 같은 코드를 통해 wandb 세션(run)을 시작할 수 있다.\nimport wandb config = { \u0026#39;batch_size\u0026#39; : 32, \u0026#39;epoch\u0026#39; : 4, \u0026#39;some-other-config\u0026#39; : \u0026#34;you can put any value here\u0026#34; ... } wandb.init(project=\u0026#39;my-wandb-project\u0026#39;, name=\u0026#39;run-1\u0026#39;, config=config, save_code=False) wandb.init()에서의 주요 파라미터는 다음과 같다.\nproject : 프로젝트명. 웹에서 \u0026ldquo;Create new project\u0026quot;로 생성한 프로젝트명을 사용하거나 새로운 이름을 사용할 수 있다. 존재하지 않는 프로젝트명을 입력하면 자동으로 해당 이름을 가진 새로운 프로젝트가 생성된다. name : 세션의 이름. 입력하지 않으면 자동으로 랜덤한 이름이 배정된다. config : 세션에서 사용한 주요 설정값. key-value 형태의 dictionary로 전달한다. save_code : True인 경우 해당 세션을 수행한 코드를 함께 저장한다. 세션이 활성화되면 wandb.log()를 통해 해당 세션에서 값들을 기록할 수 있다.\nwandb.log({\u0026#39;var1\u0026#39; : value, \u0026#39;var2\u0026#39; : value2}) 값 기록은 위와 같은 dictionary 형태로 이루어지며, wandb.log()를 호출할 때마다 동일한 키에 대한 값들이 누적되어 기록된다. 단순 스칼라(scalar) 값 외에도 히스토그램, 산포도와 같은 차트를 비롯해 이미지, 사운드, 비디오 등 다양한 형태를 기록할 수 있다.(참조)\n기록이 모두 끝나면 wandb.finish()를 통해 현재 활성화된 세션을 종료할 수 있다.\ntrain_model() 함수에 wandb 코드 추가하기 이제 이전 글에서 작성했던 train_model() 함수에 wandb와 연동하는 코드를 추가해보자. 기존 코드에서 추가된 부분을 주석으로 강조했다.\ndef train_model(net, criterion, optimizer, dataloaders_dict, num_epochs, model_name=\u0026#39;\u0026#39;): device = \u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39; net.to(device) print(f\u0026#34;Using device: {device}\u0026#34;) ### config값을 저장한 dict 생성 ### config = { \u0026#39;model_name\u0026#39; : model_name, \u0026#39;batch_size\u0026#39; : dataloaders_dict[\u0026#39;train\u0026#39;].batch_size, \u0026#39;epoch\u0026#39; : num_epochs, \u0026#39;criterion\u0026#39; : criterion, \u0026#39;optimizer\u0026#39; : optimizer, \u0026#39;some-other-config\u0026#39; : \u0026#39;you can put any value here\u0026#39; } ### 새 wandb 세션 시작 ### wandb.init(reinit=True, project=\u0026#39;my-wandb-project\u0026#39;, config=config) ### 모델 파라미터(그래디언트 등) 추적을 위한 .watch 호출 ### wandb.watch(net) for epoch in range(num_epochs): print(\u0026#34;-----------------\u0026#34;) print(f\u0026#34;\u0026gt; Epoch {epoch+1}/{num_epochs}\u0026#34;) for phase in [\u0026#39;train\u0026#39;, \u0026#39;val\u0026#39;]: torch.set_grad_enabled(phase == \u0026#39;train\u0026#39;) if phase == \u0026#39;train\u0026#39;: net.train() else: net.eval() if (epoch == 0) and (phase == \u0026#39;train\u0026#39;): continue epoch_loss = 0.0 epoch_corrects = 0 for x,y in tqdm(dataloaders_dict[phase]): x, y = x.to(device), y.to(device) output = net(x) loss = criterion(output, y) if phase == \u0026#39;train\u0026#39;: optimizer.zero_grad() loss.backward() optimizer.step() _, y_pred = torch.max(output, 1) epoch_loss += loss.item() * x.size(0) epoch_corrects += torch.sum(y_pred == y.data) ### train phase 한정으로 각 batch마다 loss를 기록 ### if phase == \u0026#39;train\u0026#39;: wandb.log({\u0026#39;train_loss\u0026#39; : loss}) epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset) epoch_acc = epoch_corrects.double() / len(dataloaders_dict[phase].dataset) ### 각 epoch마다 해당 phase에서의 loss, acc을 기록 ### wandb.log({ f\u0026#39;{phase}_epoch_loss\u0026#39; : epoch_loss, f\u0026#39;{phase}_epoch_acc\u0026#39; : epoch_acc }) print(f\u0026#34;{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f} ({epoch_corrects}/{len(dataloaders_dict[phase].dataset)})\u0026#34;) ### 학습이 끝난 후 세션 종료를 위한 .finish() 호출 ### wandb.finish() 이제 다양한 상황에서 위 train_model() 함수를 호출해보자. 코드를 단순화하기 위해 하이퍼파라미터를 튜닝하는 대신 서로 다른 모델을 불러와 학습하는 것으로 대체했다.\n# resnet18로 학습 진행 net = models.resnet18() net.fc = nn.Linear(in_features=net.fc.in_features, out_features=10) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(params = net.parameters()) train_model(net, criterion, optimizer, dataloaders_dict, num_epochs=4, model_name=\u0026#39;resnet18\u0026#39;) # resnet34로 학습 진행 net = models.resnet34() net.fc = nn.Linear(in_features=net.fc.in_features, out_features=10) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(params = net.parameters()) train_model(net, criterion, optimizer, dataloaders_dict, num_epochs=4, model_name=\u0026#39;resnet34\u0026#39;) # vgg16으로 학습 진행 net = models.vgg16(pretrained=True) net.classifier[6] = nn.Linear(in_features=net.classifier[6].in_features, out_features=10) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(params = net.parameters()) train_model(net, criterion, optimizer, dataloaders_dict, num_epochs=4, model_name=\u0026#39;vgg16(pretrained)\u0026#39;) 코드를 실행하고 출력을 보면 매 train_model() 호출마다1 새로운 wandb 세션이 만들어지는 것을 볼 수 있다.\n웹에서 wandb 결과 확인하기 위 코드를 보면 각 batch마다 loss를, 각 epoch/phase마다 loss, acc을 기록하는 것을 볼 수 있다. 웹브라우저를 열어 wandb에 접속하면 학습이 진행됨에 따라 대시보드에 실시간으로 차트가 그려진다.\n별다른 설정을 하지 않았다면 wandb.log() 를 통해 기록된 값들이 각 세션별 plot으로 그려지며, 그 외에 이미지, 사운드 등 미디어의 경우 별도의 적절한 형태로 표시된다.\n왼쪽 목록에서 세션 이름을 클릭하면 세부 정보를 확인할 수 있다. 각 탭에서 볼 수 있는 정보에 대해 알아보자.\nOverview 탭에서는 해당 세션이 실행된 환경의 기본적인 정보와 함께, wandb.init()에서 config에 넘겨줬던 값들을 확인할 수 있다.\nCharts 탭에서는 해당 세션에서 wandb.log()로 기록했던 값들을 확인할 수 있다.\nSystem 탭에서는 해당 세션이 실행되는 동안의 CPU, GPU, 메모리 사용량 등을 확인할 수 있다.\nLogs 탭에서는 해당 세션이 실행되는 동안 print()를 통해 콘솔에 출력된 값을 그대로 확인할 수 있다.\nFiles 탭에서는 해당 세션과 관련된 메타데이터 파일을 볼 수 있다. 기본적으로 환경 정보, 패키지 정보, 로그파일 등이 저장되며, wandb.init(save_code=True)인 경우 소스파일이 함께 저장된다.\n이외에도 wandb에서는 \u0026ldquo;학습이 끝나면 메일/슬랙으로 알림 보내기\u0026rdquo;, \u0026ldquo;팀원들 간 공유 프로젝트 만들기\u0026rdquo; 등 생산성 향상을 위한 다양한 기능을 제공한다.\n정확히는 wandb.init() 호출마다\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022년 2월 12일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-06/","section":"Posts","summary":"학습 진행상황 기록하기 이전 글에서 작성한 코드는 tqdm 라이브러리를 통해 “학습이 얼마나 진행되었는가\"를 실시간으로 출력했다. 이를 통해 실행이 끝나기까지 막연히 기다리기만 하는 일반적인 코드와는 달리, 각 epoch당 남은 예상시간과 epoch별 train/val loss, accuracy를 출력해 학습이 진행됨에 따라 정확도가 높아지는 모습을 로그로 남길 수 있었다.\n","title":"[PyTorch] 06. wandb로 학습 진행상황 로깅하기","type":"posts"},{"content":"","date":"2022년 2월 12일","externalUrl":null,"permalink":"/series/pytorch/","section":"Series","summary":"","title":"PyTorch","type":"series"},{"content":"","date":"2022년 2월 12일","externalUrl":null,"permalink":"/tag/%EA%B0%9C%EB%B0%9C/","section":"Tags","summary":"","title":"개발","type":"tags"},{"content":"어디가서 학과 이름 말할 수 있을 때까지\n","date":"2022년 2월 12일","externalUrl":null,"permalink":"/category/%EC%9E%90%EC%8A%B5/","section":"Categories","summary":"어디가서 학과 이름 말할 수 있을 때까지\n","title":"자습","type":"categories"},{"content":"","date":"2022년 2월 12일","externalUrl":null,"permalink":"/tag/%EC%A0%84%EC%82%B0/","section":"Tags","summary":"","title":"전산","type":"tags"},{"content":" 자체 Dataset 클래스의 필요성 지금까지의 실습에서는 torchvision.datasets에서 제공하는 데이터셋을 사용했다. torchvision 라이브러리는 vision과 관련된 약 30여개의 데이터셋을 제공하며, 전체 목록은 공식 홈페이지에서 확인할 수 있다.\n그러나 실습이나 연구를 진행하다 보면, 기존에 잘 알려진 데이터셋 외에 자신만의 데이터로 학습을 진행해야 하는 상황이 자주 발생한다. 이를 위해 torch.utils.data.dataloader에 넘길 수 있는 나만의 Dataset 객체를 만드는 법을 알아보자.\n방법 1. torchvision.datasets.ImageFolder() 이용 이미지 데이터의 경우 torchvision.datasets.ImageFolder() 함수를 이용해 간단히 처리 가능하다. 각 이미지 파일들이 다음과 같은 구조로 정리되어 있다고 가정하자.\ndata/dog/xxx.png data/dog/xxy.png data/dog/[...]/xxz.png data/cat/123.png data/cat/nsdf3.png data/cat/[...]/asd932_.png 이렇게 이미지 파일들이 ..[공통경로]/[라벨명]/[파일명]으로 정리된 구조를 가질 때, 다음과 같은 코드로 손쉽게 dataset 객체를 생성할 수 있다.\nimport torchvision from torchvision import transforms # transforms.toTensor()보다 조금 더 세부적인 전처리 수행 data_transform = transforms.Compose([ transforms.Resize(resize), transforms.CenterCrop(resize), transforms.ToTensor(), transforms.Normalize(mean, std) ]) # ./data 아래의 파일을 재귀적으로 읽어 폴더명(label)에 맞게 dataset 객체를 생성한다. train_data = torchvision.datasets.ImageFolder(root=\u0026#39;./data\u0026#39;, transform=data_transform) # 이후는 이전 포스팅에서의 코드와 동일하다 train_size = int(len(train_data)*0.8) val_size = len(train_data) - train_size train_dataset, val_dataset = data.dataset.random_split(train_data, [train_size, val_size]) dataloaders_dict = { \u0026#39;train\u0026#39;: data.DataLoader(train_dataset, shuffle=True, batch_size=50), \u0026#39;val\u0026#39;: data.DataLoader(val_dataset, batch_size=50) } 이렇듯 일반적인 이미지 파일에 대한 실습은 ImageFolder() 함수로 충분하다. 그러나 train, val 과정에 서로 다른 전처리를 적용하거나 데이터파일의 폴더 구조가 복잡한 구성을 가지는 등, 좀더 세부적인 작업을 필요로 하는 경우 다음과 같이 torch.data.Dataset 클래스를 직접 구현해야 한다.\n방법 2. torch.data.Dataset 클래스 작성 여기서는 torchvision이 아닌 torch에서 제공하는 클래스를 통해 torch.data.Dataset 클래스를 직접 작성해보기로 하자. Dataset 클래스는 크게 다음과 같은 메소드를 가진다\n__init__(self) : 초기화 함수 __len__(self) : 전체 데이터셋의 개수를 리턴하는 함수 __getitem(self, idx)__ : idx번째 데이터를 (x, y) 형태의 tuple로 리턴하는 함수. 이때 x, y는 torch.Tensor(), 혹은 텐서로 변환될 수 있는 대상이어야 한다. 이를 구현해보자. 우리가 만들 Dataset 클래스는 다음 기능을 가진다.\n데이터 파일 경로들의 리스트(path_list)를 받아 관리 현재 데이터셋이 어떤 phase(\u0026rsquo;train\u0026rsquo;, \u0026lsquo;val\u0026rsquo;)에 사용되는지 관리 적용중인 phase에 따라 서로 다른 로직 사용 이를 코드로 작성하면 아래와 같다. transforms_dict가 사용되는 방식에 유의하자.\nimport torch.utils.data as data from torchvision import transforms from PIL import Image class MyDataset(data.Dataset): def __init__(self, path_list, phase=\u0026#39;train\u0026#39;): self.path_list = path_list self.phase = phase # phase에 따라 서로 다른 전처리(transform) 적용 img_size = 224 img_mean = (0.485, 0.456, 0.406) img_std = (0.229, 0.224, 0.225) self.transforms_dict = { \u0026#39;train\u0026#39; : transforms.Compose([ # data augmentation을 위한 random 변환을 추가 transforms.RandomResizedCrop(img_size, scale=(1.0, 1.0)), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize(img_mean, img_std) ]), \u0026#39;val\u0026#39; : transforms.Compose([ transforms.Resize(img_size), transforms.CenterCrop(img_size), transforms.ToTensor(), transforms.Normalize(img_mean, img_std) ]) } # 문자열로 된 label들을 int로 바꾸기 위한 dict. 일관성을 위해 sorted 사용 labels = sorted(list({ path.split(\u0026#39;/\u0026#39;)[-2] for path in path_list })) self.label_to_idx = { label : idx for idx,label in enumerate(labels) } def __len__(self): return len(self.path_list) def __getitem__(self, idx): img_path = self.path_list[idx] # idx번째 경로명을 가져오고 img = Image.open(img_path) # 해당 경로에서 이미지 파일을 읽어들여 img_transformed = self.transforms_dict[self.phase](img) # 적절한 전처리를 적용하고 label_idx = self.label_to_idx[img_path.split(\u0026#39;/\u0026#39;)[-2]] # 라벨명을 식별한다 return img_transformed, label_idx 이렇게 만들어진 MyDataset 클래스는 다음과 같이 사용할 수 있다.\nfrom glob import glob # 해당 데이터는 https://download.pytorch.org/tutorial/hymenoptera_data.zip 에서 받을 수 있다 train_path_list = glob.glob(\u0026#34;./data/hymenoptera_data/train/*/*\u0026#34;) train_dataset = MyDataset(path_list=train_path_list, phase=\u0026#39;train\u0026#39;) val_path_list = glob.glob(\u0026#34;./data/hymenoptera_data/val/*/*\u0026#34;) val_dataset = MyDataset(path_list=val_path_list, phase=\u0026#39;val\u0026#39;) # DataLoader 설정 batch_size = 16 train_dataloader = torch.utils.data.DataLoader(train_dataset, batch_size=batch_size, shuffle=True) val_dataloader = torch.utils.data.DataLoader(val_dataset, batch_size=batch_size, shuffle=False) dataloaders_dict = {\u0026#39;train\u0026#39; : train_dataloader, \u0026#39;val\u0026#39; : val_dataloader} 간단한 경우는 torchvision.datasets.ImageFolder()를, 데이터에 복잡한 전처리나 추가적인 작업이 필요한 경우 torch.data.Dataset 클래스를 직접 만들어 사용하는 것이 좋다.\n","date":"2022년 2월 9일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-05/","section":"Posts","summary":"자체 Dataset 클래스의 필요성 지금까지의 실습에서는 torchvision.datasets에서 제공하는 데이터셋을 사용했다. torchvision 라이브러리는 vision과 관련된 약 30여개의 데이터셋을 제공하며, 전체 목록은 공식 홈페이지에서 확인할 수 있다.\n그러나 실습이나 연구를 진행하다 보면, 기존에 잘 알려진 데이터셋 외에 자신만의 데이터로 학습을 진행해야 하는 상황이 자주 발생한다. 이를 위해 torch.utils.data.dataloader에 넘길 수 있는 나만의 Dataset 객체를 만드는 법을 알아보자.\n","title":"[PyTorch] 05. data.Dataset으로 나만의 Dataset 클래스 만들기","type":"posts"},{"content":"굉장히 오랜만에 이어지는 포스팅이다\u0026hellip; 핑거스냅에 당해서 블립(blip) 당함\n모델 학습에서의 Training \u0026amp; Validation 이전 글에서 작성했던 마지막 코드를 다시 살펴보자. 이전 글에서는 아래 5가지 단계를 거쳤다.\nnn.Module을 상속받은 모델 클래스 작성 torchvision.datasets 라이브러리에서 유명 데이터셋(MNIST) 다운로드 torch.utils.data.Dataloader 사용법 모델의 1 epoch 학습 진행 모델의 test 성능 측정 각 단계별로 주요 로직을 정리해 하나의 코드로 합친 결과는 다음과 같다.\n# 모델 생성 및 설정 net = MyModel() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters()) # 데이터 로드 train_data = torchvision.datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=True, transform=transforms.ToTensor()) test_data = torchvision.datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=False, transform=transforms.ToTensor()) # dataloader에 적재 train_loader = torch.utils.data.DataLoader(train_data, shuffle=True, batch_size=50) test_loader = torch.utils.data.DataLoader(test_data) # 학습(training) 진행 st = time.time() print(f\u0026#34;training with {len(train_data)} data... \u0026#34;, end=\u0026#39;\u0026#39;) for epoch in range(1): for x,y in train_loader: y_pred = net(x) loss = criterion(y_pred, y) optimizer.zero_grad() loss.backward() optimizer.step() print(f\u0026#34;done (elapsed {time.time() - st}s)\u0026#34;) # 검증(test) 진행 with torch.no_grad(): acc, tot = 0, 0 for x, y in test_loader: y_pred = net(x) acc += (y==y_pred.argmax(1)).sum() tot += len(y) print(f\u0026#34;test accuracy : {acc}/{tot} ({100*acc/tot}%)\u0026#34;) 모델의 학습과정을 보면 최종 검증 직전까지도 아무런 중간점검 없이 전체 epoch의 학습이 진행되는 것을 볼 수 있다. 그러나 모델을 본격적으로 학습시키기 위해서는 학습 과정에서 training loss를 계산하거나 주기적으로 validation을 진행하는 등, 학습의 진행도를 추적하기 위해 다양한 절차를 추가하는 경우가 많다. 이번 글에서는 학습 과정에 다음 두 작업을 추가해보자.\nvalidation phase 추가 train/val 단계마다 평균 손실(loss와)과 평균 정확도(acc) 계산 dataloaders_dict 작성 이전에는 train, test용 dataloader를 각각 따로 만들어 관리했다. 그러나 복잡한 코드에서는 관리의 편의성을 위해 둘을 dict로 모아 관리하는 방법이 자주 사용된다.1 다음 작업을 수행하자.\ntrain_data에서 일정 비율(80%)을 분리해 train용으로, 나머지를 val용으로 나눈다 각 데이터셋을 data.Dataloader를 이용해 dataloader로 만든다 두 dataloader를 적절한 키(\u0026rsquo;train\u0026rsquo;, \u0026lsquo;val\u0026rsquo;)와 함께 dictionary에 등록한다 이를 코드로 작성하면 다음과 같다.\nimport torch.utils.data as data import torchvision from torchvision import transforms # 본 포스팅에서는 RGB채널이 모두 있는 CIFAR10 데이터셋을 사용한다 train_data = torchvision.datasets.CIFAR10( root=\u0026#39;./data\u0026#39;, download=True, train=True, transform=transforms.ToTensor() ) train_size = int(len(train_data)*0.8) # 전체의 80%를 학습용으로 사용 val_size = len(train_data) - train_size train_dataset, val_dataset = data.dataset.random_split(train_data, [train_size, val_size]) dataloaders_dict = { \u0026#39;train\u0026#39; : data.DataLoader(train_dataset, shuffle=True, batch_size=50), \u0026#39;val\u0026#39; : data.DataLoader(val_dataset, batch_size=50) } 이제 dataloaders_dict['train'], dataloaders_dict['val']를 통해 각 phase용 dataloader에 접근할 수 있다.\ntrain_model() 함수 작성 이제 validation phase를 추가해 train_model() 함수를 작성하자. 수행할 작업은 다음과 같다\n사용 가능한 device를 인식하고 모델을 변환\n각 epoch마다 train, val 과정 수행\nphase(\u0026rsquo;train\u0026rsquo;, \u0026lsquo;val\u0026rsquo;)에 따라 model.train(), model.eval() 모드 변경 train phase에서는 파라미터 업데이트를 위해 set_grad_enabled 활성화 val phase에서는 불필요한 계산 방지를 위해 set_grad_enabled 비활성화 각 epoch, phase마다 loss, acc 출력\n이를 코드로 작성하면 다음과 같다.\ndef train_model(net, criterion, optimizer, dataloaders_dict, num_epochs): # 사용 가능한 device 인식(GPU가 있으면 GPU 사용) device = \u0026#39;cuda\u0026#39; if torch.cuda.is_available() else \u0026#39;cpu\u0026#39; net.to(device) print(f\u0026#34;Using device: {device}\u0026#34;) # train \u0026amp; val 전체 진행루틴 for epoch in range(num_epochs): print(\u0026#34;-----------------\u0026#34;) print(f\u0026#34;\u0026gt; Epoch {epoch+1}/{num_epochs}\u0026#34;) # 각 epoch마다 \u0026#39;train\u0026#39;, \u0026#39;val\u0026#39;을 모두 진행 for phase in [\u0026#39;train\u0026#39;, \u0026#39;val\u0026#39;]: # 현재 phase에 따라 모델의 상태 변경 torch.set_grad_enabled(phase == \u0026#39;train\u0026#39;) if phase == \u0026#39;train\u0026#39;: net.train() else: net.eval() # 첫 epoch에서는 validation만 수행(초기 상태의 성능 측정 목적) if (epoch == 0) and (phase == \u0026#39;train\u0026#39;): continue # 매 epoch마다 loss(손실), correct(맞은 개수)를 기록 epoch_loss = 0.0 epoch_corrects = 0 # tqdm : 진행바(progress bar)를 표시하기 위한 라이브러리 for x,y in tqdm(dataloaders_dict[phase]): x, y = x.to(device), y.to(device) output = net(x) loss = criterion(output, y) # train phase일 때만 loss값을 기반으로 파라미터 갱신 if phase == \u0026#39;train\u0026#39;: optimizer.zero_grad() loss.backward() optimizer.step() # loss, correct 계산 _, y_pred = torch.max(output, 1) epoch_loss += loss.item() * x.size(0) # size(0): 해당 batch의 size epoch_corrects += torch.sum(y_pred == y.data) # epoch이 끝난 후 해당 epoch에서의 평균 손실, 정확도 계산 및 출력 epoch_loss = epoch_loss / len(dataloaders_dict[phase].dataset) epoch_acc = epoch_corrects.double() / len(dataloaders_dict[phase].dataset) print(f\u0026#34;{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f} ({epoch_corrects}/{len(dataloaders_dict[phase].dataset)})\u0026#34;) 모델 학습 진행 앞서 작성한 함수들을 이용해 모델 학습을 진행해보자. 편의상 별도의 모델 클래스를 만드는 대신 torchvision.models의 resnet18을 사용했다.\nimport torch import torch.nn as nn import torch.optim as optim import torch.utils.data as data import torchvision from torchvision import models, transforms \u0026#34;\u0026#34;\u0026#34; 앞서 작성한 dataloaders_dict, train_model 코드 생략 \u0026#34;\u0026#34;\u0026#34; # resnet18 모델에서 마지막 output layer만 변경한다(총 10개의 label) net = models.resnet18() net.fc = nn.Linear(in_features=net.fc.in_features, out_features=10) # criterion, optimizer 설정 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(params = net.parameters()) # 학습 진행 train_model(net, criterion, optimizer, dataloaders_dict, num_epochs=5) 이를 실행하면 다음과 같은 출력을 볼 수 있다. 함께 출력되는 진행바는tqdm의 효과이다.\nUsing device: cuda ----------------- \u0026gt; Epoch 1/5 100%|██████████| 200/200 [00:05\u0026lt;00:00, 34.14it/s] val Loss: 2.3033 Acc: 0.1043 (1043/10000) ----------------- \u0026gt; Epoch 2/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.52it/s] train Loss: 1.4675 Acc: 0.4736 (18944/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.41it/s] val Loss: 1.2823 Acc: 0.5465 (5465/10000) ----------------- \u0026gt; Epoch 3/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.53it/s] train Loss: 1.0625 Acc: 0.6243 (24970/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.35it/s] val Loss: 1.1661 Acc: 0.5980 (5980/10000) ----------------- \u0026gt; Epoch 4/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.52it/s] train Loss: 0.8876 Acc: 0.6887 (27547/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.33it/s] val Loss: 1.0737 Acc: 0.6208 (6208/10000) ----------------- \u0026gt; Epoch 5/5 100%|██████████| 800/800 [00:59\u0026lt;00:00, 13.50it/s] train Loss: 0.7564 Acc: 0.7349 (29394/40000) 100%|██████████| 200/200 [00:04\u0026lt;00:00, 42.28it/s] val Loss: 0.9722 Acc: 0.6625 (6625/10000) 이는 이후 다른 글에서 다룰 torchvision.transforms의 활용에 있어서도 동일하다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022년 2월 7일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-04/","section":"Posts","summary":"굉장히 오랜만에 이어지는 포스팅이다… 핑거스냅에 당해서 블립(blip) 당함\n모델 학습에서의 Training \u0026 Validation 이전 글에서 작성했던 마지막 코드를 다시 살펴보자. 이전 글에서는 아래 5가지 단계를 거쳤다.\nnn.Module을 상속받은 모델 클래스 작성 torchvision.datasets 라이브러리에서 유명 데이터셋(MNIST) 다운로드 torch.utils.data.Dataloader 사용법 모델의 1 epoch 학습 진행 모델의 test 성능 측정 각 단계별로 주요 로직을 정리해 하나의 코드로 합친 결과는 다음과 같다.\n","title":"[PyTorch] 04. validation phase를 추가해 train_model() 함수 작성하기","type":"posts"},{"content":"예전 소멤(Samsung Software Membership)에서 받은 삼성 노트북9를 쓴 지도 벌써 만 4년이 지났다. 요새는 노트북에 키보스, 마우스, 모니터까지 따로 연결해 거의 노트북을 데탑처럼 쓰고 있다보니, 이참에 데스크탑 PC를 하나 구입할까 고민하던 중 소프티어(Softeer)에서 모의대회를 연다는 소식을 접했다.\n1등 상품이 맥북 프로인 걸 보고 \u0026ldquo;혹시\u0026hellip;?\u0026rdquo; 하는 마음에 주변에 소문내지 않고 조용히 참가를 신청했다. 대회는 일반적인 ICPC 방식(패널티 합산)으로 이루어졌고, 이런 대회의 특성상 푼 문제 수만큼이나 빠르게 푸는 게 승부를 가를 것 같았다.\n그리고 그렇게 1등을 했다(?). ICPC 시간 패널티에 대해 아는 사람이라면 눈치챘겠지만 4솔브에서 도합 3분 패널티라는 아슬아슬한 차이1로 승부가 갈렸는데, 2등 이름을 보니 후배여서 대회 종료 후 오랜만에 연락해 의문의 동문회(?)를 진행했다.\n대회는 작년 12월 3일이었지만, 신상품 재고 수급 문제로 막상 맥북은 엊그제 받았다.\n방에서 언박싱을 진행했다.\n지금은 이런저런 앱을 설치해 적응중이다. M1 인 만큼 일부 라이브러리(ex: PyTorch)는 아직 호환이 잘 안 된다고 하는데, 일단 윈도우랑 소소한 차이가 있어 적응에 시간이 필요할 것 같다. 일단 하루 정도는 전원 어댑터 없이도 충분히 사용 가능한 것 같고, \u0026lsquo;프로\u0026rsquo;인 만큼 한동안 성능 걱정은 덜어둬도 될 것 같아 만족중이다. 사실 미개봉으로 팔고 존버한 다음 M2 맥북에어 + 데스크답 조합으로 갈까도 고민했는데 상품으로 받은 걸 되파는 건 왠지 마음이 불편해 그냥 쓰기로 함\np.s. 이제 스타벅스 갈 수 있다(혹시 몰라서 애플케어+도 결제했다)\n첫 문제를 1분 빠르게 풀었다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2022년 1월 20일","externalUrl":null,"permalink":"/blog/macbook-pro-review/","section":"Posts","summary":"예전 소멤(Samsung Software Membership)에서 받은 삼성 노트북9를 쓴 지도 벌써 만 4년이 지났다. 요새는 노트북에 키보스, 마우스, 모니터까지 따로 연결해 거의 노트북을 데탑처럼 쓰고 있다보니, 이참에 데스크탑 PC를 하나 구입할까 고민하던 중 소프티어(Softeer)에서 모의대회를 연다는 소식을 접했다.\n1등 상품이 맥북 프로인 걸 보고 “혹시…?” 하는 마음에 주변에 소문내지 않고 조용히 참가를 신청했다. 대회는 일반적인 ICPC 방식(패널티 합산)으로 이루어졌고, 이런 대회의 특성상 푼 문제 수만큼이나 빠르게 푸는 게 승부를 가를 것 같았다.\n","title":"새 맥북프로 생긴 후기(feat. Softeer)","type":"posts"},{"content":"지난번 2021년도 대회 후기 - 해킹(CTF) 편에 이은 알고리즘(PS) 편이다. 올해는 전반적으로 성적이 좋지는 못한데, 개인적으로 기대했던 SCPC에서 억울할 정도의(\u0026hellip;) 성적을 받아 여러모로 후유증이 남기도 했다. Z플립 3 지를때 \u0026ldquo;SCPC에서 보충해야지\u0026quot;라고 계획까지 세워놨는데\u0026hellip;\n구글 코드잼(Google Code Jam) \u0026ldquo;원래 코드잼은 티셔츠 받으려고 하는거죠\u0026rdquo;\n주관 : 구글(Google) 결과 : Round 3 진출 매년 구글에서 진행하는 프로그래밍 대회이다. Qualification Round → Round 1 → Round 2 → Round 3 → World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다.\nQualification Round 절대평가로 30점 이상의 점수를 얻으면 Round 1로 진출 결과 : 100/100으로 Round 1 진출 Round 1 3개의 라운드(1A, 1B, 1C) 중 하나에서 1500위 이상을 달성하면 Round 2로 진출 결과 : Round 1A에서 1146위로 Round 2 진출 Round 2 Round 2 참가자들 중 상위 1000명이 Round 3에 진출 결과 : 405위로 Round 3 진출 Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 Round 3 진출자 전원에게 코드잼 티셔츠 증정 결과 : 최종 372위로 종료 World Final 살면서 한번쯤은 여기까지 올라가보고 싶어요\u0026hellip; 실력 유지를 위한 암묵적인 선으로 Round 3 상위 500명 이내를 목표로 잡았는데, 재작년, 작년에 이어 올해도 목표를 달성해 새 티셔츠를 얻을 수 있었다.\n페이스북 해커컵(Facebook Hacker Cup) \u0026ldquo;자정까지 CCE 뛰고 새벽 2시에 또 대회하기는 무리였습니다..ㅠㅠ\u0026rdquo;\n주관 : 페이스북(Facebook) 결과 : Round 2 진출 이번엔 티셔츠는 못 받았다 매년 페이스북에서 진행하는 프로그래밍 대회이다. 코드잼과 유사하게 Qualification Round에서 World Final까지 총 5단계로 이뤄지며, 각 라운드의 진출요건은 다음과 같다.\nQualification Round 절대평가로 1문제 이상을 해결하면 Round 1로 진출 결과 : 65/100으로 Round 1 진출 Round 1 절대평가로 24점 이상의 점수를 얻으면 Round 2로 진출 결과 : 63/100으로 Round 1 진출 Round 2 Round 2 참가자들 중 상위 500명이 Round 3에 진출 1문제 이상을 해결한 상위 2000명의 참가자들에게 티셔츠 제공 결과 : (미참가) Round 3 Round 3 참가자들 중 상위 25명이 World Final에 진출 World Final 코드잼과 동일 이번 Round 2는 새벽 2시에 진행되었는데, 전날 오전 9시부터 자정까지 사이버공격방어대회(CCE) 예선을 진행하고 나니 몸이 피곤해 대회를 하나 더 뛸 수 있는 상태가 아니었다. 고민 끝에 1문제만 풀고 티셔츠라도 받겠다는 마음에 알람을 맞췄지만 침대에 뻗은 몸은 그대로 일어나지 못했다고 한다(\u0026hellip;)\n삼성 대학(원)생 프로그래밍 경진대회(SCPC) \u0026ldquo;어려운거 맞아놓고 쉬운거 못풀어서 터짐(\u0026hellip;)\u0026rdquo;\n주관 : 삼성전자 결과 : 본선 진출, but No Awards(\u0026hellip;) 아니 문제 1번의 상태가\u0026hellip; 삼성전자에서 주관하는 대학생/대학원생 대상 알고리즘 대회로, 국내 대회 중 가장 상금 규모가 크고 대학원생 신분으로도 참가할 수 있는 몇 안 되는 대회 중 하나이다. 작년에 이어 올해도 1,2차 예선, 본선 모두 온라인으로 진행되었다. 2회 수상하면 입상자격이 사라지는 만큼 올해 상을 받아 SCPC를 졸업하겠다는 목표를 세웠다.1\n그리고 결과는 폭망했다(\u0026hellip;) 대회를 시작하고 1번부터 계속해서 오답이 나왔고, 몇 번 틀린 이후 결국 2번으로 넘어갔다. 겨우 2번을 해결하고 보니 2번문제의 만점자 수가 3명밖에 없었고, 1,3번을 해결한 사람은 많았기에 \u0026ldquo;지금부터 멘탈만 제대로 잡으면 최소 3등상, 잘하면 2등상이다\u0026quot;라는 헛된 꿈을 꾸면서 다시 1번으로 돌아갔다. 그러지 말았어야 했는데 그러나 대회가 거의 끝나갈 무렵까지 정답을 받지 못했고, 그렇게 3번 이후는 제대로 보지도 못한 채 대회가 종료되었다.\n대회가 종료된 후 최종 스코어보드를 통해 2번문제 만점자가 4명, 3번문제 만점자는 38명으로 3번의 난이도가 2번에 비해 다소 낮았던 것을 알 수 있었다. 풀이를 들어보니 1번의 정해는 case work였는데, 일반화된 규칙을 적용해보려는 시도를 하면서 edge case를 제대로 찾지 못한 게 패인이었다. 3번은 Suffix Array를 구현하거나, 손으로 규칙을 찾아 직관적(?)으로 구현하면 되는 문제여서 더 아쉬움이 컸다. 차라리 1번을 빠르게 포기하고 2,3번 + 4번을 긁었으면 어땠을까라는 생각이 머리를 떠나지 않았고, 그렇게 며칠이 지나고 나서야 고난이도 문제를 풀었으면서 막상 (비교적) 쉬운 문제를 풀지 못해 떨어졌다는 미련에서 벗어날 수 있었다.2\nMOBIS 알고리즘 경진대회 \u0026ldquo;갓들의 향연 속에서 소소하게 용돈만 벌어갑니다\u0026rdquo;\n주관 : 현대모비스 결과 : 5등상 현대모비스에서 개최한 알고리즘 경진대회로, 만 18세 이상이면 누구나 참가할 수 있었다. 1등상으로 기아 K5 자동차라는 스케일 큰 부상을 내걸어 PS업계(?) 사람들 사이에서는 화제가 됐던 대회이기도 했다. 예선/본선 모두 프로그래머스 플랫폼을 이용해 진행되었다.\n예선에서 상위 50명만이 본선에 올라가는 만큼 마지막 문제에서 조금 추한 방법3까지 동원한 끝에 본선에 진출할 수 있었다. 본선에서는 다른 대회들에 비해 수학적인 요소를 다루는 문제들이 많아 생각보다 난이도가 높다는 인상을 받았고, 풀이 고민과 함께 한때 PS대회를 떠났지만 KIA K5를 향해 되돌아오신 천상계 분들을 구경하며 대회를 마쳤다. 워낙 쟁쟁한 분들이 많이 참가했기에 상위 17명(4등상 기준)이라는 숫자는 약간 빠듯하다고 생각했는데, 아쉽게도 그 예상이 어긋나지 않아 최종 5등상을 받을 수 있었다.\n2017년도에 4등상을 수상한 적이 있다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n사실 아직도 조금 남아있다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n답이 존재한다면 그 해를, 존재하지 않는다면 -1을 출력하는 전형적인 문제였는데, 휴리스틱으로 답을 탐색하다가 연산횟수가 일정 수치를 넘어가면 그냥 -1을 출력시켰다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2021년 11월 1일","externalUrl":null,"permalink":"/blog/PS-contests-in-2021-review/","section":"Posts","summary":"지난번 2021년도 대회 후기 - 해킹(CTF) 편에 이은 알고리즘(PS) 편이다. 올해는 전반적으로 성적이 좋지는 못한데, 개인적으로 기대했던 SCPC에서 억울할 정도의(…) 성적을 받아 여러모로 후유증이 남기도 했다. Z플립 3 지를때 “SCPC에서 보충해야지\"라고 계획까지 세워놨는데…\n","title":"2021년도 대회 후기 - 알고리즘(PS) 편","type":"posts"},{"content":"원래 대회 하나 끝낼 때마다 후기를 쓰려고 했는데 딱히 쓸만한 성과가 나오지 않았기도 했고, 한동안 바빴다보니 이번에 몰아서 쓰게 됐다. 모든 대회를 다 쓸까 하다가 그나마 성격 비슷한 것들끼리 모아서 2편으로 나눴다. 학부 때는 주로 알고리즘 대회 위주로 나갔다보니 CTF를 제대로 나가본 적은 올해가 처음이지만 그걸 감안해도 아쉬움이 많이 남는 경험이었다.\n공군 사이버전사 경연대회 일시 : 8.3.(예선), 8.9.(본선) 주관 : 공군본부 결과 : 1위 해킹알못인 내가 이세계에선 사이버전사? 지난번에 별도로 후기글을 작성했던 공군 CTF 대회이다. 대회 자체가 아래 설명할 국방 사이버보안 경진대회에 출전할 공군 대표를 선발하기 위한 절차이기도 한 만큼, 입상자로서 대표 선발 \u0026amp; 교육파견을 나갈 수 있었다. 팀으로 나가는 CTF에서는 주로 크립토만 건드리다가 개인 대회는 처음 나가봤는데, 생각보다 좋은 결과를 얻어 만족스러웠다.\n국방 사이버보안 경진대회 일시 : 10.14. 취소됨 주관 : 군사안보지원사령부 결과 : COVID-19로 인한 대회 취소\u0026hellip;ㅠㅠ 군사안보지원사령부에서 주관하는 전군(육+해+공+국직) 대상 경진대회이다. 단순히 Jeopardy(문제풀이) 방식 CTF가 아니라 필기+실기가 합쳐진 방식이라고 하며, 자세한 내용은 겪어보지 않아 잘 모르겠다. 공군 대표로 선발되고 약 1개월동안 교육파견까지 나갔는데 막상 대회 1주일 전에 코로나로 취소됐다는 공지가 내려왔다(\u0026hellip;)\n화이트햇 콘테스트 일시 : 9.11.(예선), 10.9.(본선) 주최/주관 : 대한민국 국방부 / 사이버작전사령부 결과 : 국방트랙 5위1 사이버작전사령부에서 주관하는 CTF로, 일반 / 청소년 / 국방트랙이 별도로 분리되어 진행된다. 일반/청소년부는 다른 CTF와 유사하게 pwn, web, reversing, forensic 등 분야별 문제가 출제되었고, 국방트랙은 침해사고를 당한 VM 이미지가 제공되어 이를 분석해 공격자를 추적하고 관련 문제를 해결하는 방식으로 진행되었다.\n본선 중반까지 순위권을 유지하고 있어 \u0026ldquo;이 페이스만 유지하면 2등상(합참의장상)은 받을 수 있겠다\u0026quot;라는 생각을 했는데, 이런 생각을 하면 항상 그렇듯이 후반부에 다른 팀들이 많이 해결한 문제를 풀지 못해 등수가 떨어져 최종 5위라는 아쉬운 성적으로 대회를 끝마쳤다. Python으로 만들어진 exe를 분석하는 문제였는데, 우리 팀은 대회 내내 pyinstaller decompiler가 잘 동작하지 않아 결국 IDA를 통해 일일히 분석해보려다 실패했다. 대회가 끝나고 다른 팀들에게 물어보니 pyinstaller decompiler를 사용할 때 python 버전, pyinstaller 버전에 따라 동작이 다를 수 있어 환경을 정확히 맞춰줘야 한다는 말을 듣고 아쉬움이 남았다.\nCCE 사이버공격 방어대회 일시 : 9.25.(예선), 10.26.(본선) 주최/주관 : 국가정보원 / 국가보안기술연구소 결과 : 전체 16위, 공공기관 5위 2 국가정보원에서 주관하는 CTF로 일반인들을 위한 일반분야와 공공기관 종사자들을 위한 공공분야로 나눠 진행된다. 화이트햇과는 달리 일반분야, 공공분야가 모두 동일한 문제를 사용해 진행된다. 예선은 일반적인 Jeopardy(문제풀이) 방식, 본선은 공격받는 Live 서버를 실시간으로 방어하는 방식으로 진행되었다.\n실시간 방어 방식은 처음인 만큼 헤멘 부분이 많다. 방어대상 서버 5개 중 2개가 죽어버려 계속 SLA Fail 패널티를 받으면서 진행하기도 하고, 서버가 중간중간 뻗을 때마다 \u0026ldquo;서버 램 8GB에서 조금만 더 올려주지\u0026hellip;\u0026ldquo;라는 생각과 함께 docker를 여러 번 재시작하기도 했다. 마지막까지 잡은 문제는 gitlab 서버를 공격하는 문제였는데, 주어진 gitlab 서버 버전에 해당하는 CVE를 찾고 공격 코드를 작성했지만 반응이 없어 마지막까지 원인을 파악하면서 시간을 보냈다. 대회 종료까지 \u0026ldquo;이것만 풀리면 공공기관 3등인데\u0026hellip;\u0026rdquo; 라는 생각으로 문제를 붙잡았지만 결국 풀리지 않았고, 공공기관 5위라는 성적으로 대회를 끝마쳤다.\n다음날 수상팀의 발표를 들어보니 우리팀이 찾았던 것과 동일한 CVE에 거의 동일한 공격 코드를 사용했다는 것이 밝혀서 더 큰 아쉬움이 남았다. 대회가 끝나 확인이 어렵지만 우리팀의 경우 공격을 위한 중계서버로 대회에서 주어진 내부망 원격PC(IP 192.168.x.x)를 사용했는데, 수상팀은 본인들의 별도 서버(공인 IP)를 사용했다는 차이점이 있어 이게 원인이 아닐까 추정된다. 서버가 없던 것도 아니고, 다른 문제(Scenario 2) 해결을 위해 팀원의 PC를 이용해 중계서버를 구축해놓은 게 있어서 아쉬움이 배로 남았다(\u0026hellip;)\n화이트햇, CCE 모두 나쁜 성적은 아니지만 명시적인 상을 받지는 못했고, 둘 모두 한 문제만 더 풀었어도 수상권이었기에 지금까지도 미련이 남아있다. 내년에도 참가할지는 모르겠지만 좀더 좋은 성과를 낼 수 있었으면 좋겠다.\n3위까지 상이 수여된다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n이것도 3위까지 상이 수여된다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2021년 10월 30일","externalUrl":null,"permalink":"/blog/CTF-contests-in-2021-review/","section":"Posts","summary":"원래 대회 하나 끝낼 때마다 후기를 쓰려고 했는데 딱히 쓸만한 성과가 나오지 않았기도 했고, 한동안 바빴다보니 이번에 몰아서 쓰게 됐다. 모든 대회를 다 쓸까 하다가 그나마 성격 비슷한 것들끼리 모아서 2편으로 나눴다. 학부 때는 주로 알고리즘 대회 위주로 나갔다보니 CTF를 제대로 나가본 적은 올해가 처음이지만 그걸 감안해도 아쉬움이 많이 남는 경험이었다.\n","title":"2021년도 대회 후기 - 해킹(CTF) 편","type":"posts"},{"content":"TL;DR : 전세계 단위에서 한 자리 등수(8등) 찍어봄\n그동안 킥스타트는 굳이 후기를 안 썼지만 이번에 역대급 성과를 낸 김에 글로 남겨본다.\n예전에도 10~20등 사이의 성적을 낸 적은 있었지만 사이트가 리뉴얼된 후 참가자가 급증하면서1 한동안 100등 바깥으로 밀려났었다. 재작년 코포 접은 이후로 PS 문제풀이를 자주 하진 않더라도 실력체크 겸 코드잼/해커컵/킥스타트 정도는 계속 해보자는 생각에 꾸준히 참가했고, 이번 킥스타트에서는 마침 상성이 잘 맞는(?) 문제들을 만나 8등이라는 좋은 성적을 거둘 수 있었다.\n개인적으로 \u0026ldquo;세계 규모 대회에서 한 자리 등수를 한 번은 찍어보고 싶다\u0026rdquo; 라는 생각을 했었는데 이번 기회에 목표를 달성하게 돼 만족스럽다. 비록 코드잼이나 해커컵처럼 위상 높은 대회는 아니지만2 그래도 구글에서 주최하는 대회에, 거의 7천여명 중에서 10등 안쪽이니 충분히 자랑해도 될 성과라고 생각한다(나만 그렇게 생각하는 거면 어쩔 수 없지만). 풀이는 대회 공식 웹사이트에 있으니 이번에도 생략한다(\u0026hellip;)\n킥스타트 리뉴얼 전에 비해 전세계에서 약 10배 정도 참가자가 늘었다\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n살면서 한번이라도 코드잼이나 해커컵에서 25등 이내로 들어볼 수는 있을지 모르겠다ㅋㅋㅋ\u0026hellip;\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2021년 9월 14일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2021-RoundE-review/","section":"Posts","summary":"TL;DR : 전세계 단위에서 한 자리 등수(8등) 찍어봄\n그동안 킥스타트는 굳이 후기를 안 썼지만 이번에 역대급 성과를 낸 김에 글로 남겨본다.\n예전에도 10~20등 사이의 성적을 낸 적은 있었지만 사이트가 리뉴얼된 후 참가자가 급증하면서1 한동안 100등 바깥으로 밀려났었다. 재작년 코포 접은 이후로 PS 문제풀이를 자주 하진 않더라도 실력체크 겸 코드잼/해커컵/킥스타트 정도는 계속 해보자는 생각에 꾸준히 참가했고, 이번 킥스타트에서는 마침 상성이 잘 맞는(?) 문제들을 만나 8등이라는 좋은 성적을 거둘 수 있었다.\n","title":"Code Jam Kickstart 2021 Round E 후기","type":"posts"},{"content":"8월의 반이 지나서야 쓰는 2021년도 첫글. 올해도 코드잼은 했고(티셔츠 배송은 아직 안 옴), 다음달이면 SCPC 본선도 있긴 하지만 올해 글이 너무 없는거같아서 일단 하나 써보기로 했다.\n어차피 군대에서 여는 대회다보니 뭘 얼마나 공개 가능한지 몰라서 문제에 대한 세부 내용이나 그런건 어차피 안 나온다. 그냥 군에서 이런 대회를 했고, 부대 인원들의 배려로 좋은 경험을 했다는걸 기록으로 남기고 싶어서 쓰는 의미없는 글이다.\n공군에서는 전체 부대를 대상으로 \u0026lt;공군 사이버전사 경연대회\u0026gt; 라는 이름의 CTF를 개최한다. 예선은 온라인, 본선은 오프라인으로 진행되며 수상자에게는 참모총장상, 공군본부 정보화부장상, 사이버작전센터장상 등이 수여된다.\n라이트업(풀이)을 쓰는 시간을 포함해 약 2시간동안 진행되는 개인전 대회인 만큼, 특별한 트릭을 사용하기보다는 기본에 충실한 문제들 위주로 출제되었다. 시간 여유가 많지 않은 만큼 금방 풀 수 있을 것 같은 문제들을 먼저 풀고 라이트업을 작성해 제출하니 2시간은 생각보다 금방 지나갔다.\n그리고 최우수상을 받았다(?)\n시간이 부족할 것 같아 포렌식 문제들은 아예 건드리지도 않았는데, 나중에 풀이를 들어보니 너무 빨리 단념했나 싶은 생각도 잠깐 들었다. 어쨌든 결과는 나쁘지 않으니 만족하기로 했다. 별이 4개인데 당연히 만족해야지\np.s. 몰랐는데 대회 결과가 국방일보에 박제됐다(\u0026hellip;) 덕분에 다음날 선배/동기들한테서 축하인사 받음\n","date":"2021년 8월 15일","externalUrl":null,"permalink":"/blog/airforce-cyberwarrior-contest-2021-review/","section":"Posts","summary":"8월의 반이 지나서야 쓰는 2021년도 첫글. 올해도 코드잼은 했고(티셔츠 배송은 아직 안 옴), 다음달이면 SCPC 본선도 있긴 하지만 올해 글이 너무 없는거같아서 일단 하나 써보기로 했다.\n어차피 군대에서 여는 대회다보니 뭘 얼마나 공개 가능한지 몰라서 문제에 대한 세부 내용이나 그런건 어차피 안 나온다. 그냥 군에서 이런 대회를 했고, 부대 인원들의 배려로 좋은 경험을 했다는걸 기록으로 남기고 싶어서 쓰는 의미없는 글이다.\n","title":"공군 사이버전사 경연대회 후기","type":"posts"},{"content":"지난 코드잼, 해커컵 이후로도 출전한 대회는 몇 있었지만 문제 유출 방지규정이나 다른 사정(대회 종료 직후의 귀찮음이라던가), 혹은 딱히 별도의 글로 쓸만한 성과가 아니었다는 이유로 따로 후기를 쓰진 않았다. 그러다보니 시간이 꽤 지나 굳이 각각을 별도의 글로 쪼개기도 애매해졌고, 대회 후기글로 블로그를 도배할 생각도 없어 이번 기회에 한번 몰아서 정리해봤다.\n브랜디 코드네임 B 한줄후기: 내가 상금 받았으면 좋은 대회\n브랜디(Brandi)라는 의류 스타트업에서 신규 개발자를 채용하기 위해 개최한 대회이다. 상금 액수가 꽤 높은 반면 수상자 수는 적어서 \u0026ldquo;적당히 하면 30만, 잘하면 100만원 정도는 받지 않을까?\u0026rdquo; 라는 생각으로 참가했는데 운이 좋았는지 2등상이라는 기대 이상의 성적을 받았다. 가난한 대학원생(?)에게 500만원(세후 478만원)이라는 은혜가 내려져 한동안 풍족하게 살았다.\n코로나로 한창 조심할 시기여서 그런지 시상식은 zoom으로 진행됐다.\n개발자 채용을 목적으로 연 대회답게 입사혜택1과 기업문화에 대한 소개시간이 있었는데, 듣다보니 꽤 좋은 회사라는 인상을 받았다. (물론 아직 자유의 몸이 되려면 멀었다\u0026hellip;ㅠ)\n국방 오픈소스 해커톤(군장병 코딩경진대회) 한줄후기: 이왕이면 에어팟 받고싶었는데 vs 그래도 3등이 어디야\n국방오픈소스아카데미에서 진행하는 해커톤 선발과정의 하나로 개최된 대회이다. 개발경험이 적다보니 해커톤에는 큰 기대를 하지 않았고, 오히려 이 코딩경진대회를 메인으로 생각해 참가했다. 해커톤 프로젝트가 주가 되는 행사여서 그런지 코딩대회 수상자는 \u0026ldquo;1등 갤럭시탭 A 8.0, 2등 에어팟2, 3등 DJI드론\u0026rdquo; 이렇게 3명으로 한정됐다.\n총 5문제 중 4개를 해결하기까지 25분이 채 걸리지 않아 \u0026ldquo;못해도 에어팟은 받겠다\u0026quot;라고 생각했지만 마지막 문제에서 아이디어가 막히는 바람에 휴리스틱으로 추하게 긁었고(\u0026hellip;) 최종 등수는 3등에 그쳤다. 부상으로는 DJI 텔로 드론을 받았는데 여기에선 날릴 수가 없어 본가에 봉인해둔 상태이다.\n암호분석경진대회 \u0026amp; 부채널분석경진대회 한줄후기: 버스 승차감 안락합니다^^\nSEC연구소에서 주최하는 암호분야 문제풀이 대회이다. 비슷한 대회로 국가정보원에서 주최하는 암호공모전이 있었는데 여기는 올해부터 문제풀이 분야가 사라지고 논문제출 분야만 진행되면서 이번엔 참가하지 못했다. 학과 동기들과 같이 3인 팀을 이뤄 나갔고, 우수한 팀원들이 운전하는 버스 좌석에 앉아(?) 수상의 영광을 누릴 수 있었다.\np.s. 문제가 일부 겹친 김에 부채널분석경진대회도 같이 나갔다.\n사이버공격방어대회(CCE) 한줄후기: 예선에서 선방했지만 본선에서 망함(\u0026hellip;)\n국가정보원에서 주최하는 문제풀이 형식의 CTF 대회이다. 민간부문과 공공부문으로 나뉘어 진행한다는 점이 특징이며, 부서 사람들과 같이 팀을 이뤄 공공부문으로 참가했다. 규정상 최대 4인까지 허용됐지만 사람이 부족해 3인팀으로 진행했다.\n예선에서 공공분야 4등이라는 괜찮은 성적과 함께 본선에 진출했다. 분야별 3등까지 수상할 수 있어 \u0026ldquo;본선 당일 컨디션이 좋다면 기대해 볼 만 하다\u0026quot;라고 생각했지만 본선에서 익숙하지 않은 유형들을 만나 10등 밖으로 밀려났다.. PS와는 다르게 CTF는 아직 부족한 점이 많다는 걸 느꼈다.\n삼성 대학생 프로그래밍 경진대회(SCPC) 한줄후기: 난이도 밸런스 실화?\n대학원생도 참가 가능한 자비로운 삼성의 알고리즘 문제풀이 대회이다. 작년에는 대학원생이 아니라고 못 나갔기에2 학부 이후 3년만에 다시 참가한 SCPC였다.\n무난하게(?) 본선에 진출하고 문제를 열었는데 난이도가 심상치 않았다. 1번을 풀고 2번은 미심쩍은 마음과 함께 $O(NM)$을 제출했지만 역시나(\u0026hellip;) subtask 2까지만을 통과했다. 3번은 생각이 안 났고, 왠지 뚫릴거같은 기하문제인 4번에 집중하기로 했다. 4번문제 제출이 20회를 넘긴 시점에서 2번을 마저 풀어봤자 패널티로 수상은 불가능할 것 같아 계속 4번에 집착했고, 그렇게 4번을 어떻게든 뚫어보려 애쓰다 대회를 종료했다.\n대회가 끝난 후 다른 사람들의 후기를 들어보니 나처럼 1번 해결 + 2번 긁기 후 4번을 잡은 사람들이 많았던 모양이다. 최종 결과가 발표되고 예상과는 달리 2번 긁기를 빠르게 했으면 5등상, 2번을 마저 해결했으면 4등상이라는 소식을 들었다. 이럴거면 괜히 4번을 잡았나라는 아쉬운 마음과 함께 밸런스패치에 대한 원망도 있었지만 나중을 위한 액땜이라고 생각하기로 했다.\n여담으로 이번 SCPC 본선은 코로나로 인해 온라인으로 진행됐다. 전통적으로 주던 대형 에코백, 티셔츠, 키보드와 함께 촬영을 위한 삼각대가 택배로 왔고, 이를 이용해 내 모습 + 노트북 화면을 화상회의로 전송했다. 대회는 숙소에서 진행했는데, 중간에 갑작스럽게 정전안내가 나와 주최측에 사정을 설명하고 스마트폰 테더링을 이용해 재접속하는 해프닝이 있었다.\n기본 연봉상승 1000만원과 함께 사이닝 보너스 1000만원, 스톡옵션 1000만원\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n2019년 9월 입학이어서 대회가 진행되는 여름방학 당시엔 대학원생이 아니었다\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020년 11월 18일","externalUrl":null,"permalink":"/blog/programming-contests-in-2020-review/","section":"Posts","summary":"지난 코드잼, 해커컵 이후로도 출전한 대회는 몇 있었지만 문제 유출 방지규정이나 다른 사정(대회 종료 직후의 귀찮음이라던가), 혹은 딱히 별도의 글로 쓸만한 성과가 아니었다는 이유로 따로 후기를 쓰진 않았다. 그러다보니 시간이 꽤 지나 굳이 각각을 별도의 글로 쪼개기도 애매해졌고, 대회 후기글로 블로그를 도배할 생각도 없어 이번 기회에 한번 몰아서 정리해봤다.\n","title":"2020년도 대회 후기","type":"posts"},{"content":"TL;DR : 이번엔 티셔츠 받음\n2019년 해커컵에 이어 올해도 2020년도 페이스북 해커컵 대회가 열렸다. 이번에도 Round 2에서 늦잠을 자면서 Round 3은 달성하지 못했지만1 티셔츠까지는 받을 수 있었다. 구글 코드잼은 작년에 티셔츠를 받았지만 해커컵은 이번이 첫 티셔츠여서 그런지 나름 만족스럽다.\n그동안 해커컵에서의 제일 큰 불만은 small / large가 없어서 조금이라도 실수하면 돌이킬 수가 없다라는 점이었는데, 이번에는 validate step이 나와서 이런 점이 조금은 완화됐다. 코드포스의 pretest 느낌으로 validate_input.txt을 제공하고, validate_output.txt를 제출하면 이에 대한 채점결과를 알려주는 시스템이 추가되면서 \u0026ldquo;그래도 바보같은 코딩실수는 안했구나\u0026quot;라는 확인을 할 수 있었다.\n작년에는 Round 2 기준 상위 500명에게만 티셔츠를 제공했지만 올해 해커컵에서는 기준이 조금 바뀌었다. Round 2에서 1문제 이상을 해결한 경우 기념 티셔츠를, Round 3(상위 200명)에 진출하는 경우 티셔츠에 \u0026ldquo;Top 200\u0026quot;이라는 뱃지가 추가되어 나온다는 모양이다. 이왕 받는거 Top 200이었다면 좋았겠지만 노말등급(?) 티셔츠도 전체 참가자 수 대비 5% 미만의 사람들만 받은 만큼 너무 아쉬워할 필요는 없을거같다.\n각 라운드의 성적은 다음과 같다.\nQualification Round Round 1 진출을 위해서는 1문제 이상만 정답이면 된다. 하지만 퀄라운드가 72시간이라는 긴 시간동안 진행되면서 \u0026ldquo;남는 시간동안 다른 문제들도 고민해보자\u0026quot;라는 생각을 했고, 결과적으로 모든 문제 정답이라는 나름 기분좋은 성적으로 진출할 수 있었다.\nRound 1 A번이 같은 상황에 데이터 조건만 다르게 해 A1, A2, A3으로 분리되어 나왔다. 보통 이렇게 분할된 문제들은 A3이 A1, A2의 상위호환2인 경우가 많은데, 막상 문제를 보니 각 상황별로 다른 알고리즘을 사용해야 했다.\n이번 Round 1에선 C번 문제를 제외한 나머지를 전부 제출했는데, 제출 후 얼마 지나지 않아 A3에서 세그트리로 처리해야할 최대 배열 사이즈를 잘못 설정했다는 것을 깨달았다. validate step에서는 걸러지지 않았던 문제라 눈치채지 못했고, 라운드 종료 후 확인해보니 예상대로(?) 오답 판정을 받았다. 배열 사이즈를 2배로 늘린 후 다시 제출하니 정답 판정이어서 조금 아쉬웠지만 어쨌든 Round 2로 진출했으니 \u0026ldquo;제대로 된 풀이를 떠올린 건 맞다\u0026quot;는 점에 의의를 두기로 했다.\nRound 2 올해도 Round 2는 일요일 새벽 2시부터 5시까지 진행됐다. 작년엔 자느라 30분 늦게 시작했던 아쉬움이 있었기에 이번엔 꼭 시간을 맞추겠다는 다짐을 했고, 편의점에서 미리 에너지드링크까지 사오면서 단단히 준비했다.\n하지만 1시에 \u0026ldquo;잠깐 눈좀 붙일까\u0026quot;라는 생각을 해서는 안 됐음에도 한 게 화근이었고, 그렇게 눈을 뜨니 2시 19분이었다(\u0026hellip;) 다행히 바뀐 규정에 따라 1문제 이상만 해결해도 티셔츠는 받을 수 있었기에 부담을 조금 내려놓고 대회를 시작했다. A번은 Round 2인 걸 감안하면 간단한 난이도, B는 약간의 수식을 세우면 $O(N^2)$ DP로 풀리는 확률계산 문제였다. 이후 시간을 들여 C를 코딩했으나 예제가 나오지 않아 확인하던 중, 문제 접근을 살짝 엇나갔다는 사실을 깨달았다.\n패널티를 확인해보니 C를 마저 풀어도 Round 3 진출(상위 200명)이 아슬아슬한 상황이었다. 어떻게 할지 고민했지만 어차피 결과에 큰 차이는 없다는 생각(과 자다 깨서 대회를 뛴 피곤함) 끝에 C를 더 잡지 않고 최종 629등으로 대회를 마무리했다.\n일찍 일어났어도 200등은 애매했을거같다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n각각의 데이터 조건이 subset 관계를 이뤄, A3을 풀었다면 같은 소스코드를 A1, A2에 제출하면 통과되는 경우.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020년 8월 30일","externalUrl":null,"permalink":"/blog/facebook-hackercup-2020-review/","section":"Posts","summary":"TL;DR : 이번엔 티셔츠 받음\n2019년 해커컵에 이어 올해도 2020년도 페이스북 해커컵 대회가 열렸다. 이번에도 Round 2에서 늦잠을 자면서 Round 3은 달성하지 못했지만1 티셔츠까지는 받을 수 있었다. 구글 코드잼은 작년에 티셔츠를 받았지만 해커컵은 이번이 첫 티셔츠여서 그런지 나름 만족스럽다.\n그동안 해커컵에서의 제일 큰 불만은 small / large가 없어서 조금이라도 실수하면 돌이킬 수가 없다라는 점이었는데, 이번에는 validate step이 나와서 이런 점이 조금은 완화됐다. 코드포스의 pretest 느낌으로 validate_input.txt을 제공하고, validate_output.txt를 제출하면 이에 대한 채점결과를 알려주는 시스템이 추가되면서 “그래도 바보같은 코딩실수는 안했구나\"라는 확인을 할 수 있었다.\n","title":"Facebook Hacker Cup 2020 후기","type":"posts"},{"content":"TL;DR : 올해도 티셔츠 받음\n지난주 토요일 2020년 코드잼을 마쳤다. 사실 대회에 대한 설명이나 기본적인 소개는 작년에 거의 똑같은 글을 썼어서 이번엔 별로 할 말이 많지는 않다.\n작년에 이어 이번에도 티셔츠를 받을 수 있는 Round 3까지 진출에 성공했다. 작년 티셔츠는 L사이즈로 신청했는데, 한국이랑 미국 옷의 기준이 다른건지 티셔츠 끝이 거의 허벅지까지 내려와 밖에서 입고 다니지는 못했다.\n같은 실수를 반복하지 않기 위해 올해는 사이즈를 M으로 변경했다. 올해부터는 신청한 정보로 알아서 주는 게 아니라 쇼핑몰 같은 곳에 들어가 상품코드(메일로 전달받음)를 입력하는 형식으로 바뀐 모양이다.\n1, 2라운드를 안정적으로 통과했던 작년과 달리 이번엔 Round 1A 중간에 뇌정지(\u0026hellip;)를 겪으면서 1500등 안에 들지 못했고, 이 때문에 Round 1B가 있기 전까지 \u0026ldquo;그동안 PS공부 안한 벌을 드디어 받는건가\u0026quot;라는 생각에 살짝 의기소침해 있었다. 다행히 Round 1B에서 진출에 성공하면서 끝없는 자기반성의 늪에 빠지는 일은 막을 수 있었다(?)\n작년 초까지는 코드포스나 백준에서 눈에 들어오는 문제가 있으면 풀어보곤 했는데, 하반기 이후로는 2019년 코드잼을 제외하면 알고리즘보다는 대학원과 직장(?)에 집중하느라 문제풀이를 많이 하지는 못했다. 그러다보니 이번 코드잼에선 예전만큼 성적이 좋지 못할까봐 조금 걱정했는데 그래도 어떻게 Round 3까지 올라간 덕에 티셔츠도 받고 나름 만족스럽게 끝낼 수 있었다.\n이번 코드잼에서는 신선한 인터렉티브 문제들이 많았다. Round 1B에서의 Blindfolded Bullseye나 Round 3에서의 Pen Testing같은 문제들은 대체 어떤 발상을 해야 출제할 수 있는지 궁금할 정도였다. Pen Testing은 나중에라도 다시 풀어볼까 싶다.\n작년 코포 오렌지를 찍고 Competitive Programming을 쉬겠다는 글을 쓰면서 \u0026ldquo;이제 대학원에 집중해야지\u0026quot;라고 생각했는데, 막상 1년동안 뭘 했나 되돌아보면 잘 모르겠다. 적당한 학회에 2저자 포스터 하나, 마이너한 학회1에 1저자 논문 하나를 쓰긴 했는데 아직 당당히 내놓을 만한 실적은 딱히 없는 것 같다. 아직 연구의 주 방향을 못 잡고다보니 조금씩 조급해지는 느낌이다. 이런 와중이어서인지 이번 대회가 작게나마 위안이 된다.\n너무 마이너해서 어디 내놓기도 좀 망설여진다(\u0026hellip;)\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020년 6월 11일","externalUrl":null,"permalink":"/blog/Google-CodeJam-2020-review/","section":"Posts","summary":"TL;DR : 올해도 티셔츠 받음\n지난주 토요일 2020년 코드잼을 마쳤다. 사실 대회에 대한 설명이나 기본적인 소개는 작년에 거의 똑같은 글을 썼어서 이번엔 별로 할 말이 많지는 않다.\n작년에 이어 이번에도 티셔츠를 받을 수 있는 Round 3까지 진출에 성공했다. 작년 티셔츠는 L사이즈로 신청했는데, 한국이랑 미국 옷의 기준이 다른건지 티셔츠 끝이 거의 허벅지까지 내려와 밖에서 입고 다니지는 못했다.\n","title":"Google Code Jam 2020 후기","type":"posts"},{"content":"지난번 글에서는 이산 로그 문제에 대해 다뤘으니 이번에는 소인수분해 문제를 해결하는 알고리즘에 대해 알아보자. 코드는 이전과 마찬가지로 아래와 같은 수정이 들어간 파이썬 문법을 따랐다.\n양 끝을 명시적으로 나타내기 위해 range(st, ed+1) 대신 [st...ed]와 같은 표기를 사용했다. 파이썬에서 거듭제곱을 나타내는 a**b 대신 일반적으로 사용되는 a^b 표기를 사용했다. 지난 이산 로그 문제와는 달리 모두가 알 것이라 믿고 소인수분해 문제에 대한 자세한 설명은 생략한다. 또한 알고리즘 설계를 간단히 하기 위해 소인수분해 문제의 요구조건을 주어진 자연수 n에 대해, 1과 자기 자신(=n)을 제외한 약수를 발견하는 것으로 정했다.\nBrute-Force 가장 간단한 방법은 모든 경우를 계산해보는 것이다. 그런데 $n$이 $a$를 약수로 가지면 $n/a$도 약수로 가진다는 사실을 이용하면 전체 $n$이 아닌 $\\sqrt{n}$ 까지만 시도해보면 충분함을 알 수 있다.\ndef factor(n): for d in [2, ..., floor(sqrt(n))]: if n % d == 0: return d return None 이는 $O(\\sqrt{n})$ 만큼의 시간이 소요된다. 1억 이하의 작은 수들에 대해서는 충분할 수 있으나, 이보다 큰 숫자들에 대해서는 이후 소개할 알고리즘들을 사용해야 한다.\nPollard-(p-1) Algorithm Pollard-(p-1) 알고리즘은 페르마 소정리(Fermat\u0026rsquo;s Little Theorem)을 활용한다. 주어진 $n$이 어떤 소수 $p$를 약수로 가진다면 페르마 소정리에 의해 $a^{p-1} \\equiv 1 \\pmod p$ 이 성립하고, 이는 자연수 $k$에 대해 $a^{(p-1)\\cdot k} - 1$이 $p$의 배수가 됨을 의미한다. 다시 말해, 어떤 자연수 $m$이 $p-1$의 배수가 된다면 $gcd(a^m - 1, n)$ 은 최소한 $p$를 약수로 가지게 된다.\npollard-(p-1) 알고리즘에서는 적당히 큰 $B$에 대해 $(p-1) \\| B!$ 이 성립하기를 기대한다. 만일 $p-1$이 $B$보다 큰 소인수를 가지지 않고1, 각 소인수가 포함된 개수2가 크지 않다면 $(p-1) \\| B!$ 을 만족할 수 있다. 이를 이용한 코드를 아래에 작성했다. 알고리즘에서 $a$의 선택은 영향을 미치지 않으나 일반적으로 2를 사용한다.\ndef factor(n, B=1000): a = 2 for i in [1, ..., B]: a = (a^i) % n d = gcd(a-1, n) if 1 \u0026lt; d \u0026lt; n: # d == 1 means B is too low return d # d == n means B is too high else: return None 역으로 말해, 이러한 알고리즘으로 소인수분해되지 않게 하려면 n의 소인수 p가 (p-1)​이 큰 소인수를 가진다는 조건을 만족하도록 설정해야 한다.\nPollard-rho Algorithm pollard-rho(ρ) 알고리즘은 지난 이산 로그 문제에서와 마찬가지로 일종의 충돌쌍을 이용해 소인수를 찾는 방식이다. 구체적으로, 어떤 $x, x'$이 $x\\neq x'$, $x \\equiv x' \\pmod p$을 만족한다면 $gcd(x - x', n)$ 은 최소한 $p$를 약수로 가진다는 사실을 이용한다. 랜덤한 $a, b$들을 시험해보면서 $gcd(a - b, n)\u003e1$ 을 만족하는지 알아보는 대신, 조사를 효율적으로 진행하기 위해 변환규칙을 사용한다.\n위 알고리즘에서의 변환규칙 $f()$를 보면 한번 충돌($x_{i}=x_{2i}$)이 발생하면 $x_{i+1}=x_{2i+1},\\cdots$ 로 계속 이어짐을 쉽게 파악할 수 있다. 따라서 1칸씩 움직이는 $s_i$와 2칸씩 움직이는 $s_{2i}$, 이렇게 2개만을 이용해도 많은 쌍을 동시에 검증하는 효과를 얻을 수 있다.\n이전 글에서의 Pollard-rho 알고리즘 설명 중\n이산 로그 문제에서와 달리 이번에는 변환규칙 $x_{i+1} = f(x_i)$을 정할 때 경우를 나누지 않으며, 일반적으로 $f(x) = x^2 + 1$을 많이 사용한다.\ndef factor(n, max_iter=1000): # transition rule def f(x): return (x*x + 1) % n # function main x1 = x2 = 1 for i in [0...max_iter]: x1 = f(x1) x2 = f(f(x2)) d = gcd(x1 - x2, n) if 1 \u0026lt; d \u0026lt; n: return d return None Dixon\u0026rsquo;s Random Square Algorithm 만일 $x^2 \\equiv y^2 \\pmod n$이고 $x \\not \\equiv \\pm y \\pmod n$ 를 만족하는 $x,y$를 찾으면 $gcd(x-y, n) \u003e 1$이 성립하므로 $n$의 약수를 찾을 수 있게 된다. Dixon\u0026rsquo;s Random Square 알고리즘은 이러한 $x, y$를 찾는 방법을 제시한다.\n알고리즘은 다음과 같이 진행된다.\n소수들의 집합 $B = \\{p_1, p_2, \\cdots, p_t\\}$를 정한다. (일반적으로 작은 순으로 $t$개의 소수들을 사용)\n랜덤한 $x_1, x_2, \\cdots$들에 대해 다음을 진행한다.\n$x_i^2 \\equiv b_i \\pmod n$에 대해 $b_i$를 $B$에 속한 소수들로 인수분해한다. $b_i = \\prod_{p_j \\in B} p_j^{c_j}$ 꼴로 정리하면 $b_i$를 $\\langle c_1, c_2, \\cdots, c_t \\rangle$ 로 쓸 수 있으며, $B$에 속한 소수들만으로 인수분해가 불가능할 경우 새로운 $x_i$를 뽑는다. 2번 과정을 거치면서 구한 벡터들 $\\langle c_1, c_2, \\cdots, c_t \\rangle_{\\{i\\}}$ 을 결합해 모든 원소가 짝수인 벡터(=제곱수)를 만들 수 있는지 확인한다.\n→ 해당 벡터를 만드는 $\\prod x_i$와 $\\sqrt{\\prod b_i}$는 각각 $a^2 \\equiv b^2 , a \\not \\equiv \\pm b \\pmod n$ 를 만족하는 $a, b$가 된다. 만일 $a \\equiv \\pm b \\pmod n$이라면 계속 탐색을 진행한다\n실제 예시를 통해 살펴보자. $B = \\{2, 3, 5, 7, 11, 13\\}$ 을 이용해 $n=15770708441$을 소인수분해하려고 하고, 아래와 같은 정보를 얻었다고 하자.\n$$ \\begin{align*} (2773700011)^2 \u0026 \\equiv 2 \\times 3\\times 13 \u0026\\pmod n\\\\ (8340934156)^2 \u0026 \\equiv 3 \\times 7 \u0026\\pmod n\\\\ (12044942944)^2 \u0026 \\equiv 2 \\times 7 \\times 13 \u0026 \\pmod n \\end{align*} $$이를 종합하면 다음을 얻을 수 있다.\n$$ (8340934156 \\times 12044942944 \\times 2773700011)^2 \\equiv (2 \\times 3 \\times 7 \\times 13)^2 \\pmod n $$계산하면 식의 왼쪽은 $(9503435785)^2$, 오른쪽은 $(246)^2$ 이 되고, 여기에 유클리드 알고리즘을 이용해 $(9503435785 - 246)$과 $15770708441$ 의 공약수를 구하면 n의 자명하지 않은 약수 $15770708441$을 얻을 수 있다.\n알고리즘의 핵심이 되는 3번 과정은 $\\langle c_1, c_2, \\cdots, c_t \\rangle_{\\{i\\}}$ 에 $\\mathbb{Z}_2$ 위에서의 가우스 소거법을 적용해 찾아낼 수 있다.\n이러한 성질을 B-smooth 라고 한다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n$p-1 = \\prod q_i^{c_i}$에서 $c_i$\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020년 5월 27일","externalUrl":null,"permalink":"/blog/algorithms-for-integer-factorization/","section":"Posts","summary":"지난번 글에서는 이산 로그 문제에 대해 다뤘으니 이번에는 소인수분해 문제를 해결하는 알고리즘에 대해 알아보자. 코드는 이전과 마찬가지로 아래와 같은 수정이 들어간 파이썬 문법을 따랐다.\n양 끝을 명시적으로 나타내기 위해 range(st, ed+1) 대신 [st...ed]와 같은 표기를 사용했다. 파이썬에서 거듭제곱을 나타내는 a**b 대신 일반적으로 사용되는 a^b 표기를 사용했다. 지난 이산 로그 문제와는 달리 모두가 알 것이라 믿고 소인수분해 문제에 대한 자세한 설명은 생략한다. 또한 알고리즘 설계를 간단히 하기 위해 소인수분해 문제의 요구조건을 주어진 자연수 n에 대해, 1과 자기 자신(=n)을 제외한 약수를 발견하는 것으로 정했다.\n","title":"소인수분해 알고리즘(Pollard-(p-1), Pollard-rho, Dixon's Random Square)","type":"posts"},{"content":"","date":"2020년 5월 27일","externalUrl":null,"permalink":"/tag/%EC%88%98%ED%95%99/","section":"Tags","summary":"","title":"수학","type":"tags"},{"content":"전산에 관한 잡다한 글을 씁니다\n","date":"2020년 5월 27일","externalUrl":null,"permalink":"/category/%EC%A0%84%EC%82%B0/","section":"Categories","summary":"전산에 관한 잡다한 글을 씁니다\n","title":"전산","type":"categories"},{"content":"직장 도서관에서 책을 빌릴 수 있긴 하지만 책을 신청하면 빨라도 1주일 후에 구매되는 일이 많고, 약속시간이 애매하게 남거나 굳이 책을 넣어갈 가방이 없는 경우(ex: 노트북 파우치) 종이책을 들고다니기엔 부담스러웠던 적이 종종 있었다. 그러던 중 지난 4월 초 직장에서 상여금이 나왔고(!) 주변 지인들로부터 전자책에 대한 긍정적인 평을 많이 들었던 터라 전자책 리더기를 하나 장만했다.\n이참에 태블릿PC를 하나 장만할까라는 생각도 잠깐 했지만 휴대성 면에서 아무래도 전용리더기만큼의 효율이 나오긴 힘들 것 같았기에 전자잉크 리더기로 결정했다. 전자잉크 리더기들이 대부분 자회사들만 사용할 수 있다는 제약이 있다. 하지만 책이라는 특성상 독점작에 크게 영향받는 것도 아니기에 어차피 루팅하면 그건 크게 상관없기도 하고 기기 자체의 디자인이나 편의성을 위주로 고민했다.\n최종 결론은 리디북스에서 나온 페이퍼 프로(Paper Pro)였다. 다른 리더기들이 6인치로 작은 사이즈였던 반면, 8인치 크기로 일반적인 책 크기와 같으면서 베젤이 얇아 깔끔해 보인다는 사실이 마음을 끌었다. 마침 리디페이퍼에서 6인치 크기의 신제품을 출시하면서 기존 8인치 기기의 할인행사1를 진행하고 있어 저렴하게 구입할 수 있었다.\n사진 왼쪽이 리더기 본체, 오른쪽이 함께 구입한 북커버 케이스의 모습이다.\n내부를 열면 간단한 설명서와 함께 본체를 볼 수 있다.\n전자잉크를 처음 접해보는데 실제 종이와 거의 동일한 느낌이어서 신기했다. 사진엔 드러나지 않지만 밝기 및 색온도(백열등 vs 형광등을 생각하면 된다) 조절도 가능하다.\n커버를 닫았을 때의 사진. 슬림한 두께와 커버의 질감이 마음에 든다.\n처음엔 택배를 받고 바로 후기를 작성하려 했는데 \u0026ldquo;그래도 한달은 써보고 생각해봐야지\u0026rdquo; 라는 생각에 계속 미뤄왔다. 결론부터 말하면 꽤 만족스러운 구매였다. 물론 아직도 방에서는 많은 시간을 넷플릭스에 쓰지만(\u0026hellip;) 밖에 돌아다닐 때는 이전보다 책을 많이 읽게 됐다. 소소하게 외출할 때나 약속장소에 갈 때 파우치나 크로스백에 부담없이 넣을 수 있는 사이즈에, 읽다 지루해지면 이 책 저 책 번갈아가며 읽을 수 있다는게 큰 장점이다.\n마무리는 카페에서 찍은 설정샷(\u0026hellip;)\n정가 25만원 → 할인가 17만원\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2020년 5월 18일","externalUrl":null,"permalink":"/blog/ridibooks-paper-pro-review/","section":"Posts","summary":"직장 도서관에서 책을 빌릴 수 있긴 하지만 책을 신청하면 빨라도 1주일 후에 구매되는 일이 많고, 약속시간이 애매하게 남거나 굳이 책을 넣어갈 가방이 없는 경우(ex: 노트북 파우치) 종이책을 들고다니기엔 부담스러웠던 적이 종종 있었다. 그러던 중 지난 4월 초 직장에서 상여금이 나왔고(!) 주변 지인들로부터 전자책에 대한 긍정적인 평을 많이 들었던 터라 전자책 리더기를 하나 장만했다.\n","title":"리디북스 페이퍼 프로(Ridi Paper Pro) 사용기","type":"posts"},{"content":"암호학, 특히 공개키 기반 암호시스템은 주로 1)소인수분해 문제 혹은 2)이산 로그 문제를 기반으로 설계되는 경우가 많다. 그중 이산 로그 문제와 이를 해결하는 알고리즘에 대해 알아보자. 예시로 든 코드는 기본적으로 파이썬 문법을 따라 작성했으며 가독성을 위해 다음과 같은 부분을 수정했다.\n파이썬에서 range(n)은 0부터 시작해 n이 아닌 n-1까지를 포함한다. 양 끝을 명시적으로 나타내기 위해 [st...ed]와 같은 표기를 사용했다. 파이썬에서 거듭제곱을 나타내는 a**b 대신 일반적으로 사용되는 a^b 표기를 사용했다. mod p에서의 역원을 a^(-1)로 표기했다. 실제 구현에서는 다음과 같이 구할 수 있다. 페르마 소정리에 의해 $a^{p-1} \\equiv 1 \\pmod p$이므로 $a\\cdot a^{p-2} \\equiv 1$에서 $a^{-1} \\equiv a^{p-2}$ $p$가 소수가 아닌 경우, 확장 유클리드 알고리즘을 사용한다 이산 로그 문제 이산 로그 문제는 이산 대수 문제라고도 하며, 순환군 $\\langle g \\rangle$와 군의 원시근(primitive root) $g$, 그리고 $y \\in \\langle g \\rangle$ 가 주어졌을 때, $y=g^k$를 만족하는 최소의 자연수 $k$를 찾는 문제이다. 암호학에서는 큰 소수 $p$에 대해 $p$로 나눈 나머지들의 모임인 $\\mathbb{Z}_p^\\ast$를 사용하는 경우가 많다.\n일반적인 실수($\\mathbb{R}$) 체계에서 이는 $\\log_{g}y$ 가 된다는 점에서 이산 로그 라는 이름이 붙었으며, $a \u003c b$ 이면 $\\log_g{a} \u003c \\log_g{b}$라는 성질을 이용해 산술적으로 쉽게 해를 구할 수 있는 일반적인 로그 문제와 달리 군에서는 이러한 대소 비교가 원활하지 않기에 어려움이 있다.\n예로 mod 13에 대해 원시근 g가 2인 상황을 살펴보자\n$k$ 0 1 2 3 4 5 6 7 8 9 10 11 12 $g^k$ 1 2 4 8 3 6 12 11 9 5 10 7 1 6 \u0026gt; 5 이지만 $g^k=5$를 만족하는 k는 5, $g^k=5$를 만족하는 k는 9로 대소 관계가 유지되지 않는다. 이러한 불규칙성으로 인해 일반적인 이산 대수 문제를 해결하는 효율적인 알고리즘은 아직 발견되지 않았다.\n이제 이를 해결하는 몇 가지 방법들에 대해 알아보자. 단 문제를 풀 순환군이 소수 $p$에 대해 $\\mathbb{Z}_p^\\ast$ 인 경우만을 생각하며, 편의상 군의 크기를 $n(=p-1)$으로 표현하기로 한다.\nBrute-Force 가장 간단한 방법은 모든 경우를 계산해보는 것이다. 이를 코드로 표현하면 다음과 같다.\ndef solveDLP(y, g, p): t = 1 for i in [0...p-2]: if t == y: return i t = (t * g) % p return None 이는 추가 메모리는 $O(1)$로 거의 필요하지 않지만 최악의 경우 순환군의 원소의 개수, 즉 $O(n)$ 만큼의 시간이 소요된다.\nShanks\u0026rsquo; Algorithm Shanks 알고리즘은 baby-step, giant-step 알고리즘이라고도 불리며 다음과 같이 동작한다.\n군의 크기(n, 여기서는 n=p-1)에 대해 $m=\\sqrt{n}$을 지정한다.\n$i=0, 1, \\cdots, (m-1)$에 대해 $(a_i, b_i) = (y\\cdot g^{-i}, i)$들을 기록한다.\n$j=0, 1, \\cdots, \\lceil p/m \\rceil$ 에 대해 $(a_j, b_j) = (g^{m\\cdot j}, m \\cdot j)$들을 기록한다.\n2, 3에서 $a_i = a_j$인 쌍을 찾으면 $\\log_g{y} = (b_i + b_j)$ 가 성립한다.\n위 알고리즘의 정당성은 4. 에서 $a_i = y\\cdot g^{-i} = g^{m\\cdot j} = a_j$ 라는 사실을 생각하면 쉽게 알 수 있다. 거듭제곱을 1칸씩 뛰는 2단계(baby step)과 m칸씩 뛰는 3단계(giant step)로 이루어져 있어 이같은 이름이 붙었다. 알고리즘을 코드로 나타내면 다음과 같다.\ndef solveDLP(y, g, p): m = sqrt(p) baby, giant = {}, {} val = 0 gm = (g^m) % p for i in [0...p//m]: giant[val] = m * i # giant[g^(m*i)] = m*i val = (val * gm) % p val = y for i in [0...m-1]: baby[val] = i # baby[y * g^(-i)] = i if val in giant.keys(): return giant[val] + i val = (val * g^(-1)) % p return None 딕셔너리에 키의 존재성을 확인하는 단계에서 균형이진트리를 가정하면, 위 알고리즘은 메모리 $O(\\sqrt{n})$, 시간 $O(\\sqrt{n} \\log n)$의 복잡도를 가진다.\nPollard-rho Algorithm pollard-rho(ρ) 알고리즘은 $g, y$에 대해 $g^{a}y^{b}=g^{a'}y^{b'}$를 만족하는 쌍 $(a, b) \\neq (a', b')$을 찾을 수 있다면 이로부터 $\\log_g y = (a - a') \\cdot (b' - b)^{-1}$를 구할 수 있다는 점에 착안한다. 그러나 임의의 $a, a', b, b'$에 대해 $g^a b^y, g^{a'}y^{b'}$를 계산해 두 값이 동일하기를 기대하는 것은 비효율적이므로, 이를 위해 다음과 같은 방식을 사용한다.\n순환군 $G(=\\mathbb{Z}_p^\\ast)$의 원소를 3종류로 분할한다. ($G = S_0 \\cup S_1 \\cup S_2, i\\neq j \\rightarrow S_i \\cap S_j = \\emptyset$) 분할방식은 무관하며, 일반적으로 $S_i = \\{x \\mid x \\equiv i \\pmod 3\\}$ 을 사용 $s_0 = (x_0, a_0, b_0) = (1, 0, 0)$에서 시작한다.\n$(x_i, a_i, b_i)$에서 $x_i$에 따라 $(x_{i+1}, a_{i+1}, b_{i+1})$를 구하는 규칙 $s_{i+1} = f(s_i)$를 설정한다.\n$$ f(s_i) = (x_{i+1}, a_{i+1}, b_{i+1})= \\begin{cases} (y\\cdot x_{i},\u0026 a_i,\u0026 b_i+1)\u0026\\mathrm{if}\\,\\,x_i \\in S_0 \\\\ (x_{i}^2,\u0026 2a_i,\u0026 2b_i)\u0026\\mathrm{if}\\,\\,x_i \\in S_1\\\\ (g\\cdot x_{i},\u0026 a_i+1,\u0026 b_i)\u0026\\mathrm{if}\\,\\,x_i \\in S_2 \\end{cases} $$ $s_{i+1} = f(s_i), s_{2i+2} = f(f(s_{2i}))$임을 이용해 $s_i, s_{2i}$들을 탐색해 $x_i = x_{2i}$ 여부를 확인한다. 위 알고리즘에서의 변환규칙 $f( )$를 보면 한번 충돌($x_i = x_{2i}$)이 발생하면 $x_{i+1} = x_{2i+1}, \\cdots$ 로 계속 이어짐을 쉽게 파악할 수 있다. 따라서 1칸씩 움직이는 $s_i$와 2칸씩 움직이는 $s_{2i}$, 이렇게 2개만을 이용해도 많은 쌍을 동시에 검증하는 효과를 얻을 수 있다. 이를 코드로 나타내면 다음과 같다.\ndef solveDLP(y, g, p, max_iter=1000): # transition rule def f(x, a, b): if x % 3 == 0: return (y*x)%p, a, b+1 elif x % 3 == 1: return (x*x)%p, 2*a, 2*b else: return (g*x)%p, a+1, b # function main (x1, a1, b1) = (x2, a2, b2) = (1, 0, 0) for i in [0...max_iter]: x1, a1, b1 = f(x1, a1, b1) x2, a2, b2 = f(f(x2, a2, b2)) if x1 == x2: # (p-1)은 소수가 아니므로 ax = b (mod p-1)의 해가 유일하지 않을 수 있다 # 실제 코드에서는 이를 고려한 처리가 필요하다 return ((a1 - a2) * (b2 - b1)^(-1)) % (p-1) return None 이 알고리즘의 수행시간은 transition rule $f$의 설정과 분할방식 $(S_0,S_1,S_2)$에 따라 달라질 수 있으며, 근사적으로 $O(\\sqrt n)$ 정도임이 알려져 있다.\nPohlig-Hellman Algorithm pohlig-hellman 알고리즘은 순환군 $\\mathbb{Z}_p^\\ast$의 크기 n(=p-1)이 작은 소인수들로 인수분해될 때 사용할 수 있는 방식이다. 구체적으로, 소수 $q_i$들을 이용해 $$ |G| = |\\mathbb{Z}_p^\\ast| = n = (p-1) = \\prod_{i=1}^m q_i^{c_i} $$ 로 표현했을 때 $q_i$들이 모두 작은 경우에 사용할 수 있다. 이 알고리즘은 페르마 소정리에 의해 $g^{p-1}\\equiv 1 \\pmod p$라는 사실과, 중국인의 나머지 정리에 의해 $g^k \\equiv y \\pmod p$를 만족하는 k는\n$$ k \\equiv k_1 \\pmod {q_1^{c_1}}\\\\ k \\equiv k_2 \\pmod {q_2^{c_2}}\\\\ \\vdots\\\\ k \\equiv k_2 \\pmod {q_m^{c_m}} $$들이 주어졌을 때 유일하게 결정된다는 사실을 이용한다.\n이제 $k \\pmod {q_1^{c_1}}$을 구하는 방법을 알아보자. 가독성을 위해 $q_i, c_i$ 대신 $q, c$로 표기했다.\n먼저 $k = a_0 + a_1 \\cdot q + \\cdots + a_{c-1} \\cdot q^{c-1} + s \\cdot q^c$으로 쓸 수 있다. 그러면 주어진 y에 대해\n$$ \\begin{aligned} y^{n/q} = (g^k)^{n/q} \u0026= (g^{a_0 + a_1 \\cdot q + \\cdots + a_{c-1} \\cdot q^{c-1} + s \\cdot q^c})^{n/q}\\\\ \u0026= (g^{a_0+S\\cdot q})^{n/q}\\\\ \u0026= g^{a_0 \\cdot n/q}\\cdot g^{S\\cdot n}\\\\ \u0026= g^{a_0 \\cdot n/q}\\cdot 1^{S} \\end{aligned} $$가 성립하고, 이를 이용해 $a_0$를 최대 q번의 시도로 구할 수 있다. 마찬가지 방법으로 $y^{n/{q^2}}$을 통해 $a_1$을 구할 수 있고, 이를 계속해 $a_i$들을 구해가면 최종적으로 $k \\pmod {q^{c}}$를 얻는다.\n이제 이를 다른 소인수에도 마찬가지로 적용하면서 m가지 식 $k \\equiv k_i \\pmod {q_i^{c_i}}$ 을 모두 세우면, 중국인의 나머지 정리를 통해 $k \\pmod p$를 구할 수 있다. $p-1$을 소인수분해하는 과정을 제외하면 이 알고리즘은 $O\\left(\\sum (c_i \\cdot q_i \\cdot \\log p) + C\\right)$의 복잡도를 가진다. (Square-and-Multiply 방식을 사용하면 거듭제곱에 $O(\\log p)$가 소요되며, $C$는 중국인의 나머지 정리를 적용하는 데 소요되는 시간을 의미한다)\n","date":"2020년 5월 8일","externalUrl":null,"permalink":"/blog/algorithms-for-discrete-logarithm-problem/","section":"Posts","summary":"암호학, 특히 공개키 기반 암호시스템은 주로 1)소인수분해 문제 혹은 2)이산 로그 문제를 기반으로 설계되는 경우가 많다. 그중 이산 로그 문제와 이를 해결하는 알고리즘에 대해 알아보자. 예시로 든 코드는 기본적으로 파이썬 문법을 따라 작성했으며 가독성을 위해 다음과 같은 부분을 수정했다.\n파이썬에서 range(n)은 0부터 시작해 n이 아닌 n-1까지를 포함한다. 양 끝을 명시적으로 나타내기 위해 [st...ed]와 같은 표기를 사용했다. 파이썬에서 거듭제곱을 나타내는 a**b 대신 일반적으로 사용되는 a^b 표기를 사용했다. mod p에서의 역원을 a^(-1)로 표기했다. 실제 구현에서는 다음과 같이 구할 수 있다. 페르마 소정리에 의해 $a^{p-1} \\equiv 1 \\pmod p$이므로 $a\\cdot a^{p-2} \\equiv 1$에서 $a^{-1} \\equiv a^{p-2}$ $p$가 소수가 아닌 경우, 확장 유클리드 알고리즘을 사용한다 이산 로그 문제 이산 로그 문제는 이산 대수 문제라고도 하며, 순환군 $\\langle g \\rangle$와 군의 원시근(primitive root) $g$, 그리고 $y \\in \\langle g \\rangle$ 가 주어졌을 때, $y=g^k$를 만족하는 최소의 자연수 $k$를 찾는 문제이다. 암호학에서는 큰 소수 $p$에 대해 $p$로 나눈 나머지들의 모임인 $\\mathbb{Z}_p^\\ast$를 사용하는 경우가 많다.\n","title":"이산 로그 문제(DLP)에 대한 알고리즘(Shanks, Pollard-rho, Pohlig-Hellman)","type":"posts"},{"content":" 발상: Knockoff Nets 어제(2일) 대학원에서 Mario Fritz 박사님의 AI Security에 대한 강연을 들어볼 수 있었다. 딥러닝이 대세가 되면서 등장한 여러 보안 문제들에 대한 특강이었는데, 그중 Model Stealing(구체적으로는 Functionality Stealing)에 대해 다룬 knockoff-net 관련 내용이 흥미로워 pytorch로 구현해보았다. 다만 노트북이라는 한계로 원본 논문보다는 디테일을 다소 단순화했다(\u0026hellip;)\nKnockoff net을 간단히 말하면 이미 학습이 끝난 모델 V를 블랙박스로 이용해 (V와 같은 기능을 하도록) 자신의 모델 A를 학습시키는 것이라고 할 수 있다. 모델을 학습시킬 때 마주하는 문제 중 하나는 소위 좋은 학습 데이터를 구하기 어렵다는 점이다. Classification 문제를 생각해봐도 대표적인 MNIST나 CIFAR-10 등 유명한 일부를 제외하면 잘 정제되어 있고 + 올바른 라벨이 붙어있는 데이터는 구하기 쉽지 않다. 이때 이런 고급 데이터셋으로 학습된 모델 V를 이용해 (해당 데이터셋 없이) 새로운 모델 A를 학습시킨다고 생각해 보자.\n핵심은 모델 V를 블랙박스처럼 사용해 모델 A를 위한 학습 데이터셋을 생성하는 것이다. 딥러닝의 원리를 생각해보면 이를 좀더 직관적으로 이해할 수 있다. Classification Model의 경우\n가상의 함수 \\(F\\_\\{ideal\\}\\) : 이미지를 넣으면 원하는 라벨이 one-hot 인코딩되어서 나오는 함수 모델 V의 .forward()(이하 \\(F\\_V\\)) : 위 \\(F\\_\\{ideal\\}\\)을 모방하는 함수 라고 하면 \\(F\\_V = F\\_\\{ideal\\}\\)가 우리가 원하는 결과이며, 이를 위해 모델 V의 파라미터를 조절하는 것이다. 다시 말해, 딥러닝 모델의 학습은 주어진 Training Dataset \\(D=\\{ (d\\_\\{sample\\}, d\\_\\{label\\}) \\} \\)에 대해 \\(F\\_\\{V\\}(d\\_\\{sample\\}) \\simeq F\\_\\{ideal\\}(d\\_\\{sample\\})=d\\_\\{label\\}\\) 이면 일반화된 다른 입력에 대해서도 \\(F\\_\\{V\\}(sample) \\simeq F\\_\\{ideal\\}(sample)=label\\) 이 될 것이라는 기대에 기반한다.\n그렇다면 \\(D\\)가 아닌 다른 데이터셋 \\(R=\\{ (r\\_\\{sample\\}, \\_) \\} \\)에 대해 \\(F\\_A(r\\_\\{sample\\}) \\simeq F\\_V(r\\_\\{sample\\})\\)이 되도록 조정한다면, 일반화된 샘플에 대해서 \\(F\\_\\{A\\}(sample) \\simeq F\\_\\{V\\}(sample) \\simeq F\\_\\{ideal\\}(sample)\\)이 되지 않을까? knockoff net 논문에서는 그림이라는 선은 지켰지만1 본 블로그에서는 랜덤한 노이즈를 통해 \\(R\\)을 생성했다.\n(그림 출처: Knockoff Nets: Stealing Functionality of Black-Box Models )\n제대로 학습된 모델 V 만들기 먼저 모델 V를 만들기 위해 이전 글에서 MNIST 분류기를 가져왔다. 출력값 확인을 위해 기존 모델의 끝에 softmax 레이어를 추가해 출력벡터의 각 요소를 0~1 사이로 조절했다. 이 net은 MNIST를 정상적으로 학습한 모델이 된다.\nclass MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.layer1_conv = nn.Conv2d(1, 6, 5, 1) # 입력 1개, 출력 6개, 필터 크기는 5x5, 1칸 단위로 이동하면서 필터를 씌운다 self.layer1_relu = nn.ReLU() # 활성화 함수. ReLU(x) 는 max(x, 0)과 같다 self.layer1_pool = nn.MaxPool2d(2) # 각 2x2 칸마다 최대값 하나씩만을 남긴다 self.layer2_conv = nn.Conv2d(6, 16, 5, 1) self.layer2_relu = nn.ReLU() self.layer2_pool = nn.MaxPool2d(2) self.fc = nn.Linear(16*4*4, 10) self.softmax = nn.Softmax(dim=1) def forward(self, x): x1 = self.layer1_conv(x) # 1x28x28 형식의 데이터가 6x24x24 형식으로 변환된다 x2 = self.layer1_relu(x1) # x3 = self.layer1_pool(x2) # 6x24x24 형식의 데이터가 6x12x12 형식으로 변환된다 x4 = self.layer2_conv(x3) # 6x12x12 형식의 데이터가 16x8x8 형식으로 변환된다 x5 = self.layer2_relu(x4) # x6 = self.layer2_pool(x5) # 16x8x8 형식의 데이터가 16x4x4 형식으로 변환된다 x7 = x6.view(-1, 256) # 16x4x4 형식의 데이터가 256-벡터로 변환된다 x8 = self.fc(x7) # 256-벡터가 10-벡터로 변환된다 x9 = self.softmax(x8) # 최종 10-벡터의 각 값은 0~1 사이 값을 가지고 총합은 1이 된다 return x9 net = MyModel() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters()) 위 모델을 학습시키면 5 epoch 후 test set에 대해 97.82%의 정확도를 얻을 수 있다. 적어도 내 노트북에선 그렇게 나왔다\n랜덤 노이즈 입력에서 모델 V 따라하기 모델 A를 위해 랜덤한 데이터를 생성해보자. MNIST가 28x28 크기의 흑백 이미지이므로 torch.randn 함수를 사용해 같은 크기의 이미지를 만들었다.\n이 이미지를 앞서 학습이 끝난 모델 V (net) 에 입력하면 아래와 같은 출력을 내놓는다.\ntensor([2.6900e-10, 4.2002e-23, 6.6886e-01, 1.5307e-04, 1.0798e-23, 4.9928e-07, 3.4142e-08, 1.7324e-13, 3.3099e-01, 4.0419e-12]) 모델에 따르면 이 이미지는 66.9%로 숫자 2, 33.1%로 숫자 8인 모양이다 물론 이 결과는 별다른 의미를 가지지 않는다. 그렇다면 이렇게 만든 (랜덤한 이미지, 예측값)을 학습 데이터로 사용해 모델 A를 학습시키면 어떻게 될까?\nnet_koff = MyModel() # 기존 net과 구조는 동일하다. criterion_koff = nn.BCELoss() # 라벨이 아닌 두 \u0026#39;분포\u0026#39;에 대한 학습이므로 BCELoss 사용 optimizer_koff = optim.Adam(net_koff.parameters()) num_epoch = 10 st = time.time() print(f\u0026#34;training with {len(train_data)} noises...\u0026#34;) for epoch in range(num_epoch): tot_loss = 0 for x,y in train_loader: noise_x = torch.randn_like(x) # train data와 동일한 크기의 랜덤 데이터를 생성 y_target = net(noise_x).detach() # 모델 V(net)의 예측값을 타겟으로 설정 y_pred = net_koff(noise_x) # 모델 A(net_koff)의 예측 y_pred가 y_target이 되도록 학습 loss = criterion_koff(y_pred, y_target) tot_loss += loss optimizer_koff.zero_grad() loss.backward() optimizer_koff.step() print(f\u0026#34; - Epoch {epoch+1}/{num_epoch}. loss: {tot_loss}....(elapsed {time.time() - st}s)\u0026#34;) print(f\u0026#34;done (elapsed {time.time() - st}s)\u0026#34;) # 실제 MNIST의 test data로 test accuracy 측정 net_koff.eval() acc, tot = 0, 0 for x, y in test_loader: y_pred = net_koff(x) acc += (y==y_pred.argmax(1)).sum() tot += len(y) print(f\u0026#34;test accuracy of knockoff net: {acc}/{tot} ({100*acc/tot}%)\u0026#34;) 일반적인 학습과 거의 동일하지만 train_loader에서 가져온 데이터 대신 torch.randn_like()를 이용해 같은 크기의 노이즈로 대체했다. 또한 라벨 y를 사용하는 대신 net의 출력값을 y_target으로 지정했음에 유의하자. 말이 안 되는 것 같지만 놀랍게도 학습이 끝난 후 net_koff의 test accuracy를 측정해보면 83.6% 라는 높은 수치를 보여준다.\n내부 구조가 달라도 일단 따라해보기 아마 net과 net_koff의 내부 구조(Conv 레이어 크기, hidden layer개수 등)가 동일하다는 점 때문에 학습 결과 내부 파라미터들이 높은 확률로 서로 비슷한 값을 가졌기 때문이라고 생각할 수 있다. 그래서 원본 모델(net)과 내부 구조부터 다르게 한 MyAnotherModel 클래스를 새로 작성했다.\nclass MyAnotherModel(nn.Module): def __init__(self): super(MyAnotherModel, self).__init__() self.layer1_conv = nn.Conv2d(1, 8, 5, 1) self.layer1_relu = nn.ReLU() self.layer1_pool = nn.MaxPool2d(2) self.layer2_conv = nn.Conv2d(8, 12, 5, 1) self.layer2_relu = nn.ReLU() self.layer2_pool = nn.MaxPool2d(2) self.fc1 = nn.Linear(12*4*4, 50) self.fc_relu = nn.ReLU() self.fc2 = nn.Linear(50, 10) self.softmax = nn.Softmax(dim=1) def forward(self, x): x1 = self.layer1_conv(x) # 1x28x28 형식의 데이터가 8x24x24 형식으로 변환된다 x2 = self.layer1_relu(x1) # x3 = self.layer1_pool(x2) # 8x24x24 형식의 데이터가 8x12x12 형식으로 변환된다 x4 = self.layer2_conv(x3) # 8x12x12 형식의 데이터가 12x8x8 형식으로 변환된다 x5 = self.layer2_relu(x4) # x6 = self.layer2_pool(x5) # 12x8x8 형식의 데이터가 12x4x4 형식으로 변환된다 x7 = x6.view(-1, 192) # 12x4x4 형식의 데이터가 192-벡터로 변환된다 x8 = self.fc1(x7) # 192-벡터가 50-벡터로 변환된다 x9 = self.fc_relu(x8) x10 = self.fc2(x9) # 50-벡터가 10-벡터로 변환된다 x11 = self.softmax(x10) return x11 학습은 이전 MyModel의 net_koff와 동일한 방법으로 이루어졌다. 실험 결과 이렇게 만든 net_koff2는 MNIST test set에서 약 56.3%라는 정확도를 보였다. 일반적인 MNIST에서 이는 크게 의미있는 수치가 아니지만2 학습에 사용한 입력이 전부 torch.randn([1, 28, 28])이라는 점을 고려하면 꽤 놀라운 결과라고 할 수 있다.\nUPD: 몇번 더 돌려보니 70, 80%를 넘어 MyAnotherModel에서도 91% 이상을 찍는 경우도 발생한다. 랜덤시드에 따른 문제같은데, 불필요한 오해를 피하기 위해 블로그 본문에서는 여러 번의 테스트 중 낮은 수치를 소개했다.\n후기(?) \u0026lt;파이널 판타지 14\u0026gt;라는 갓겜이 있다. 파티를 맺고 던전을 돌면서 보스를 잡는 게임인데, 직업별로 최적화된 \u0026ldquo;딜사이클\u0026quot;이라는 개념이 존재한다. 실력이 미숙해 딜미터기 최하위를 차지하던 시절, 네임드 유저분이 올린 사이클을 그대로 따라하니 원리는 모르겠지만 어쨌든 딜이 오르는 경험을 한 적이 있다. 그땐 원리 이해도 못하고 무작정 따라하는게 본 실력이라고 할 수 있나 싶었는데 knockoff net이 학습되는 과정을 보면 그런 생각은 잠시 접어둬도 될 것 같다(\u0026hellip;)\np.s. 실험에 사용한 전체 코드는 여기에서 볼 수 있다. (PC 전용)\n새 분류기를 모방하는 데 강아지, 자전거 사진 등을 이용해 학습을 진행했다\u0026#160;\u0026#x21a9;\u0026#xfe0e;\nMNIST는 간단한 모델에서도 쉽게 90% 이상의 정확도를 얻을 수 있는 것으로 잘 알려져 있다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 11월 3일","externalUrl":null,"permalink":"/blog/knockoff-net-with-complete-noise/","section":"Posts","summary":"발상: Knockoff Nets 어제(2일) 대학원에서 Mario Fritz 박사님의 AI Security에 대한 강연을 들어볼 수 있었다. 딥러닝이 대세가 되면서 등장한 여러 보안 문제들에 대한 특강이었는데, 그중 Model Stealing(구체적으로는 Functionality Stealing)에 대해 다룬 knockoff-net 관련 내용이 흥미로워 pytorch로 구현해보았다. 다만 노트북이라는 한계로 원본 논문보다는 디테일을 다소 단순화했다(…)\n","title":"[ML] Knockoff Net: 랜덤한 입력으로 다른 모델을 모방할 수 있을까?","type":"posts"},{"content":" 예제 dataset 가져오기 지금까지는 \\(y=ax+b\\)나 \\(y=ax^2+bx+c\\) 처럼 간단한 형태의 숫자→숫자 함수만을 모델링했으니 이제 좀더 흥미로운 데이터를 살펴보자.\n딥러닝, 특히 이미지 인식 관련 예제를 보면 MNIST나 CIFAR-10같은 이름이 많이 등장한다. 이 둘은 Image Classification 문제의 대표적인 예시로 각각 손글씨와 물체 사진 이미지셋에 해당하며, 흔히 모델의 벤치마크 용도로 활용되고 있다. 이번 글에서는 좀더 단순한 MNIST를 사용하기로 한다. MNIST 데이터는 아래와 같이 색이 없는 1채널(=gray scale) 28x28픽셀 이미지들의 모음으로 0에서 9까지 총 10개의 라벨을 가지고 있다.\n유명한 데이터셋인 만큼 인터넷에서 손쉽게 구할 수 있지만 있을 건 다 있는 python답게 torchvision 모듈의 datasets 에서도 가져올 수 있다. root인자로 데이터가 저장된(혹은 저장하고자 하는) 경로를 넘겨주면 이를 읽어오며, download=True인 경우 해당 경로에 자동으로 다운로드까지 함께 이루어진다.\nimport torchvision.datasets as datasets train_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=True, download=True) test_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=False, download=True) train_data에 대한 정보를 살펴보면 각 데이터는 (PIL 이미지, 라벨) 형식의 tuple로 이루어져 있음을 알 수 있다.\n이제 이를 인식하는 모델을 만들어야 한다.\ntorch.nn 모듈 조립해서 CNN 만들기 이미지 인식이라는 주제로 찾아보면 어김없이 CNN이라는 단어가 등장한다. CNN(Convolutional Neural Network)은 이미지에 2d convolution 필터를 씌워 처리하는 네트워크로 크게 아래와 같은 구조를 가진다.\n이전 글에서 모델 클래스를 만드는 법을 알아봤으니 convolution, sampling, activation에 필요한 설정을 생각해보자.\n2D-convolution convolution filter 하나당 필터 크기만큼의 2차원 텐서, 그 외 기타 설정값(ex: stride 등) 필요 \\(C\\_{in}\\)개 입력, \\(C\\_{out}\\)개 출력이면 \\(C\\_{in} \\times C\\_{out}\\) 개 필터를 관리해야 함 Activation 어떤 activation인지에 따라 다르지만 ReLU인 경우 일단은 max 함수로 구현 가능 Sampling 샘플링 함수 작성. 제일 간단한 max pooling의 경우 매 MxM 사각형에서 최대값 1개를 추출 \u0026hellip;.불가능한 건 아니지만 상당히 번거롭다. 어차피 알고리즘은 동일하고 세부 파라미터만 수정하면 되는데 누가 해놓지 않았을까? 왠지 노양심같지만 파이썬을 하다보면 이런 마인드가 자연스럽게 탑재된다\npytorch.nn에서 미리 구현된 모듈들을 가져올 수 있다. 간단히 2d-Convolution → activation → subsampling으로 이어지는 layer을 2개 만들고, 마지막에 Fully Connected layer를 덧붙였다.\nclass MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.layer1_conv = nn.Conv2d(1, 6, 5, 1) # 입력 1개, 출력 6개, 필터 크기는 5x5, 1칸 단위로 이동하면서 필터를 씌운다 self.layer1_relu = nn.ReLU() # 활성화 함수. ReLU(x) 는 max(x, 0)과 같다 self.layer1_pool = nn.MaxPool2d(2) # 각 2x2 칸마다 최대값 하나씩만을 남긴다 self.layer2_conv = nn.Conv2d(6, 16, 5, 1) self.layer2_relu = nn.ReLU() self.layer2_pool = nn.MaxPool2d(2) self.fc = nn.Linear(16*4*4, 10) def forward(self, x): x1 = self.layer1_conv(x) # 1x28x28 형식의 데이터가 6x24x24 형식으로 변환된다 x2 = self.layer1_relu(x1) # x3 = self.layer1_pool(x2) # 6x24x24 형식의 데이터가 6x12x12 형식으로 변환된다 x4 = self.layer2_conv(x3) # 6x12x12 형식의 데이터가 16x8x8 형식으로 변환된다 x5 = self.layer2_relu(x4) # x6 = self.layer2_pool(x5) # 16x8x8 형식의 데이터가 16x4x4 형식으로 변환된다 x7 = x6.view(-1, 256) # 16x4x4 형식의 데이터가 256-벡터로 변환된다 x8 = self.fc(x7) # 256-벡터가 10-벡터로 변환된다 return x8 생각보다 꽤 단순하다. 그럼 동작하는지 확인해보자.\nnet = MyModel() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters()) # TypeError: conv2d(): argument \u0026#39;input\u0026#39; (position 1) must be Tensor, not Image net(train_data[0][0]) 에러에서 볼 수 있듯이 PIL 이미지를 먼저 텐서로 변환해줄 필요가 있다. torchvision.transforms 에 있는functional.to_tensor() 함수를 통해 PIL이미지를 텐서로 변환하자. 혹은 앞서 데이터를 읽어들이는 시점에 다음과 같이 파라미터를 설정함으로써 PIL 이미지 대신 변환된 텐서를 가져올 수 있다.\n# 이제 _data의 각 요소는 (PIL Image, Tensor) 대신 (Tensor, Tensor) 가 된다. train_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=True, transform=transforms.ToTensor()) test_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=False, transform=transforms.ToTensor()) 그런데 입력 데이터를 텐서 형식으로 넘겨줘도 또다른 에러가 발생한다. 이는 pytorch 모델이 기본적으로 단일 데이터가 아닌 데이터 batch에 대해 동작하도록 설계되었기 때문인데, 이를 위해 데이터에 축(axis)을 하나 추가해주자.\n# * transform=transforms.ToTensor()로 이미 변환된 데이터라고 가정 # 모델에 1x28x28 입력이 아닌 (batch_size)x1x28x28 입력을 전해줘야 한다. x_input = train_data[0][0] # x_input은 1x28x28 크기의 텐서 net(x_input[None,:]) # [None,:] 으로 축을 추가해 텐서 크기를 1x1x28x28로 만든다 이제 다음과 같은 출력을 확인할 수 있다.\ntensor([[ 0.0534, 0.0028, -0.0284, -0.0451, -0.0616, 0.0883, 0.0050, -0.0106, 0.0624, 0.0225]], grad_fn=\u0026lt;ThAddmmBackward\u0026gt;) 출력 텐서가 크기 10인 1차원 텐서가 아니라 1x10 크기의 텐서임에 유의하자. (batch_size) x 10 크기의 텐서가 결과로 출력되는 것이다.\n아직은 학습이 전혀 진행되지 않았으니 이는 무의미한 값들에 불과하다. 학습이 끝나면 이 무의미한 값이 어떤 의미를 가져야 할까? 머신러닝 관련 키워드에 관심이 많다면 원 핫 인코딩(One Hot Encoding)이 떠오를 것이다. 핵심만 말하면 10-벡터에서 정답에 해당하는 인덱스에 1이, 나머지에는 0이 들어가도록, 혹은 기준을 조금 완화해 argmax(prediction_tensor)가 정답 라벨과 일치하도록 만드는 것이다.\n말은 쉽지만 세부 사항을 정하기 위해서는 1)라벨을 one-hot 형식으로 바꾸고, 2)결과 벡터를 정규화하고, 3)두 벡터의 \u0026lsquo;차이\u0026rsquo;를 어떻게 정의할 것인지 결정하는 등 많은 과정이 필요하다. 물론 이 역시 이젠 새삼스럽지도 않게 다른 전처리 과정과 같이 pytorch에 구현되어 있다. 자세한 원리를 알고싶다면 공식 문서를 참조하자.\ncriterion = nn.CrossEntropyLoss() optimizer는 이전의 SGD 대신 Adam을 사용한다. 좀더 세련된(\u0026hellip;) 방식의 파라미터 업데이트를 수행한다.\n학습 진행하기 이제 학습을 진행해보자\ntrain_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=True, transform=transforms.ToTensor()) test_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=False, transform=transforms.ToTensor()) class MyModel(nn.Module): \u0026#34;\u0026#34;\u0026#34;MyModel 클래스 정의는 위와 동일\u0026#34;\u0026#34;\u0026#34; pass net = MyModel() criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters()) # training for 1 epoch st = time.time() print(f\u0026#34;training with {len(train_data)} data... \u0026#34;, end=\u0026#39;\u0026#39;) for epoch in range(1): for x,y in train_data: y_pred = net(x[None,:]) # batch를 지정하지 않았으므로 이전과 같이 [None,:]으로 변환 y = y[None] # 라벨(y)도 마찬가지로 크기 1인 batch로 변환한다 loss = criterion(y_pred, y) optimizer.zero_grad() loss.backward() optimizer.step() print(f\u0026#34;done (elapsed {time.time() - st}s)\u0026#34;) # test accuracy with torch.no_grad(): # 테스트 시에는 gradient를 계산할 필요가 없다 acc, tot = 0, 0 # 맞은 개수, 전체 개수 for x, y in test_data: y_pred = net(x[None,:]).argmax() # argmax를 이용해 가장 유력한 후보를 답으로 제시 tot += 1 acc += int(y_pred==y) print(f\u0026#34;test accuracy : {acc}/{tot} ({100*acc/tot}%)\u0026#34;) 위 코드의 실행결과는 다음과 같다.\ntraining with 60000 data... done (elapsed 70.17357993125916s) test accuracy: 9766/10000 (97.66%) 1 epoch만 돌린 것 치고 생각보다 성능이 좋게 나온다(?) 실제로 MNIST는 깔끔한 데이터 특성으로 인해 모델에 따라 99% 선까지 정확도를 높일 수 있지만 일단은 여기서 만족하자.\ndataloader 사용하기 앞서 언급한 바와 같이 pytorch 모델은 단일 데이터가 아닌 데이터 batch에 대해 동작하도록 설계되었다. 이전 예시에서는 단일 데이터를 x[None,:]을 통해 강제로 크기 1인 batch로 바꿔줬지만 이번에는 제대로 batch 데이터를 다뤄보자.\ndata batch는 용어 그대로 데이터들을 묶어 세트로 만든 것이다. 예로 [(x1, y1), (x2,y2), (x3,y3), \u0026hellip; ] 로 저장된 데이터를 batch_size=5로 정리하면 [ [(x1,y1),\u0026hellip;,(x5,y5)], [(x6,y6),\u0026hellip;,(x10,y10)], [(x11,y11),\u0026hellip;,(x15,y15)], \u0026hellip; ] 와 같은 꼴이 된다. 이쯤 되면 당연하게도 pytorch에서는 이 일련의 작업을 torch.utils.data.DataLoader를 통해 간단히 수행할 수 있다.\ntrain_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=True, transform=transforms.ToTensor()) test_data = datasets.MNIST(root=\u0026#39;./data\u0026#39;, train=False, transform=transforms.ToTensor()) train_loader = torch.utils.data.DataLoader(train_data, shuffle=True, batch_size=50) test_loader = torch.utils.data.DataLoader(test_data) 앞서 진행한 학습 과정은 dataloader를 이용해 아래와 같이 다시 작성할 수 있다.\nst = time.time() print(f\u0026#34;training with {len(train_data)} data... \u0026#34;, end=\u0026#39;\u0026#39;) for epoch in range(1): for x,y in train_loader: y_pred = net(x) # 이젠 batch data이므로 입력에 특별한 처리가 필요하지 않다 loss = criterion(y_pred, y) optimizer.zero_grad() loss.backward() optimizer.step() print(f\u0026#34;done (elapsed {time.time() - st}s)\u0026#34;) # test accuracy with torch.no_grad(): acc, tot = 0, 0 for x, y in test_loader: y_pred = net(x) # 이전과 달리 batch_size가 1이 아니므로 argmax(dim=1) 설정 필요 # 자세한 설명은 https://pytorch.org/docs/stable/torch.html#torch.argmax 참조 acc += (y==y_pred.argmax(1)).sum() tot += len(y) print(f\u0026#34;test accuracy : {acc}/{tot} ({100*acc/tot}%)\u0026#34;) 위 코드의 실행결과는 다음과 같다.\ntraining with 60000 data... done (elapsed 18.177398204803467s) test accuracy : 9725/10000 (97.25%) 정확도 면에서는 큰 차이가 없지만 학습 시간이 급격하게(70초 → 18초) 줄어든 것을 확인할 수 있다.\nMNIST 데이터 특성상 이번에는 전체 training 데이터를사용한 1 epoch 학습으로도 상당한 정확도를 얻을 수 있었지만, 일반적으로 training 데이터를 전부 학습에 사용하면 과적합(overfitting) 문제가 발생하기 쉽다. 이를 피하기 위해 데이터를 training set / test set의 2종류 대신 training / validating set / test set의 3종류로 분리하는 방법이 잘 알려져 있으며, 이밖에 dropout layer나 data segmentation 등 다양한 기법이 존재한다.\n","date":"2019년 8월 25일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-03/","section":"Posts","summary":"예제 dataset 가져오기 지금까지는 \\(y=ax+b\\)나 \\(y=ax^2+bx+c\\) 처럼 간단한 형태의 숫자→숫자 함수만을 모델링했으니 이제 좀더 흥미로운 데이터를 살펴보자.\n딥러닝, 특히 이미지 인식 관련 예제를 보면 MNIST나 CIFAR-10같은 이름이 많이 등장한다. 이 둘은 Image Classification 문제의 대표적인 예시로 각각 손글씨와 물체 사진 이미지셋에 해당하며, 흔히 모델의 벤치마크 용도로 활용되고 있다. 이번 글에서는 좀더 단순한 MNIST를 사용하기로 한다. MNIST 데이터는 아래와 같이 색이 없는 1채널(=gray scale) 28x28픽셀 이미지들의 모음으로 0에서 9까지 총 10개의 라벨을 가지고 있다.\n","title":"[PyTorch] 03. torch.nn 모듈 조립으로 CNN 만들기","type":"posts"},{"content":" 학습의 각 단계를 추상화하기 앞서 그래디언트를 이용해 간단한 학습이 이루어지는 과정을 알아보았다. 이전 글의 마지막 코드를 다시 살펴보면 학습의 한 round를 크게 다음과 같은 단계들로 추상화시킬 수 있다.\nfor i in range(num_adjust): # 1) 파라미터를 이용해 입력(xs)으로부터 예상 결과를 생성 y_pred = a*xs + b # 2) 예상 결과(y_pred)와 실제 결과(ys)를 비교해 오차를 계산 err = ((y_pred - ys)**2).mean() # 3) .backward()를 통해 각 파라미터가 오차에 관여하는 정도를 역산 err.backward() # 4) 역산된 grad를 바탕으로 파라미터 수정 a.data.sub_(0.01 * a.grad) b.data.sub_(0.01 * b.grad) # 5) 파라미터들의 grad값을 0으로 초기화 a.grad.zero_() b.grad.zero_() 그런데 이번과 같이 모델이 \\(y=ax+b\\) 처럼 간단한 형태인 경우 파라미터(a, b)들을 각자 관리할 수 있지만, 복잡한 모델의 경우 이는 상당히 번거로운 과정이 될 수 있다. 흩어진 파라미터들을 통합해 관리한다면 어떤 형태가 가능할지 생각해 보자.\nparams = \u0026#34;통합된 파라미터들을 저장할 모음. list,dict,class,... 중 뭐가 좋을까?\u0026#34; def init_params(params): params = \u0026#34;어떤 파라미터를 사용할 것인지 정의하고, 각 파라미터에 초기값 설정\u0026#34; pass def predict(x, params): y_pred = \u0026#34;입력(=x)과 params으로부터 예측값 계산\u0026#34; return y_pred def calculate_err(y_pred, y): err = \u0026#34;예측값(y_pred)과 실제값(y)간의 차이를 계산\u0026#34; return err def update_params(params): \u0026#34;grad를 바탕으로 params를 조절한다\u0026#34; pass def clear_grad(params): \u0026#34;params의 grad 값들을 전부 0으로 초기화한다\u0026#34; pass # 추상화된 학습과정 init_params() for i in range(num_adjust): # 1) 파라미터를 이용해 입력(xs)으로부터 예상 결과를 생성 y_pred = predict(xs, params) # 2) 예상 결과(y_pred)와 실제 결과(ys)를 비교해 오차를 계산 err = calculate_err(y_pres, ys) # 3) .backward()를 통해 각 파라미터가 오차에 관여하는 정도를 역산 err.backward() # 4) 역산된 grad를 바탕으로 파라미터 수정 update_params(params) # 5) 파라미터들의 grad값을 0으로 초기화 clear_grad(params) 대부분의 경우 학습과정은 이 형태를 크게 벗어나지 않을 것이다. 그런데 여기서 일부 함수들이 순수 함수(pure function)가 아닌, 내부 상태를 변화시키는 \u0026lsquo;동작\u0026rsquo; 을 하는 프로시저(procedure)라는 사실이 눈에 들어온다. 일관성 있는 관리를 위해 각 단계들을 다시 역할에 따라 모아 클래스로 만들 수 있다.\n예측값 계산 담당 init_params(), predict() 오차 계산 담당 calculate_err() 파라미터 조정 담당 update_params(), clear_grad() pytorch 모델은 기본적으로 이러한 분류를 따라 설계된다. 각각은 흔히 model(혹은 module), criterion, optimizer라는 이름으로 불리며, 이들 용어는 특별한 언급이 없는 한 이 블로그에서도 동일한 의미로 약속하자.\nModel 클래스 만들기 pytorch 모델 클래스를 위한 기본 틀은 다음과 같다. 선형 함수는 너무 간단하니 3개의 파라미터(a, b, c)를 설정해 2차함수 근사를 위한 모델을 만들었다.\nimport torch import torch.nn as nn class MyModel(nn.Module): # nn.Module 클래스의 확장이 된다 def __init__(self): # 부모 생성자 호출 super(MyModel, self).__init__() # 파라미터들의 타입이 torch.tensor가 아니라 nn.Parameter임에 유의 self.a = nn.Parameter(torch.tensor([1.])) self.b = nn.Parameter(torch.tensor([2.])) self.c = nn.Parameter(torch.tensor([3.])) def forward(self, x): y = self.a * (x**2) y += self.b * (x**1) y += self.c return y 이렇게 정의된 모델 클래스는 다음과 같이 사용할 수 있다.\n# fitting 대상 데이터 xs = [ 1.00, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00, 9.00 ] ys = [ 9.15, 5.64, -0.21, -2.36, -0.91, -0.67, 2.65, 10.36, 17.13 ] # 모델 객체 생성 model = MyModel() # model.parameters()로 모델의 파라미터에 접근할 수 있다 # 파라미터들의 이름을 같이 보고싶다면 model.named_parameters()를 사용하자 for param in model.parameters(): print(param) y_pred = model(xs) # model.forward(xs)와 동일 처음 예측을 진행해보면 y_pred 는 (당연하게도) 전혀 들어맞지 않는 결과가 나온다.\n이제 오차를 계산하고 파라미터를 조정해야 한다.\n오차 계산하기 이전까지는 오차를 Mean Square Error(MSE)로 정의해 이를 코드상에서 계산했다. 그런데 꼭 이것만이 유일한 답일까? 오차를 정의하는 다른 방법들은 없을까?\n잘 알려진 MSE 외에도 Cross Entropy나 KL-divergence와 같은 다양한 방법들이 존재하며, 이들 대부분은 pytorch내부에 이미 구현되어 있다. 이번에는 torch.nn.MSELoss에 정의된 MSE를 그대로 사용하자. 설정할 수 있는 옵션들이 궁금한 사람을 위해 공식 문서가 준비되어 있다.\ncriterion = nn.MSELoss() # 처음 한 번만 정의 ---------- loss = criterion(y_pred, ys) # 매 라운드(epoch)마다. *앞으로는 err 대신 loss라는 용어를 사용한다 loss.backward() 파라미터 조정하기 loss.backward()까지 진행하면 각 파라미터들마다 계산된 .grad를 얻는다. 이제 이를 바탕으로 파라미터를 조정하자. 이전에는 아래와 같은 방식을 이용했다.\nself.a -= 0.01 * self.a.grad self.b -= 0.01 * self.b.grad self.c -= 0.01 * self.c.grad ... 요약하면 모든 파라미터에 param -= (small_ratio) * param.grad 를 적용하는 것이라고 할 수 있다. 이런 방식을 Stochastic Gradient Descent(SGD)라고 하며, 이밖에도 Adam, ASGD, Rprop 등 다양한 업데이트 방식이 존재한다. 당연하게도 이런 업데이트 알고리즘 역시 pytorch의 torch.optim 내에 구현되어 있다. 여기서는 SGD를 그대로 사용하자. 업데이트 시 변화량에 곱해지는 \u0026lsquo;작은 수\u0026rsquo;를 lr(learning ratio)이라고 지칭한다.\nimport torch.optim as optim optimzer = optim.SGD(model.parameters(), lr=1e-4) # 정의 시점에 model.parameters()를 넘겨준다 ---------- loss.backward() optim.step() # 파라미터 업데이트 optim.zero_grad() # 파라미터들의 grad를 0으로 초기화 All-in-One 이제 전체 코드를 종합해보자.\n# Model 클래스 구조 class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() self.a = nn.Parameter(torch.tensor([1.])) self.b = nn.Parameter(torch.tensor([2.])) self.c = nn.Parameter(torch.tensor([3.])) def forward(self, x): y = self.a * (x**2) y += self.b * (x**1) y += self.c return y # fitting 대상 데이터 xs = torch.tensor([ 1.00, 2.00, 3.00, 4.00, 5.00, 6.00, 7.00, 8.00, 9.00 ]) ys = torch.tensor([ 9.15, 5.64, -0.21, -2.36, -0.91, -0.67, 2.65, 10.36, 17.13 ]) # model과 criterion, optimizer 선언 model = MyModel() criterion = nn.MSELoss() optimizer = optim.SGD(model.parameters(), lr=1e-4) # 학습 for epoch in range(100000): y_pred = model(xs) loss = criterion(y_pred, ys) optimizer.zero_grad() # .zero_grad()의 위치는 이곳과 .step() 이후 중 어느 곳이든 상관 없음 loss.backward() # 인터넷의 다른 예제들과의 통일성을 위해 loss.backward() 이전에 놓았다 optimizer.step() if(epoch % 10000): print(f\u0026#34;Loss after {epoch} steps: {loss}\u0026#34;) 학습 epoch이 증가함에 따라 점차 fitting하고자 하는 데이터에 가까워지는 것을 확인할 수 있다.\n","date":"2019년 8월 20일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-02/","section":"Posts","summary":"학습의 각 단계를 추상화하기 앞서 그래디언트를 이용해 간단한 학습이 이루어지는 과정을 알아보았다. 이전 글의 마지막 코드를 다시 살펴보면 학습의 한 round를 크게 다음과 같은 단계들로 추상화시킬 수 있다.\nfor i in range(num_adjust): # 1) 파라미터를 이용해 입력(xs)으로부터 예상 결과를 생성 y_pred = a*xs + b # 2) 예상 결과(y_pred)와 실제 결과(ys)를 비교해 오차를 계산 err = ((y_pred - ys)**2).mean() # 3) .backward()를 통해 각 파라미터가 오차에 관여하는 정도를 역산 err.backward() # 4) 역산된 grad를 바탕으로 파라미터 수정 a.data.sub_(0.01 * a.grad) b.data.sub_(0.01 * b.grad) # 5) 파라미터들의 grad값을 0으로 초기화 a.grad.zero_() b.grad.zero_() 그런데 이번과 같이 모델이 \\(y=ax+b\\) 처럼 간단한 형태인 경우 파라미터(a, b)들을 각자 관리할 수 있지만, 복잡한 모델의 경우 이는 상당히 번거로운 과정이 될 수 있다. 흩어진 파라미터들을 통합해 관리한다면 어떤 형태가 가능할지 생각해 보자.\n","title":"[PyTorch] 02. nn.Module로 모델 클래스 만들기","type":"posts"},{"content":"요새는 뭘 찾아봐도 딥러닝이 나온다..\n성향이 continuous, gradient 같은 거랑은 안 맞아서 최대한 피해보려고 했지만 결국 시대의 흐름에 거스르지 못하고 어떻게 사용하는지 정도는 알아두는게 좋을 것 같아서 정리해봤다.\n요구사항(prerequisite) python에 대한 기초지식 (ex: ABC.xyz()는 클래스/객체 ABC 안에 있는 xyz 라는 함수를 호출하는 것이다) 함수에 대한 개념(규칙에 따라 input → output으로 이어지는 블랙박스로 이해해도 된다) 머릿속 이미지화를 위한 약간의 상상력(?) tensor 클래스 tensor는 \\(n\\_1 \\times n\\_2 \\times \\cdots \\times n\\_d \\) 개의 숫자를 \\(d\\)차원으로 정렬한 객체이다. 편의상 예시는 2차원 이하만을 다룰 예정이지만 임의의 차원을 가질 수 있다는 점은 알아두자.\n생성 x = torch.tensor([[1,2,3],[4,5,6]]) # 1~6으로 채워진 2차원 텐서(크기 2x3)를 생성 y = torch.rand((3, 4)) # 0~1 사이의 임의의 수로 채워진 2차원 텐서(크기 3x4)를 생성 z = torch.zeros((1,2,3)) # 0으로 채워진 3차원 텐서(크기 1x2x3)를 생성. 1로 채우는 ones()도 있다 x.shape # 텐서 x의 크기. 위 예시에서는 torch.Size([2, 3]) 연산 numpy에서와 비슷하게 다양한 연산을 지원한다. 왠지 있을법한건 대충 다 있다고 보면 된다.\nz = x.mean() # x의 모든 원소들의 평균 z = x.sum() # x의 모든 원소들의 합 z = x + y # x,y의 각 원소들에 대한 element-wise 덧셈. + 대신 *를 쓰면 곱셈 연산이 된다 # 예외적으로 y가 스칼라이거나 1x1 크기인 경우 x의 모든 원소에 대해 연산이 이루어진다 z = x @ y # x,y의 행렬곱. x,y가 크기 제약조건(N x M 행렬과 M x R 행렬)을 만족해야 한다 # 예외적으로 y가 1차원인 경우 상황에 맞는 행벡터or열벡터인 것처럼 처리된다 함수명이 _로 끝나는 경우는 in-place 연산을 의미한다.\nx.add_(3) # x에 3을 더한다 x.sub_(4) # x에 4를 뺀다 x.zero_() # x를 0으로 만든다 tensor와 autograd 딥러닝은 결국 gradient descent 과정이다. 이를 이해하기 위해 gradient란 무엇이고, 어떻게 구할 수 있는지 알아보자.\n연산 그래프(Computational Graph) 먼저 텐서들의 연산에 대한 머리속 구조(Mental Model)를 살짝 바꾸는 작업이 필요하다.\nx = torch.tensor([3]) y = torch.tensor([4]) z = x + y 흔히 이런 코드를 보면 x와 y를 3, 4가 들어있는 일종의 상자로 생각하고, z를 7이 저장된 새로운 상자로 이미지하곤 한다(아래 그림의 왼쪽). int나 float 같은 primitive 타입에서는 이렇게 생각해도 문제가 없지만 tensor라는 \u0026lsquo;객체\u0026rsquo;는 조금 다르게 동작한다.\n왼쪽 그림에서와 달리 오른쪽에서 z는 연산의 결과값(7)뿐만이 아닌, 어떻게 그 값을 얻게 되었는지를 역추적할 수 있는 링크를 볼 수 있다.1 이렇게 얻은 z를 이용해 연산을 진행할수록 그림의 그래프도 마찬가지로 확장해나갈 수 있는데, 이러한 형태의 그래프를 연산 그래프(Computational Graph)라고 한다.\n연산 그래프를 만들기 위해서는 torch.tensor를 정의할 때 requires_grad=True를 설정해야 한다. 대부분의 경우 선언 시점에 requires_grad를 설정하겠지만, 코드 중간에 이를 변경해야 하는 경우 .requires_grad_() 함수에 True/False를 넘김으로써 온/오프가 가능하다.\nx = torch.tensor([3.0], requires_grad=True) # tensor([3.], requires_grad=True) y = torch.tensor([4.0], requires_grad=True) # tensor([4.], requires_grad=True) z = x + y # tensor([7.], grad_fn=\u0026lt;AddBackward\u0026gt;) y.requires_grad_(False) # 이제 y는 연산 그래프에서 gradient를 계산하지 않음 그래디언트 계산 그럼 이제 다음과 같은 질문이 떠오른다.\n\u0026ldquo;required_grad에서 grad는 무엇을 의미하는가\u0026rdquo; \u0026ldquo;이렇게 연산 그래프를 만들어서 무엇을 얻는가\u0026rdquo; 먼저 첫 질문에 답해보자. 그래디언트(gradient, 이하 grad)는 간단히 말해 각 변수가 \u0026lsquo;현재 시점\u0026rsquo;에서 목적함수를 변화시키는 정도라고 할 수 있다. 예로 \\(y = -x^4 + 10x^3 + 10x^2 - 15x - 4\\)의 식에서 \\(y\\)를 목적함수라고 하면 아래와 같은 그래프를 얻는다.\n그래프에서 \\(x=5\\)인 순간에 x의 그래디언트2 \\(\\frac{\\partial y}{\\partial x}\\)는 335이며, 이는 \\(x=5\\) 근처에서 x가 \\(d\\) 만큼 변하면 y는 \\(335d\\) 만큼 변하게 됨을 의미한다. 다변수 함수 \\(y=f(x\\_1,x\\_2)\\)에 대해서도 마찬가지로 \\(\\frac{\\partial y}{\\partial x\\_1}, \\frac{\\partial y}{\\partial x\\_2}\\) 를 구할 수 있다.\n이제 연산 그래프의 진가가 드러난다. 연산 그래프를 통해 최종 결과물(=목적함수)이 어떻게 계산된 것인지 역추적할 수 있으므로, 이를 처음 시작까지 거슬러 올라가면 각 입력 변수들이 목적함수에 얼마나 영향을 끼치는지를 구할 수 있다. 이 모든 과정은 최종 결과물(=y)에서 .backward() 함수 호출을 통해 이루어진다.\nx = torch.tensor([5.0], requires_grad=True) y = -1*(x**4) + 10*(x**3) + 10*(x**2) - 15*x - 4 y.backward() # dy/dx가 계산된다 print(x.grad) # tensor([335.]) 조금 더 복잡한 연산의 경우에도 마찬가지 작업이 가능하다.\nx1 = torch.tensor([1.0], requires_grad=True) x2 = torch.tensor([2.0], requires_grad=True) x3 = torch.tensor([3.0], requires_grad=True) y = x1*x2*x3*x3 + 7*x1*x2 - x2*x2 - 4*x1*x3 y.backward() # y를 계산하는데 연관된 모든 x들에 대해 dy/dx가 계산된다 print(x1.grad) # tensor([20.]) print(x2.grad) # tensor([12.]) print(x3.grad) # tensor([8.]) grad와 머신러닝 grad가 왜 중요할까? .backward() 와 grad를 이용해 선형 회귀 문제를 풀어보자.3\nxs = torch.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0]) # fitting하고자 하는 데이터 ys = torch.tensor([3.0, 6.5, 5.0, 6.0, 8.0, 12.0]) # # y = a*x + b 형태의 선형 함수로 근사한다 a = torch.tensor([0.0], requires_grad=True) # a의 초기값은 0 b = torch.tensor([7.0], requires_grad=True) # b의 초기값은 7 y_pred = a*xs + b # a, b를 이용해 y의 예측값을 계산한다 err = ((ys - y_pred)**2).mean() # 예측한 값의 제곱 평균 오차(Mean Square Error)를 구한다 아무 조작도 하지 않은 초기상태(\\(a=0, b=7\\))에서의 근사는 오른쪽과 같은 모습이다.\n이제 err.backward()를 실행하면 무슨 일이 벌어지는지 살펴보자.\nerr.backward() print(a.grad) # tensor([-6.6667]). a가 d만큼 변하면 err는 -6.6667*d만큼 변한다 print(b.grad) # tensor([0.5000]). b가 d만큼 변하면 err는 0.5000*d만큼 변한다. 우리는 근사의 오차(=err)가 0에 최대한 가깝게 변하기를 원한다. 그런데 err은 제곱수(양수)이므로 0이 되기 위해서는 - 방향으로 움직여야 하고, 따라서 a는 + 방향으로, b는 - 방향으로 조정하는 것이 좋다는 결론을 내릴 수 있다. 그렇다면 a,b 각각을 \u0026lsquo;얼마나 큰 크기로\u0026rsquo; 조정해야 할까? 조정을 위한 기본 원칙을 세워보자.\n(그래디언트는 현재 값 근처에서만 유효하므로) 한번에 너무 많이 움직이면 안 된다 목표 함수에 핵심적인 역할을 하는 변수는 그렇지 않은 변수보다 큰 폭으로 변화시키는게 좋다 이만하면 나름 그럴듯한 기준이다. 이를 고려하면 아래와 같은 간단한 변환을 생각할 수 있다.\na -= 0.01 * a.grad # 기술적인 이유로 실제 코드에서는 a.data.sub_(0.01 * a.grad) 이 된다. b -= 0.01 * b.grad # 기술적인 이유로 실제 코드에서는 b.data.sub_(0.01 * b.grad) 이 된다. 식을 살펴보면 목표 함수(err)가 항상 양수이고, 이를 0에 최대한 가깝게 만드는 것이 목표일 때 위 연산은 항상 올바른 방향으로 변수를 조정한다는 것을 알 수 있다. 이제 이렇게 조정된 a,b를 가지고 다시 앞선 과정을 반복하자. 반복 작업의 편의를 위해 이를 코드로 정리했다.\nfor i in range(num_adjust): y_pred = a*xs + b err = ((y_pred - ys)**2).mean() err.backward() a.data.sub_(0.01 * a.grad) b.data.sub_(0.01 * b.grad) a.grad.zero_() # 조정이 끝나면 다시 grad값을 0으로 초기화한다 b.grad.zero_() # 이렇게 갱신과 별도로 초기화하는 이유는 추후 다른 글을 통해 설명한다 그렇다면 num_adjust가 증가할수록 실제로 err이 줄어들까? 아래 그림은 조정 횟수에 따른 최종 \\(y=ax+b\\) 그래프를 정리한 결과이다. 조정 횟수가 증가함에 따라 점차 근사가 정확해지는(=오차가 줄어드는) 것을 확인할 수 있다.\n모든 머신러닝은 입력 x에 대해 모델의 출력 y_pred가 참값 y와 가까워지도록(=오차 err이 감소하도록) 만드는 과정임을 생각하면, 앞서 수행한 조정은 단순히 선형 회귀가 아닌 좀더 복잡한 모델에도 마찬가지로 적용 가능하다는 사실을 알 수 있다.\n다음 글에서는 pytorch에서 복잡한 모델을 생성하는 방법과, 모델의 여러 파라미터를 좀더 영리하게 조정하는 방법에 대해 알아본다.\n직관적인 그림을 위해 설명을 단순화했다. 실제로는 모든 텐서가 아닌 requires_grad=True로 설정된 텐서들만이 그래프의 유의미한 노드가 된다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n정확한 표현은 \u0026lsquo;x의 편미분값\u0026rsquo; 이다. 수학에서 그래디언트는 단일 변수가 아닌, 관여한 전체 변수들에 대한 편미분들의 모음이다. (ex: 다변수 함수 \\(y=f(x\\_1,x\\_2)\\)에서 \\(\\mathrm{grad}\\,y = ( \\frac{\\partial y}{\\partial x\\_1}, \\frac{\\partial y}{\\partial x\\_2} )\\))\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n사실 1차원 선형 회귀 문제는 굳이 이런 번거로운 방법을 쓰지 않고도 수학적으로 최적의 답을 구할 수 있다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 8월 18일","externalUrl":null,"permalink":"/blog/notes-on-pytorch-01/","section":"Posts","summary":"요새는 뭘 찾아봐도 딥러닝이 나온다..\n성향이 continuous, gradient 같은 거랑은 안 맞아서 최대한 피해보려고 했지만 결국 시대의 흐름에 거스르지 못하고 어떻게 사용하는지 정도는 알아두는게 좋을 것 같아서 정리해봤다.\n요구사항(prerequisite) python에 대한 기초지식 (ex: ABC.xyz()는 클래스/객체 ABC 안에 있는 xyz 라는 함수를 호출하는 것이다) 함수에 대한 개념(규칙에 따라 input → output으로 이어지는 블랙박스로 이해해도 된다) 머릿속 이미지화를 위한 약간의 상상력(?) tensor 클래스 tensor는 \\(n\\_1 \\times n\\_2 \\times \\cdots \\times n\\_d \\) 개의 숫자를 \\(d\\)차원으로 정렬한 객체이다. 편의상 예시는 2차원 이하만을 다룰 예정이지만 임의의 차원을 가질 수 있다는 점은 알아두자.\n","title":"[PyTorch] 01. PyTorch의 기본 구조","type":"posts"},{"content":"TL;DR : 아무래도 페이스북 티셔츠와는 연이 없는 모양이다(\u0026hellip;)\n2018년 해커컵에 이어 2019년도 페이스북 해커컵 대회가 열렸다. 이번에는 지난번같은 실수 없이 티셔츠, 운이 좋다면 Round 3까지 진출하는 걸 목표로 삼았지만 결국 달성하지 못했다. 일요일 새벽 2시에 열리는 Round 2를 위해 토요일 저녁에 미리 잠들고, 알람까지 맞춰놨지만 눈을 뜨니 2시 23분이었고 급하게 노트북을 켰지만 이미 대회 시작에서 30분이 지난 상황이었다.\n비몽사몽한 정신을 붙들고 문제를 풀어봤지만 2문제를 풀고나니 3,4번째 문제에서 아이디어를 구체화시키지 못했고, 결국 549등이라는 아쉬운 등수 - 500등까지 기념 티셔츠를 제공한다 - 로 대회를 끝마칠 수밖에 없었다. 30분 핸디캡이 없었다면 무난히 티셔츠를 받았을거라는 계산과~~(실제로 그렇다)~~ 왜 알람을 못 들었을까라는 자책(?) 등 다양한 생각이 들지만 막상 당시엔 다시 침대에 가 눕겠다는 생각이 주를 차지했던 것 같다..\n각 라운드를 간단히 요약해봤다.\nQualification Round 1문제 이상을 통과하면 Round 1로 진출. (55/100점) 문제 자체는 간단했다. 파이썬 라이브러리를 적절히 사용했다면 입력받는 부분을 제외하고 1,2줄 내로 작성 가능할 정도였지만 해커컵의 특징 - 구글 코드잼은 Small / Large로 나눠 Small의 정답 여부를 바로 알려주고 재시도가 가능하지만, 해커컵은 오직 1번만 제출 가능하고 제출한 후에도 대회 종료까지 정답/오답 여부를 알려주지 않는다 - 으로 인해 안전하게 3개 문제를 코딩해 제출했다. 결과는 3개 모두 정답으로 R1 진출.\nRound 1 30점 이상을 달성하면 Round 2로 진출 (65/100점) 절대평가로 등수와 크게 관련은 없지만 D번 문제에서 저질렀던 실수가 아쉬운 라운드이다. 앞선 A,B,C 3개 문제를 해결하고 이어진 D에서 나름 깔끔한 풀이를 찾았는데, 다음 두 쿼리를 해결해야 했다.\n저장소에 정수 x를 추가 저장소에서 k번째로 큰 수를 탐색 균형이진트리를 직접 구현할 경우 \\(O(\\log N)\\), 좌표압축과 펜윅트리를 조합하면 \\(O(\\log^2 N)\\) 복잡도로 해결 가능한 쿼리들이었지만 코딩이 귀찮아서 좀더 간단히 작성할 수 있는 방법을 찾았다.\n\u0026hellip;there are two new features — it is find_by_order() and order_of_key(). The first returns an iterator to the k-th largest element (counting from zero), the second — the number of items in a set that are strictly smaller than our item.\n(출처: C++ STL: Policy based data structures)\nGNU C++에서 제공하는 Policy-based data structure를 이용하면 Order Statistic Tree 라는 구조를 쉽게 작성할 수 있고, 문제에서 정확히 필요로 하는 연산(find_by_order())을 \\(O(\\log N)\\)에 제공한다.\n예제가 나오는 것을 확인하고 자체 테스트 결과 잘 동작하는 것처럼 보여 그대로 제출했지만, 대회가 끝나자 오답(!) 판정을 확인할 수 있었다. 몇 차례 테스트하면서 일부 데이터에서 실행할 때마다 결과가 바뀌는 현상을 발견했고, 원인을 찾던 중 중복 원소를 고려하지 않았다는 사실을 깨달았다. 구조는 order statistic tree지만 내부적으로 STL set을 사용했기에 중복 원소가 들어가면 하나로 카운트된다는 사실을 생각하지 못해 일어난 오류였다. 다시 이를 고려해 코드를 수정하자 Practice 모드에서 정답 판정을 확인할 수 있었다(\u0026hellip;) 맞았다면 100점으로 꽤 기분좋은 결과였겠지만 어쨌든 Round 2에 진출했으므로 크게 개의치 않았다. 코드잼의 Small / Large 시스템이었으면 이런 오류는 바로 발견할 수 있었을텐데 이런 점에서는 해커컵의 No Feedback 정책이 약간은 아쉽다.\nRound 2 Round 2 참가자들 중 상위 200명이 Round 3에 진출 Round 2 참가자들 중 상위 500명에게는 기념 티셔츠가 제공됨 (549위\u0026hellip;) 일요일(7.14) 새벽 2시부터 5시까지 진행되는 라운드였다. 새벽 2시까지 깨어있는건 크게 어려운 일이 아니지만 그 컨디션을 5시까지 유지하기엔 자신이 없어 아예 저녁에 미리 잠들기로 했다(\u0026hellip;) 그렇게 1시 반에 알람을 맞춰두고 눈을 떴지만 핸드폰엔 2:23이라는 글자가 반쯤 덜 깬 몸을 반겼다..\n그렇게 노트북을 켜고 접속을 마치니 이미 30분이 지난 상황이었고 문제당 시간 핸디캡을 30분씩1 가지고 시작한다는 생각에 마음이 조급해졌다. A, B번 문제를 풀고 500위가 아슬아슬하다는 걸 깨달았지만 비몽사몽+조급함 디버프를 받아서인지 C,D번 문제는 아이디어만 떠오를 뿐 디테일을 구체화시키지 못했다. 그렇게 대회가 끝났고 549등이라는 순위를 확인할 수 있었다. 끝난 마당에 의미없다는 걸 알면서도 아쉬운 마음에 패널티를 역산하니2 제 시간에 일어났더라면 무난히 400위 안쪽을 차지할 점수였기에 티셔츠에 대한 미련을 떨치기 힘들었다.\n사실 실력이 좀더 높았다면 한문제를 더 해결해 시간 패널티가 아닌 총점으로 티셔츠(+Round 3 진출)를 노릴 수 있었겠지만 평소에 PS 연습도 잘 하지 않은 입장에서 거기까지 바라는 건 과욕이라고 생각한다. 학생 때에는 ICPC를 비롯한 각종 대회라는 동기부여가 있었지만 졸업한 지금은 그런 거 없이 순수한 취미생활이기에 이전처럼 많은 시간을 투자하긴 어려울 것 같다. 이젠 대학원이라는 문제도 있으니 어쩌다 흥미로운 문제를 발견하면 풀어보고, 가끔 있는 이런 온라인 대회에 참가하는 정도에 의의를 둘 계획이다.\n동점자의 경우 \u0026ldquo;대회 시작부터 해당 문제에 대한 답안을 제출하기까지 걸린 시간\u0026quot;의 총합으로 순위를 결정한다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n총 2문제를 해결했으므로 패널티인 2:32:14에서 30분x2를 앞당긴 1:32:14로 예상했다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 7월 14일","externalUrl":null,"permalink":"/blog/facebook-hackercup-2019-review/","section":"Posts","summary":"TL;DR : 아무래도 페이스북 티셔츠와는 연이 없는 모양이다(…)\n2018년 해커컵에 이어 2019년도 페이스북 해커컵 대회가 열렸다. 이번에는 지난번같은 실수 없이 티셔츠, 운이 좋다면 Round 3까지 진출하는 걸 목표로 삼았지만 결국 달성하지 못했다. 일요일 새벽 2시에 열리는 Round 2를 위해 토요일 저녁에 미리 잠들고, 알람까지 맞춰놨지만 눈을 뜨니 2시 23분이었고 급하게 노트북을 켰지만 이미 대회 시작에서 30분이 지난 상황이었다.\n","title":"Facebook Hacker Cup 2019 후기","type":"posts"},{"content":"TL;DR : 코드잼 티셔츠 받음\n2019년 코드잼 본 대회를 치렀다. 몇 번 글을 썼던 Kickstart와는 달리 1년에 1번밖에 열리지 않는 대회이고, 작년엔 훈련소에 있느라 참가를 못 해서인지 조금은 긴장된 마음이었다. 대학 시절 내내 코드잼, 해커컵을 통틀어 티셔츠와는 인연이 없었기에 이번에야말로 하나 받아내겠다는 생각을 했다.\n구글 코드잼(Google Code Jam)은 이름에서처럼 구글에서 주최하는 코딩대회로 Qualification Round와 온사이트 World Final을 포함해 총 5단계로 이루어진다. World Final은 전 세계를 통틀어 상위 25명밖에 나갈 수 없어 기대조차 안했고(\u0026hellip;) 그 이전 단계인 Round 3(세계 상위 1000명)까지를 목표로 삼았다.\n결론부터 말하면 목표를 달성하는 데에는 성공했다. Round 3에서 395위로 티셔츠를 받을 수 있었고, World Final을 제외하면 실질적으로 인간계에서 올라갈 수 있는 마지막 라운드까지 올라간 셈이니 결과 자체로는 만족스럽다고 할 수 있다. 하지만 그와 별개로 매 라운드마다 조금씩 아쉬움이 남았는데, 특히 마지막 라운드에서는 쉽게 생각했던 문제에서 예외케이스 하나를 떠올리지 못해 대회 종료까지 수렁에서 빠져나오지 못했다. 대회가 끝나고 풀이를 들어보니 모든 문제들이 전문적인 지식을 요구하는 게 아닌, 대회 중 했던 고민들에서 조금씩 더 나아가는 풀이였기 때문에 이런 아쉬움이 더 크게 느껴졌다.\n어차피 Qualification, Round 1A가 끝난지도 이제 2개월이 넘었고, 대회 사이트의 Archive에서 각 문제들의 풀이를 제공하고 있어 이를 다시 설명하는 건 큰 의미가 없을 것 같다. 여기서는 반성하는 의미에서 각 라운드마다 아쉬웠던 점을 요약해봤다.\nQualification Round 30점 이상의 점수를 얻으면 Round 1로 진출하는 절대평가 방식. (85/100점) C. Cryptopangramss는 간단한 수학 문제였다. 알고리즘 자체는 간단했지만 다루는 수의 범위가 커서(\\( N \\leq 10^{100} \\)) 파이썬을 사용했는데, 파이썬3에서는 /으로 나눗셈을 하면 정수끼리의 연산에서도 실수가 반환된다는 사실을 고려하지 못해 Large 케이스에서 Runtime Error를 받았다. 대회가 끝나고 다시 코드에서 /를 //로 바꿔서 제출하니 정답이라는 판정이 나왔다(\u0026hellip;)\nRound 1A 3개의 라운드(1A, 1B, 1C) 중 하나에서 1500위 이상을 달성하면 Round 2로 진출. (476위 in 1A) 다른 문제는 다 해결했는데 A. Pylons 의 Large 케이스를 해결할 방법이 떠오르지 않았다. \u0026ldquo;내가 모르는 뭔가가 있나보다\u0026quot;라는 생각으로 대회 종료 후 공식 풀이를 읽어보니 다음 문구를 발견할 수 있었다.\nwe can rely on our occasional friend, randomness! (\u0026hellip;) Many problems cannot be approached with random or brute-force solutions, but identifying the problems that can be (in Code Jam or the real world) is a useful skill!\n\u0026hellip;..게임 형식의 문제에서 난수를 통해 확률적으로 해결하는 건 봤어도 공식 대회의 input/output이 명확한 문제에서 랜덤을 풀이로 제시하는 건 약간 놀라웠다. 풀지 못했던 문제의 해설을 보면서 새로운 걸 배울 수 있을거라 생각했기에 조금 실망스러웠다.\nRound 2 Round 2 참가자들 중 상위 1000명이 Round 3에 진출. (724위) 무려 16번이나 제출을 시도한 B. Pottery Lottery 문제이다. 인터렉티브 문제였는데 컴퓨터와 1:1 게임을 해서 90% 이상 승리하면 되는 확률적인 문제였다. 나름 그럴듯한 전략을 짰다고 생각했는데 막상 제출하니 Wrong Answer를 받았다. 이후 대전략은 그대로 두고 계속해서 세부 파라미터를 조금씩 수정하면서 제출했지만 결과는 끝까지 WA였다. 그런데 대회가 끝나고 공개된 풀이를 보니 생각했던 전략과 동일한 방식이었다. 어디에서 잘못됐는지 천천히 코드를 복기하던 중 Min Heap(루트가 최소 원소인 힙)을 써야 하는 자리에 C++의 priority_queue STL을 사용해 Max Heap(루트가 최대 원소인 힙)이 들어간 것을 깨닫고 이를 수정하니 정답 판정을 받았다(\u0026hellip;)\nRound 3 Round 3 참가자들 중 상위 25명이 World Final에 초청됨. (395위) Qualification Round, Round 1A에서의 아쉬움이 \u0026ldquo;다 맞게 풀어놓고 끝에 가서 멍청한 실수를 한 아쉬움\u0026rdquo;이라면 Round 3에서의 아쉬움은 \u0026ldquo;충분히 고민하지 못했던 것에 대한 아쉬움\u0026rdquo;에 더 가깝다. 대회 중 B Small을 해결하고 다른 문제들을 읽어보니 D. Napkin Folding의 Small 케이스는 손쉽게 해결할 수 있어보였다. 그렇지만 자명해 보이는 알고리즘이 계속해서 Runtime Error를 발생시켰고, \u0026ldquo;뭔가 사소한 실수 하나만 고치면 될거같은데\u0026quot;라는 생각에 끝까지 손을 놓지 못했다. 결국 어떤 경우를 잘못 생각했는지 깨달았지만1 이미 대회가 거의 끝나가는 시점이었고, 조급한 마음에서였는지 제대로 수정하지 못했다. 다른 문제들이 어려운 고급 알고리즘보다는 적절한 착안점을 발견하면 그 다음부터는 비교적 손쉽게 풀리는 것들이었기에 아쉬움이 느껴졌다.\n물론 처음에 D가 아닌 다른 문제를 잡았다고 해서 대회 시간 내에 풀어냈을 것이란 보장은 없다. 그런 만큼 \u0026ldquo;만약 그랬다면 더 좋은 결과였을지도 모른다\u0026quot;라는 생각은 굳이 하지 않기로 했다. 다만 같은 문제를 고민하더라도 대회가 끝난 후 아무런 제약 없이 생각해보는 것과 대회 진행중에 긴장된 상태로 집중하는 건 큰 차이가 있고, 후자의 경험은 대회가 끝나면 다시 겪어보기 힘들다는 점이 약간은 미련으로 남았다.\n한 직선 위에 셋 이상의 점이 존재해도 Simple Polygon이라는 조건을 만족시킬 수 있음\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 6월 11일","externalUrl":null,"permalink":"/blog/Google-CodeJam-2019-review/","section":"Posts","summary":"TL;DR : 코드잼 티셔츠 받음\n2019년 코드잼 본 대회를 치렀다. 몇 번 글을 썼던 Kickstart와는 달리 1년에 1번밖에 열리지 않는 대회이고, 작년엔 훈련소에 있느라 참가를 못 해서인지 조금은 긴장된 마음이었다. 대학 시절 내내 코드잼, 해커컵을 통틀어 티셔츠와는 인연이 없었기에 이번에야말로 하나 받아내겠다는 생각을 했다.\n구글 코드잼(Google Code Jam)은 이름에서처럼 구글에서 주최하는 코딩대회로 Qualification Round와 온사이트 World Final을 포함해 총 5단계로 이루어진다. World Final은 전 세계를 통틀어 상위 25명밖에 나갈 수 없어 기대조차 안했고(…) 그 이전 단계인 Round 3(세계 상위 1000명)까지를 목표로 삼았다.\n","title":"Google Code Jam 2019 후기","type":"posts"},{"content":"지난 25일 목요일, 삼성역 인근 서울 파르나스 호텔에서 FireEye Cyber Defense Live 2019 Seoul 컨퍼런스가 열렸다. Cyber Defense Live는 미국의 보안회사 FireEye에서 주최하는 컨퍼런스로 국제 사이버 보안의 최근 동향과 공격/방어에서의 새로운 인사이트 제시, 기업 차원에서의 PR 등 다양한 주제가 포함되어 있었다. 사실 이런 컨퍼런스가 있는 줄도 몰랐는데 직장에서 출장으로 보내줌\n안내판을 따라 들어간 로비에서 파트너 기업들의 홍보부스를 볼 수 있었다.\n발표가 이뤄지는 메인 홀의 모습이다. 약 2000여명 정도가 등록했다고 하던데 명패를 둘러본 결과 대부분이 기업 보안부서에서 온 것으로 보였다.\n오전 세션에서는 현재 사이버 보안의 동향을 분석하고 그에 대한 인사이트를 제시하는 성격의 발표가 이루어졌다.\n이와 함께 KRAFTON, 배달의민족 등이 패널로 참여해 각자가 겪었던 보안위협에 대한 경험을 소개하는 자리가 있었다.\n점심식사 후 이어진 오후 세션은 상대적으로 자사 솔루션 소개에 집중했다는 느낌을 받았다. 특히 SOAR(Security Orchestration and Automated Response)이라는 개념을 강조하면서, 파이어아이 사의 Helix가 무엇을 할 수 있는지에 중점을 둔 발표가 이루어졌다. 발표 휴식시간 동안 둘러본 홍보부스에서는 여러 파트너사에서 자사 제품을 홍보하고 있었다.\n이번 컨퍼런스에서 느낀 점을 하나만 꼽자면 역시 **\u0026ldquo;아는 만큼 보인다\u0026rdquo;**라는 사실이었다. 개인적으로 큰 맥락에서의 이야기인 오전 세션까지는 흥미있게 들었지만, 기업 입장에서의 보안 위협과 그에 대한 솔루션 위주였던 오후 세션에서는 큰 인상을 받지 못했다. 반면 현직에서 오신 다른 분들은 오후 세션에 더 관심을 가지고 듣는 모습이었고, 컨퍼런스 다음날 출근한 연구소에서도 같이 출장가셨던 선임 연구원님들이 SOAR 개념과 그 적용범위에 대해 이야기를 나누시는 걸 볼 수 있었다.\n솔직히 아직 \u0026lsquo;개념 정립\u0026rsquo;이라는 용어를 볼 때마다 막연하다는 생각을 지울 수 없다. 이미 정리가 끝난 이론을 습득하기만 하던 학부 시절에서 못 벗어나서일 수도 있고, 혹은 아직 문제를 바라보는 인사이트가 좁아서일 수도 있다. 사실 전에는 이런 연구들을 뜬구름 잡는 소리로 생각한 적도 있었다. 사이버보안에서는 새로운 공격기법을 분석/탐색하고 이를 통해 구체적인 결과물을 내는 것이 중요하지, 새로운 용어를 만들고 이를 정의하는 것은 실질적으로는 큰 의미가 없다는 생각에서였다. 요새는 그런 생각이 \u0026ldquo;코드 작성만 잘하면 되지 Type Theory같은 뜬구름 잡는 소리는 괜한 시간낭비다\u0026rdquo; 라는 생각과 다를 바 없다는 걸 느끼고 있다. 근시안적인 입장에서야 무의미한 일로 느껴질 수 있어도, 장기적으로 본다면 큰 차원의 도약을 위해 반드시 선행되어야 할 과제일 수 있다.1\n이제 연구소에서 2년, [검열됨]에서 4년이 남았다. 느끼기에 따라 긴 시간일 수도, 혹은 짧은 시간일 수도 있다 솔직히 뒤쪽 4년은 조금 길거같기는 하다. 그동안 뭘 얻어갈 수 있을지는 잘 모르겠지만 적어도 (혼자서 저런 개념을 정립하지는 못하더라도) 저런 발표를 들었을 때 비슷한 수준의 고민을 할 수 있는 정도까지는 성장했으면 좋겠다.\np.s. 부스 돌아다니면서 온갖 판촉물을 수집한 결과(\u0026hellip;)\n이렇게 말하는 지금도 저런 과제가 가지는 중요성을 머리로 \u0026lsquo;추정할\u0026rsquo; 수만 있고 마음으로 체감하지는 못하고 있어 \u0026quot;-일 수 있다\u0026quot; 라는 표현을 썼다.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 4월 28일","externalUrl":null,"permalink":"/blog/FireEye-Cyber-Defense-Live-2019-Seoul-review/","section":"Posts","summary":"지난 25일 목요일, 삼성역 인근 서울 파르나스 호텔에서 FireEye Cyber Defense Live 2019 Seoul 컨퍼런스가 열렸다. Cyber Defense Live는 미국의 보안회사 FireEye에서 주최하는 컨퍼런스로 국제 사이버 보안의 최근 동향과 공격/방어에서의 새로운 인사이트 제시, 기업 차원에서의 PR 등 다양한 주제가 포함되어 있었다. 사실 이런 컨퍼런스가 있는 줄도 몰랐는데 직장에서 출장으로 보내줌\n","title":"FireEye Cyber Defense Live 2019 Seoul 후기","type":"posts"},{"content":"","date":"2019년 4월 28일","externalUrl":null,"permalink":"/tag/%EC%BB%A8%ED%8D%BC%EB%9F%B0%EC%8A%A4/","section":"Tags","summary":"","title":"컨퍼런스","type":"tags"},{"content":"지난 Round A에 이어 리뉴얼된 킥스타트에서 치르는 두번째 대회였다. 다행히 지난번과 같은 서버 문제는 없었지만 생각처럼 문제가 잘 풀리지는 않았다. A번을 제출하고 나니 딱히 풀이가 떠오르는 문제가 없어 결국 B, C 모두 Small까지만 통과할 수 있었다. 결과는 최종 115등으로 전체 문제는 여기에서 볼 수 있다.\nProblem A. Building Palindromes 문자열이 주어지면 주어진 문자열의 l\u0026hellip;r 구간에 포함된 글자들로 팰린드롬을 만들 수 있는지 판단해야 한다. 구간 [l, r]에 속한 글자들만으로 팰린드롬을 만들기 위해서는 홀수 번 등장하는 알파벳이 최대 하나여야 한다는 사실에 유의하면, 각 알파벳 등장 횟수의 prefix sum을 이용해 각 쿼리를 \\(O(26)\\)에 처리할 수 있다. 첫 제출에서 초기화에 쓴 fill 함수의 범위를 잘못 잡았다는 걸 깨달아 대회 도중 다시 제출했다. [[source](https://github.com/nyan101/algorithm_snippet/blob/master/CodeJam/Kickstart 2019 Round B/A.cpp)]\nProblem B. Energy Stones Small에서는 모든 \\(S\\_i\\)가 동일하다 라는 조건이 있으므로 i번째 스톤을 먹기 시작하는 시점은 이전까지의 선택과 무관하게 \\(S\\_1 \\* (i-1)\\)로 고정된다. 따라서 \u0026ldquo;스톤 i를 j번째로 먹을 때\u0026rdquo; 얻을 수 있는 에너지를 사전에 계산할 수 있고, 이를 가중치로 하는 이분그래프(스톤—순서)를 그릴 수 있다. 이제 만들어진 그래프에 대해 최대 가중치를 가지는 maximum matching1을 구하면 Small을 통과할 수 있다. 최대 가중치 이분매칭은 예전 팀노트에 있던 MCMF 코드를 약간 수정했다. [[source](https://github.com/nyan101/algorithm_snippet/blob/master/CodeJam/Kickstart 2019 Round B/B.cpp)]\n정해는 먼저 각 스톤을 \\(S\\_i / L\\_i\\)를 기준으로 정렬한 후 Knapsack을 수행하는 것이다. 대회 도중 \\(S\\_i / L\\_i\\)까지는 떠올렸지만 \u0026ldquo;The stone\u0026rsquo;s energy stops decreasing once it hits zero.\u0026rdquo; 라는 조건 때문에 그 이상 발전시키지 못했는데, 이를 Knapsack으로 해결할 수 있다고 한다.\nProblem C. Diverse Subarray 먼저 왼쪽 지점(l)을 고정시킨 [l, r] 구간을 생각하자. 오른쪽 지점(r)을 한칸씩 이동시키면서 만나는 타입들의 개수 cnt[]를 관리하면, 각각의 r에 대해 [l,r] 구간을 선택했을 때 얻게 되는 trinket의 수 get[r]을 구할 수 있다. 고정된 l에 대해 get[]을 구하는 과정이 \\(O(N)\\)이므로, 이를 각 l에 대해 수행하면 전체 \\(O(N^2)\\)의 복잡도로 문제를 해결할 수 있다.\nLarge를 해결하기 위한 핵심은 고정된 l에 대해 get[]을 구했으면 다음 l\u0026rsquo;=l+1에 대해서는 (처음부터 다시 계산하는 대신) 그 변화값만을 반영시킨다 라는 아이디어로, 세그먼트 트리에 Lazy Propagation을 더해 해결할 수 있다. 대회 중에는 Small까지만 해결했고, 이후 공개된 풀이를 보고 다시 코드를 작성해봤다.[[source](https://github.com/nyan101/algorithm_snippet/blob/master/CodeJam/Kickstart 2019 Round B/C.cpp)]\n세그먼트 트리에서의 Lazy Propagation은 이론적으로는 알아도 막상 문제를 보면 눈치채지 못하는, 다시 말해 \u0026ldquo;구현은 할 줄 알지만 익숙하지는 않은\u0026rdquo; 부류에 속해있었는데 이번을 계기로 좀더 익숙해질 수 있었으면 좋겠다. 그와 별개로 킥스타트가 리뉴얼되고 이전같은 성과가 잘 나오지 않고 있는데, 이게 졸업하고 PS연습을 덜 하게 되어서인지 아니면 다른 이유가 있는 건지는 아직 잘 모르겠다..\n그래프 구조상 항상 전체 N쌍이 매칭되는 것이 보장된다\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 4월 21일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2019-RoundB-review/","section":"Posts","summary":"지난 Round A에 이어 리뉴얼된 킥스타트에서 치르는 두번째 대회였다. 다행히 지난번과 같은 서버 문제는 없었지만 생각처럼 문제가 잘 풀리지는 않았다. A번을 제출하고 나니 딱히 풀이가 떠오르는 문제가 없어 결국 B, C 모두 Small까지만 통과할 수 있었다. 결과는 최종 115등으로 전체 문제는 여기에서 볼 수 있다.\nProblem A. Building Palindromes 문자열이 주어지면 주어진 문자열의 l…r 구간에 포함된 글자들로 팰린드롬을 만들 수 있는지 판단해야 한다. 구간 [l, r]에 속한 글자들만으로 팰린드롬을 만들기 위해서는 홀수 번 등장하는 알파벳이 최대 하나여야 한다는 사실에 유의하면, 각 알파벳 등장 횟수의 prefix sum을 이용해 각 쿼리를 \\(O(26)\\)에 처리할 수 있다. 첫 제출에서 초기화에 쓴 fill 함수의 범위를 잘못 잡았다는 걸 깨달아 대회 도중 다시 제출했다. [[source](https://github.com/nyan101/algorithm_snippet/blob/master/CodeJam/Kickstart 2019 Round B/A.cpp)]\n","title":"Code Jam Kickstart 2019 Round B 후기","type":"posts"},{"content":"이런저런 생각들\n","date":"2019년 4월 19일","externalUrl":null,"permalink":"/category/%EC%9E%A1%EB%8B%B4/","section":"Categories","summary":"이런저런 생각들\n","title":"잡담","type":"categories"},{"content":"","date":"2019년 4월 19일","externalUrl":null,"permalink":"/tag/%EC%9E%A1%EB%8B%B4/","section":"Tags","summary":"","title":"잡담","type":"tags"},{"content":"* 개인 페이스북 타임라인에 썼던 내용을 다듬어 업로드한 글입니다.\n알고리즘(PS)을 공부하는 법 인터넷을 보면 종종 \u0026ldquo;알고리즘 잘 하는 법(or알고리즘 문제 잘 푸는 법)\u0026ldquo;에 대한 질문글이 올라오고, 많은 경우 \u0026ldquo;모르겠다고 답지를 보지 말고 혼자서 끝까지 고민해봐야 한다\u0026quot;라는 답변이 주를 이룬다.\n개인적으로 이 말에 동의하지 않는다. 연구를 진행하는 거라면 몰라도 문제풀이에 있어서는 모르면 풀이를 봐야 한다고 생각한다. 사람마다 \u0026ldquo;풀이를 본다\u0026quot;라는 말의 두 가지 해석이 있을 수 있다.\n말로 된 알고리즘 풀이를 보고, 스스로의 힘으로 구현해본다 (1)\n고수가 짠 정답 소스코드(a.k.a. \u0026lsquo;답지\u0026rsquo;)를 본다 (2)\n(1)에 대해서도 좋지 않게 보는 사람들이 있는데, 이에 대해서는 이미 많은 실력자 분들께서 좋은 글들을 작성한 바 있다. 한번쯤 읽어보면 좋은 글들을 아래에 첨부했다. 어째 나열하고 보니 사실상 PS계 이름쌓기\nbactree님의 \u0026ldquo;알고리즘 공부, 어떻게 해야하나요?\u0026rdquo; koosaga님의 \u0026ldquo;내가 문제풀이를 연습하는 방법\u0026rdquo; subinium님의 \u0026ldquo;PS를 공부하는 방법 (How to study Problem Solving?)\u0026rdquo; wookje님의 \u0026quot;[잡담] 내가 알고리즘을 공부하는 방법\u0026rdquo; 코딩에서 \u0026lsquo;답지\u0026rsquo;를 보는것에 대한 개인적인 생각 여기서는 (2)에 대해 말해보자. 많은 사람들이 (1)의 장점은 받아들이면서도 (2)는 치팅과 다를 바 없다고 여기는 것 같다. 하지만 개인적인 생각을 말하자면, 적어도 초급→중급 단계로 넘어가는 시점에 있어서는 (2)의 중요성을 무시할 수 없다. 학부 수업에서 다루는 기초적인 자료구조(스택, 큐, 힙)를 통해 알아보자.\n아래에 max 힙에서 최대 원소를 삭제하는 pop함수의 두 가지 구현을 작성했다. 이진 힙의 구조나 각 연산의 알고리즘에 대해서는 크게 바뀔 여지가 없다. 가장 끝 원소를 루트(배열의 1번)로 옮기고, 옮겨진 1번 원소가 제자리를 찾아가도록 바꾸면 된다.\n이진 힙의 제약은 \u0026ldquo;부모는 자식보다 커야 한다\u0026rdquo; 하나밖에 없고, 완전이진트리라는 특성상 자식 노드의 인덱스를 구하기도 쉽다. 하지만 막상 구현해보면 고려해야할 요소가 꽤 많다는 것을 알 수 있다. 구체적으로는 idx번 노드를 기준으로 다음과 같이 나뉜다.\n왼쪽 자식이 없는 경우 이 경우 힙의 구조상 오른쪽 자식도 존재할 수 없고, 그대로 루프가 끝난다. 오른쪽 자식이 존재하고, 오른쪽 자식이 왼쪽보다 큰 경우 오른쪽 자식(idx*2+1)과 현재 노드(idx)를 비교해 처리한다. 오른쪽 자식이 존재하지만, 왼쪽 자식이 오른쪽보다 큰 경우 왼쪽 자식(idx*2)과 현재 노드(idx)를 비교해 처리한다. 오른쪽 자식이 없이 왼쪽만 있는 경우 왼쪽 자식(idx*2)과 현재 노드(idx)를 비교해 처리한다. 각 경우를 \u0026lsquo;정직하게\u0026rsquo;(or 우직하게) 나눠서 구현하면 아래 코드가 나온다. heap[] 배열은 힙의 정보를, hsize는 힙의 크기를 담고있는 변수이다.\nvoid pop1() { int idx = 1; heap[1] = heap[hsize--]; while(idx*2 \u0026lt;= hsize) { if(idx*2 + 1 \u0026lt;= hsize) { if(heap[idx*2] \u0026lt; heap[idx*2+1]) { if(heap[idx*2+1] \u0026gt; heap[idx]) { swap(heap[idx*2+1], heap[idx]); idx = idx*2 + 1; } else break; } else { if(heap[idx*2] \u0026gt; heap[idx]) { swap(heap[idx*2], heap[idx]); idx = idx*2; } else break; } } else { if(heap[idx*2] \u0026gt; heap[idx]) { swap(heap[idx*2], heap[idx]); idx = idx*2; } else break; } } } 코드의 동작이라는 측면에서 보면 이 코드에 문제는 없다. 연산 전후에 힙의 성질을 유지하고, 가능한 모든 경우를 빠짐없이 체크했으며, 시간복잡도 면에서도 낭비 없는 \\(O(\\log N)\\)이다.\n이제 아래의 pop2를 보자. pop1과 동일한 알고리즘으로 매 루프에서 정확히 동일한 동작을 수행한다.\nvoid pop2() { int idx = 1; heap[1] = heap[hsize--]; while(idx*2 \u0026lt;= hsize) { int child = idx*2; if(idx*2+1 \u0026lt;= hsize \u0026amp;\u0026amp; heap[idx*2] \u0026lt; heap[idx*2+1]) child = idx*2+1; if(heap[child] \u0026lt; heap[idx]) break; swap(heap[child], heap[idx]); idx = child; } } 이 둘 중 과연 어느 쪽이 \u0026lsquo;좋은\u0026rsquo; 코드일까? 개인적으로는 pop2가 pop1보다 좋은 코드라고 생각한다.\n물론 혼자 많은 코드를 작성해보면서 점차 발전해가는 경우도 존재한다. 하지만 pop1에서 pop2로의 발전은 \\(O(N^2)\\)에서 \\(O(N\\log N)\\) 알고리즘으로 발전하는 과정과는 다르다. \\(O(N\\log N)\\)을 요구하는 문제에서 \\(O(N^2)\\) 알고리즘을 구현했다면 \u0026lsquo;틀린\u0026rsquo; 코드겠지만, 이 경우 양쪽 모두 \u0026lsquo;맞는\u0026rsquo; 코드이고 거의 모든 상황1에서 pop1은 pop2와 동일한 수준의 결과물로 인정받을 것이다. \u0026lsquo;틀린 부분\u0026rsquo;이 없으므로 지적받을 일도 없고, 특별히 관심을 가지지 않는다면 자발적으로 개선될 가능성도 낮다. 실제로 학부에서 첫 프로그래밍을 접한 동기들이 작성한 코드는 대체로 pop1과 비슷했다.\n문법에 어느정도 익숙해졌다면 \u0026ldquo;말로 서술된 지시사항을 하나하나 코드로 옮기는\u0026rdquo; 것은 어려운 일이 아니다. 여기서 더 발전하기 위해서는 많은 알고리즘을 아는 것과 함께 \u0026lsquo;좋은 코드\u0026rsquo;에 대한 감각을 길러야 한다. 그런 의미에서 PS는 실무 개발에 비하면 비교적 짧은 분량에, 동일한 로직을 가지는 소위 \u0026lsquo;모범적인\u0026rsquo; 코드를 접할 수 있는 좋은 환경이다.\n아직 \u0026lsquo;IT현업\u0026rsquo;이라고 하기엔 민망하고, 이렇다 할 경력도 없는 사람이 하기엔 부적절한 발언일 수 있다. 하지만 이런 \u0026ldquo;좋은 코드를 접하고, 그에 가까워질 수 있는 기회가 많다\u0026quot;라는 장점을 활용하지 않는 건, PS에서 얻을 수 있는 상당히 큰 소득을 포기하는 것이라고 생각한다. 같은 알고리즘이라고 같은 코드가 되는 건 아니다. 모르면 답지를 보고, 알아도 미심쩍다 싶으면 답지를 보는 게 좋다(\u0026hellip;)\n답안지 족보는 어디에 있는가 이렇게까지 써뒀으면 \u0026ldquo;그럼 그 좋은 코드들은 어디서 볼 수 있는데?\u0026rdquo; 라는 질문에도 간단하게나마 답변을 하는 게 예의인 것 같다. 개인적으로는 다음과 같은 코드를 참조한다.\nRies 마법의 슈퍼마리오(kks227님 블로그) 대회알고리즘 카테고리에 들어가면 큐나 스택, BFS/DFS 등 기초 알고리즘에서 시작해 HLD나 BCC, 최대 유량과 같은 고급 기법들까지 상세하게 정리된 포스팅을 볼 수 있다. 각 알고리즘의 원리와 함께 kks227님 본인의 예제코드, 관련 연습문제들까지 잘 정리되어 있어 혼자서 PS를 공부하는 사람에게 강력히 추천한다. Codeforces의 Editorial 매 라운드가 끝나고 제공되는 Editorial에서는 글로 된 알고리즘 풀이와 함께 공식 솔루션을 코드로 올려주는 경우가 많다. 비교적 낭비가 없고 간결한 코드가 대부분이다. 믿고 보는 OOO님 코드 코드포스에서는 라운드에 참가한 다른 사람의 코드를 볼 수 있다. 스코어보드에서 몇 가지 코드를 훑어보고 본인 스타일에 가장 잘 맞는 한두 사람을 친구로 추가해, 같은 문제를 어떻게 코딩했는지 살펴보는 것도 도움이 된다. TIP: 열어봤는데 코드에 #define이 5개 이상 있으면 일단 걸러라 ICPC 팀노트 (애매함) ICPC 팀노트를 인터넷에 공유하는 팀들이 있다. 충분히 성능이 검증된 코드이고, 팀노트 특성상 타이핑 수가 적고 간결한/깔끔한 코드들이 많다. 하지만 역으로 성능에 올인한 나머지 온갖 트릭들을 섞어놓는 경우도 있어 무조건적으로 본받는건 그다지 권장하지 않는다. 코드를 직접 보고 평가하는 심층면접 등 극히 일부를 제외한\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2019년 4월 19일","externalUrl":null,"permalink":"/blog/thoughts-on-model-answers-in-coding/","section":"Posts","summary":"* 개인 페이스북 타임라인에 썼던 내용을 다듬어 업로드한 글입니다.\n알고리즘(PS)을 공부하는 법 인터넷을 보면 종종 “알고리즘 잘 하는 법(or알고리즘 문제 잘 푸는 법)“에 대한 질문글이 올라오고, 많은 경우 “모르겠다고 답지를 보지 말고 혼자서 끝까지 고민해봐야 한다\"라는 답변이 주를 이룬다.\n","title":"코딩에서 답지를 보는것에 대한 개인적인 생각","type":"posts"},{"content":"얼마 전 코드포스에서 흥미로운 문제를 접했다. 대회 당시에는 풀지 못했는데, 에디토리얼을 읽어보던 중 뫼비우스 함수(Möbius function)에 대한 언급이 있어 이에 대해 좀더 자세히 알아보았다.\nMöbius function의 정의 뫼비우스 함수(Möbius function)는 자연수 n에 대해 다음과 같이 정의된다. (\\(p\\)는 소수)\n$$ \\mu(n) = \\begin{cases} (-1)^k\t\u0026\t\\mathrm{if\\,\\,\\,\\,} n=p_1p_2\\cdots p_k \\, (p_i \\neq p_j)\\\\ 0\t\u0026\t\\mathrm{if\\,\\,\\,\\,} p^2 | n \\end{cases} $$다시 말해, n이 서로 다른 k개 소수들의 곱이면 \\(\\mu(n)\\)은 \\( (-1)^k \\)이, 중복 소인수가 있으면(=어떤 제곱수를 약수로 가지면) 0이 되는 함수라고 할 수 있다. (이때 \\(\\mu(1)=1\\))\n뫼비우스 함수의 정의를 살펴보면 두 자연수 a,b에 대해 다음 성질이 성립함을 쉽게 알 수 있다.\n$$ \\mu(ab) = \\mu(a)\\mu(b) \\,\\,\\,\\,\\mathrm{if\\,}\\gcd(a,b)=1 $$이밖에도 다양한 성질들이 있지만 이번 글에서는 알고리즘적 측면에서의 활용에 집중하기로 한다.\n포함-배제의 원리 다음 벤 다이어그램을 살펴보자.\n전체 합집합의 넓이는 어떻게 구할 수 있을까? 색칠을 해보면 다음 식을 어렵지 않게 구할 수 있다.\n$$ \\begin{array}{cl} |A \\cup B| \u0026 = \u0026|A| + |B| - |A \\cap B|\\\\ |A \\cup B \\cup C| \u0026 = \u0026|A| + |B| + |C| - |A \\cap B| - |B \\cap C| - |C \\cap A| + |A \\cap B \\cap C| \\end{array} $$같은 원리를 확장하면 n개 집합에 대해 다음을 얻을 수 있다.\n$$ \\begin{aligned}\\\\{\\biggl |}\\bigcup _{i=1}^{n}A_{i}{\\biggr |}\u0026{}=\\sum _{i=1}^{n}\\left|A_{i}\\right|-\\sum _{i,j\\,:\\,1\\leq i","date":"2019년 3월 31일","externalUrl":null,"permalink":"/blog/Mobius-function/","section":"Posts","summary":"얼마 전 코드포스에서 흥미로운 문제를 접했다. 대회 당시에는 풀지 못했는데, 에디토리얼을 읽어보던 중 뫼비우스 함수(Möbius function)에 대한 언급이 있어 이에 대해 좀더 자세히 알아보았다.\nMöbius function의 정의 뫼비우스 함수(Möbius function)는 자연수 n에 대해 다음과 같이 정의된다. (\\(p\\)는 소수)\n","title":"Möbius 함수의 정의와 활용","type":"posts"},{"content":"코드잼이 새로운 시스템으로 바뀌었다. 사실 메인 코드잼에선 작년에 도입된 시스템이 올해 킥스타트까지 적용된 거라고 하는데 작년 코드잼은 국방부 전직퀘스트를 해야 했어서 개인적인 사정으로 참가를 못했으니 이번이 새로운 시스템을 경험하는 첫 대회였다.\n결론부터 말하면 다소 삐걱거리는 출발이었다. 시작 직후 한동안 문제 로딩이 안 되면서 몇 분 후에야 문제를 볼 수 있었고, 제출에서도 다소의 딜레이가 있었다. 거기에 더해 대회 막판에는 아예 제출버튼을 눌러도 코드 제출이 이루어지지 않아서 결국 끝까지 제출하지 못한 상태로 대회가 끝났다. 나름 지난 몇 차례의 킥스타트에서 꾸준히 10~20등 정도의 성적을 내다가 이번에 299등이라는 숫자를 보니 조금 억울하긴 하다(\u0026hellip;)\n문제 난이도는 작년보다 상당히 어려워졌다고 느꼈다. 하지만 input.txt를 다운받아 output.txt를 제출하는 예전 방식과 달리 코드 자체를 제출하게 되면서 (채점 데이터가 공개되지 않으니) 틀려도 여러 번 다시 제출할 수 있도록 바뀌었는데, 이 덕분인지 다소 가벼운 마음으로 진행할 수 있었다. 전체 문제는 여기에서 풀어볼 수 있다.\nProblem A. Training 구성된 fair team의 스킬 레벨 S가 고정되었을 때 필요한 최소 코칭시간을 구하는 건 간단하다. S이하의 스킬레벨을 가지는 학생들 중 기존 레벨이 높은 순서대로 P명을 고르고, 선택된 학생들을 S까지 끌어올리는 게 최적임은 쉽게 보일 수 있다. 이때 선택된 P명 중 레벨이 i인 학생의 수를 cnt[i]라고 하면\n$$ cost = \\sum_{i=1}^{S} (S - i) * cnt[i] $$가 된다. 그런데 이를 직접 구하면 \\( O(S\\_{max}) \\)가 되므로 전체 복잡도는 \\( O({S\\_{max}}^2) \\)이 된다. S의 범위가 1만 이하이므로 이대로도 아슬아슬하게 가능할지 모른다. 그렇지만 미리 전체 학생에 대해 각 레벨별 인원수 cnt[i]와 (Smax-i)*cnt[i]를 전처리로 구해놓으면 prefix sum과 two-pointer 기법을 종합해 각 S에 따른 cost를 매번 amortized O(1)에 구할 수 있다. 이때 정확히 P명을 선택해야 하므로 prefix sum으로부터 cost를 계산할 때 보정해야 하는 항에 주의하면 전체 \\( O(S\\_{max}) \\) 의 시간복잡도로 문제를 해결할 수 있다. [source]\nProblem B. Parcels Small은 모든 경우에 대해 BFS를 수행해보는 완전탐색으로 구할 수 있다. 이때 시간복잡도는 \\( O((R\\*C)^2) \\)이 되고 R,C가 최대 10이므로 충분히 적용 가능하다. Large는 R,C가 각각 250 이하이므로 다른 접근이 필요한데, 처음에 방법이 쉽게 떠오르지 않아서 일단 Small 전용 코드를 먼저 제출했다.\nLarge를 해결하는 데 있어서 핵심 아이디어는 새로운 오피스가 담당할 수 있는 영역은 다이아몬드 형태라는 점이다. 두 점 \\( (x\\_1,y\\_1), (x\\_2,y\\_2) \\) 사이의 거리가 \\( \\|x\\_1 - x\\_2\\| + \\|y\\_1 - y\\_2\\| \\) 로 정의되므로 새로운 오피스에서 거리가 K 이하인 영역들은 다이아몬드 형태를 가질 수밖에 없다. 따라서 이를 역으로 이용해 전체 \\(R\\*C\\) 개 영역을 1)기존 오피스들이 담당하는 영역과 2)새로운 오피스가 담당할 영역으로 나누고, 주어진 영역들을 커버할 수 있는 가장 작은 다이아몬드의 크기를 구하는 방법을 생각할 수 있다.\n이제 이를 다이아몬드를 구성하는 \\(y=x+a\\) 와 \\(y=-x + b\\) 직선을 구하는 것으로 바꿔 생각해보자. 다시 말해, 주어진 집합을 구성하는 지점들에 대해 \\( x-y, x+y \\)의 최대,최소값을 찾고 그 점들을 모두 커버하는 다이아몬드를 구하면 된다. 아래 그림을 참고하자.\n여기서 검은색 영역을 모두 커버하는 다이아몬드를 찾는다고 하자. x+y가 최대,최소인 지점과 x-y가 최대,최소인 지점에서 사선을 그리고, 그중 더 길이가 긴 변을 최종 다이아몬드의 크기로 삼으면 된다. 사전에 모든 x+y, x-y의 경우의 수를 카운트한 후, 기존 오피스들로부터 BFS를 통해 \u0026ldquo;기존 오피스가 담당하는 영역\u0026quot;을 점차 확장시키면서 새로운 오피스가 담당해야 할 영역의 크기를 구해 최종 답을 구할 수 있다. x+y, x-y의 범위가 500 이내이므로 배열에 모든 경우의 수를 담을 수 있다. 여기에 집합에서 원소가 제거될 때 최대,최소값은 각각 감소,증가하기만 한다는 사실을 이용하면 BFS를 수행하면서 남아있는 x+y, x-y의 최대,최소를 amortized O(1)에 구할 수 있다. 여기에 분홍색 사선과 같이 엇갈리게 교차되는 경우에 주의하면 전체 시간복잡도 \\( O(R\\*C)\\) 에 문제를 해결할 수 있다. [source]\n그렇게 코딩을 끝내고 B Large에 다시 제출버튼을 눌렀지만 제출이 안 됐다.. 처음엔 채점 횟수에 제한이 있거나 내 인터넷 환경의 문제인 줄 알았지만 public QnA를 통해 서버 차원에서의 문제라는 걸 알 수 있었다. 분 단위로 새로고침을 누르면서 차분히 서버가 복구되길 기다렸지만 결국 대회 종료까지 해결되지 않았고, 그렇게 B small까지 맞춘 323등으로 대회가 끝났다.\n이후 Practice 모드에서 다시 채점해 본 결과 정답 판정을 받았다(\u0026hellip;) 대회 당시에 B Large까지 통과한 사람의 수가 많지 않아 마지막 제출에 성공했다면 좀더 높은 등수를 받았을 거라는 아쉬움이 남지만 적어도 시간 내에 풀이를 떠올릴 수는 있었다는 점에서 만족하기로 했다. C는 Small에서도 아무런 아이디어가 떠오르지 않았는데 에디토리얼 올라오면 한번 읽어봐야겠다.\n","date":"2019년 3월 25일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2019-RoundA-review/","section":"Posts","summary":"코드잼이 새로운 시스템으로 바뀌었다. 사실 메인 코드잼에선 작년에 도입된 시스템이 올해 킥스타트까지 적용된 거라고 하는데 작년 코드잼은 국방부 전직퀘스트를 해야 했어서 개인적인 사정으로 참가를 못했으니 이번이 새로운 시스템을 경험하는 첫 대회였다.\n결론부터 말하면 다소 삐걱거리는 출발이었다. 시작 직후 한동안 문제 로딩이 안 되면서 몇 분 후에야 문제를 볼 수 있었고, 제출에서도 다소의 딜레이가 있었다. 거기에 더해 대회 막판에는 아예 제출버튼을 눌러도 코드 제출이 이루어지지 않아서 결국 끝까지 제출하지 못한 상태로 대회가 끝났다. 나름 지난 몇 차례의 킥스타트에서 꾸준히 10~20등 정도의 성적을 내다가 이번에 299등이라는 숫자를 보니 조금 억울하긴 하다(…)\n","title":"Code Jam Kickstart 2019 Round A 후기","type":"posts"},{"content":" High-Order Functions 다른 함수를 다룰 수 있는 함수를 고차 함수(higher-order function)라고 한다.\nDefinition do3times {X:Type} (f:X-\u0026gt;X) (a:X) : X := f (f (f a)). \u0026gt;\u0026gt; Check @do3times : @do3times : forall X : Type, (X -\u0026gt; X) -\u0026gt; X -\u0026gt; X 이 do3times는 함수 f와 인자 a를 받아 a에 f를 3번 적용한 결과를 돌려준다. 함수를 인자로 제공한다는게 어떤 의미인지 살펴보자\n\u0026gt;\u0026gt; Definition doubleMe (n:nat) : nat := n+n. \u0026gt;\u0026gt; Compute do3times doubleMe 3. : 24 : nat Filter 이제 좀더 유용한 고차 함수들에 대해 알아보자. filter는 X타입의 리스트(list X)와 원소 X에 대한 판정함수(predicate, \\(f:X\\rightarrow bool\\))를 받아 주어진 predicate를 만족하는(i.e. true를 리턴하는) 원소들만을 남긴다.\nFixpoint filter {X:Type} (test:X-\u0026gt;bool) (l:list X) : (list X) := match l with | [] =\u0026gt; [] | h::t =\u0026gt; if test h then h::(filter test t) else (filter test t) end. \u0026gt;\u0026gt; Compute filter evenb [1;2;3;4;5;6]. : [2; 4; 6] : list nat 조금 더 복잡한 predicate에서도 마찬가지로 잘 동작한다.\nDefinition length_is_1 {X : Type} (l : list X) : bool := (length l) =? 1. \u0026gt;\u0026gt; filter length_is_1 [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ] : [ [3]; [4]; [8] ] : list (list nat). filter를 이용하면 다양한 함수를 쉽게 작성할 수 있다. nat으로 이루어진 리스트에서 짝수의 개수를 세는 함수를 생각해보자. 고차 함수를 사용하지 않는다면 아래처럼 작성될 것이다.\nFixpoint count_even (l : list nat) : nat := match l with | [] =\u0026gt; O | h::t =\u0026gt; if evenb h then (count_even t) + 1 else (count_even t) end. 그런데 filter를 이용하면 동일한 내용을 다음과 같이 간결한 형태로 작성할 수 있다.\nDefinition count_even\u0026#39; (l:list nat) : nat := length (filter evenb l). Compute를 통해 확인해보자\n\u0026gt;\u0026gt; Compute count_even [1;2;3;4;5;6]. : 3 : nat \u0026gt;\u0026gt; Compute count_even\u0026#39; [1;2;3;4;5;6]. : 3 : nat Anonymous Functions 앞서 filter를 테스트할 때 length_is_1이라는 함수를 작성했다. 그런데 이렇게 한번 쓰고 말 함수를 위해 따로 정의를 작성하고 이름을 붙이는 건 조금 낭비라는 생각이 든다. 이렇게 단순한 내용이면 즉석에서 바로 만들어서 사용해도 되지 않을까? 다른 많은 함수형 언어들과 마찬가지로 Coq도 람다식(lambda expression)을 지원한다.\n\u0026gt;\u0026gt; Compute do3times (fun n =\u0026gt; n*n) 2. : 256 : nat 위 코드에서 (fun n =\u0026gt; n*n)은 \u0026ldquo;n을 받아 n*n을 반환하는 함수\u0026quot;를 의미한다. 이를 이용하면 앞서 작성한 length_is_1예제를 다음과 같이 다시 쓸 수 있다.\n\u0026gt;\u0026gt; filter (fun l =\u0026gt; (length l) =? 1) [ [1; 2]; [3]; [4]; [5;6;7]; []; [8] ]. : [ [3]; [4]; [8] ] : list (list nat) Map filter만큼 유용하게 사용되는 함수로 map이 있다. map은 함수 f와 리스트 l( = [a1;a2;\u0026hellip;;an])을 받아 [f a1; f a2; \u0026hellip; ; f an]을 반환한다.\nFixpoint map {X Y : Type} (f:X-\u0026gt;Y) (l:list X) : (list Y) := match l with | [] =\u0026gt; [] | h::t =\u0026gt; (f h)::(map f t) end. 예시를 통해 map이 어떻게 동작하는지 알아보자.\n\u0026gt;\u0026gt; Compute map (fun n=\u0026gt;n*n) [1;2;3;4;5]. : [1; 4; 9; 16; 25] : list nat map의 정의에서 타입을 X, Y 로 따로 정의했으므로 결과 리스트의 타입이 입력 리스트의 타입과 같을 필요는 없다.\n\u0026gt;\u0026gt; Compute map evenb [1;2;3;4;5]. : [false; true; false; true; false] : list bool Fold filter, map과 함께 함수형 언어의 삼신기(\u0026hellip;)로 불리는 마지막 함수 fold에 대해 알아보자. 일부 언어(ex:파이썬)에서는 reduce라는 이름으로 제공된다. 파이썬 2에서는 기본제공이었는데 파3에서는 functools 라이브러리로 따로 빠짐\nFixpoint fold {X Y : Type} (f:X-\u0026gt;Y-\u0026gt;Y) (l:list X) (b:Y) : Y := match l with | nil =\u0026gt; b | h::t =\u0026gt; f h (fold f t b) end. 정의만 봐서는 아직 어떤 함수인지 잘 감이 오지 않는다. 결론부터 말하면 fold는 인자 2개를 받는 함수 f, 리스트를 받아 각 인자들에 함수 f를 누적하면서 적용해 최종 결과를 반환하는 함수라고 할 수 있다.\n위 그림을 보면 \u0026ldquo;f를 리스트의 인자에 누적해서 적용한다\u0026quot;라는 말을 이해할 수 있다. 그런데 그림을 보면 시작점, 다시 말해 f에 맨 처음 제공될 인자가 하나 필요하다. 이를 위해 fold 함수를 정의할 때 함수 f와 리스트 l 외에도 시작점 역할을 수행할 인자(b)를 함께 명시했음에 유의하자.\n예제를 통해 fold에 대해 감을 잡아보자.\n\u0026gt;\u0026gt; Compute fold plus [1;2;3;4] 0. : 10 : nat. \u0026gt;\u0026gt; Compute fold mult [1;2;3;4] 1. : 24 : nat. \u0026gt;\u0026gt; Compute fold andb [true;true;false;true] true. : false : bool \u0026gt;\u0026gt; fold app [[1];[];[2;3];[4]] []. : [1;2;3;4] : list nat. Functions that Construct Functions 지금까지 소개한 고차 함수는 모두 \u0026ldquo;함수를 인자로 받는\u0026rdquo; 경우였다. 이제 함수를 반환하는 함수에 대해 알아보자. 다음 함수는 x를 받아 \u0026ldquo;nat타입 인자를 받아 상수 x를 반환하는 함수\u0026quot;를 반환한다.\nDefinition constfun {X:Type} (x:X) : nat-\u0026gt;X := fun (n:nat) =\u0026gt; x. constfun을 이용해 어떤 입력에도 무조건 true를 반환하는 fun_true 함수를 만들어보자.\n\u0026gt;\u0026gt; Definition fun_true := constfun true. \u0026gt;\u0026gt; Compute fun_true 123. : true : bool 결과 함수를 따로 저장하지 않고도 사용할 수 있다.\n\u0026gt;\u0026gt; Compute (constfun 5) 999. : 5 : nat 사실 이런 생소한 함수가 아니더라도 지금까지 다뤘던 많은 함수들이 함수를 결과로 반환한다. 엄밀히 말해, 모든 다인자 함수(둘 이상의 인자를 받는 함수)들은 함수를 반환하는 일인자 함수(하나의 인자를 받는 함수)라고 말할 수 있다. 단순하게 plus를 생각해보자.\n\u0026gt;\u0026gt; Check plus. : nat -\u0026gt; nat -\u0026gt; nat 지금까지 nat-\u0026gt;nat-\u0026gt;nat은 \u0026ldquo;nat인자 2개를 받아 nat을 반환하는 함수\u0026quot;라고 해석해왔다. 그런데 -\u0026gt;는 타입들 사이에서 정의된 right associative한 binary operator이다. 이를 고려하면서 위 타입을 다시 엄밀히 표기하면 nat-\u0026gt;(nat-\u0026gt;nat) 가 된다. 따라서 다음과 같은 논리를 펼 수 있다.\nnat-\u0026gt;nat는 \u0026ldquo;nat을 받아 nat을 반환하는 함수\u0026rdquo; 타입이다. nat-\u0026gt;(nat-\u0026gt;nat)은 nat을 받아 \u0026lt;nat을 받아 nat을 반환하는 함수\u0026gt;를 반환하는 함수 타입이다. 예제를 통해 위 말이 가지는 의미를 알아보자. plus에 nat 인자를 \u0026lsquo;하나만\u0026rsquo; 제공했다.\n\u0026gt;\u0026gt; Definition plus3 := plus 3. \u0026gt;\u0026gt; Check plus3. : plus3 : nat-\u0026gt;nat 이렇게 정의한 plus3은 nat-\u0026gt;nat 타입을 가진다. 구체적으로 어떤 함수인지 살펴보자.\n\u0026gt;\u0026gt; Compute plus3 5. : 8 : nat \u0026gt;\u0026gt; Compute plus3 10. : 13 : nat \u0026gt;\u0026gt; do3times plus3 2. : 11 : nat \u0026gt;\u0026gt; do3times (plus 10) 7. : 37 : nat plus3은 입력으로 들어오는 인자에 3을 더해주는 함수임을 어렵지 않게 확인할 수 있다. 이렇게 plus3을 만드는 과정이나 마지막의 (plus 10) 처럼 인자의 일부만 제공된 적용시켜 함수를 만들 수 있으며, 이를 함수에 대한 partial application이라고 한다.\n지금까지 함수형 프로그래밍 언어로서의 Coq에 대해 알아보았다. 다음 장에서는 다시 Coq의 여러 tactic들에 대해 다룬다.\n","date":"2019년 2월 27일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch04-2/","section":"Posts","summary":"High-Order Functions 다른 함수를 다룰 수 있는 함수를 고차 함수(higher-order function)라고 한다.\nDefinition do3times {X:Type} (f:X-\u003eX) (a:X) : X := f (f (f a)). \u003e\u003e Check @do3times : @do3times : forall X : Type, (X -\u003e X) -\u003e X -\u003e X 이 do3times는 함수 f와 인자 a를 받아 a에 f를 3번 적용한 결과를 돌려준다. 함수를 인자로 제공한다는게 어떤 의미인지 살펴보자\n\u003e\u003e Definition doubleMe (n:nat) : nat := n+n. \u003e\u003e Compute do3times doubleMe 3. : 24 : nat Filter 이제 좀더 유용한 고차 함수들에 대해 알아보자. filter는 X타입의 리스트(list X)와 원소 X에 대한 판정함수(predicate, \\(f:X\\rightarrow bool\\))를 받아 주어진 predicate를 만족하는(i.e. true를 리턴하는) 원소들만을 남긴다.\n","title":"[Coq 입문] Ch04. Polymorphism \u0026 Higher-Order Functions (2)","type":"posts"},{"content":"","date":"2019년 2월 27일","externalUrl":null,"permalink":"/tag/coq/","section":"Tags","summary":"","title":"Coq","type":"tags"},{"content":"","date":"2019년 2월 27일","externalUrl":null,"permalink":"/series/coq-%EC%9E%85%EB%AC%B8/","section":"Series","summary":"","title":"Coq 입문","type":"series"},{"content":"","date":"2019년 2월 27일","externalUrl":null,"permalink":"/tag/software-foundations/","section":"Tags","summary":"","title":"Software Foundations","type":"tags"},{"content":"","date":"2019년 2월 24일","externalUrl":null,"permalink":"/tag/codeforces/","section":"Tags","summary":"","title":"Codeforces","type":"tags"},{"content":"코드포스(http://codeforces.com/) Master 등급(a.k.a. 오렌지)을 달성했다. 그전까지 Candidate Master에서 거의 2년간 머무르다가 올해 들어 적극적으로 대회를 참가하기 시작했고, 그 덕분인지 최근 한 달(1.22 ~ 2.24) 동안 rating +241이라는 나름 의미있는 성과를 거두면서 승급에 성공했다. 이제 박제해야지\n최근 대회를 진행하면서 느꼈던 건 (당연한 소리겠지만) 풀 수 있어야 하는 문제를 못 푸는 일은 없어야 한다는 점이었다. 개인적으로는 Div.2 D,E (=Div.1 B,C) 정도를 기준으로 잡았다. 대바대(대회 by 대회)라고는 하지만 이 난이도에서는 대체로 \u0026ldquo;잘 알려진\u0026rdquo;, 혹은 규칙을 파악하면 코딩 자체는 어렵지 않은 문제들이 많다. 아이디어성 문제들은 어쩔 수 없다고 해도, 대회가 끝나고 나서 \u0026ldquo;이걸 왜 두시간동안 못 떠올렸지\u0026quot;라는 아쉬움을 없애자고 생각했다. 특히 최근 에듀코포 D에서 대회 종료 직전이 되어서야 풀이를 찾아내면서 이런 생각을 굳혔다. 다행히 지난 라운드에선 F의 밸런스 붕괴에 힘입어 그런 아쉬움 없이 28등으로 끝마칠 수 있었다.\n재작년 학부 마지막 ICPC에서 World Finals 진출티켓까지 얻어놓고 모종의 사유(\u0026hellip;)로 나가지 못하게 되면서 의욕이 많이 꺾였던 건 사실이다. 여기에 더해 PS는 대부분의 대회가 학부or대학원생을 대상으로 열리다보니 졸업하는 순간 나갈 수 있는 대회의 폭이 크게 좁아졌고, 그렇게 구글 코드잼이나 페이스북 해커컵처럼 메이저한 대회 몇 개를 빼면 \u0026ldquo;순전히 재미/실력 향상을 위해 언오피셜 대회를 참가하는\u0026rdquo; 빈도도 같이 줄어들었다.\n그런데도 굳이 코포를 계속한 이유가 뭐냐고 하면 솔직히 잘 모르겠다. 대회 수상이 아니어도 내가 실력이 늘고 있다는 걸 스스로에게 입증(?)시킬 수 있는 괜찮은 수단이었기도 하고, 그래도 아이디 색 바뀌는 걸 한번쯤 보고싶어서였을수도 있다. 지금도 \u0026ldquo;언젠가 레드를 찍으면 좋겠다\u0026rdquo; 같은 생각은 있지만 그렇다고 퇴근해서 평일 새벽까지 코포 라운드 돌다가 다시 출근하는 라이프를 계속하는건 조금 무리지 싶다(몇 번 해보니까 몸이 파업하는게 느껴짐..ㅠ). 한동안은 BOJ슬랙 꾸준히 눈팅하면서 좋은 문제 있으면 풀어보고, 주말이나 저녁 시간대에 열리는 대회에만 참가하려고 한다. 마침 하던 공부(Coq입문)도 있으니 일단은 그쪽에 집중할 계획이다.\n","date":"2019년 2월 24일","externalUrl":null,"permalink":"/blog/Codeforces-Master-rankup/","section":"Posts","summary":"코드포스(http://codeforces.com/) Master 등급(a.k.a. 오렌지)을 달성했다. 그전까지 Candidate Master에서 거의 2년간 머무르다가 올해 들어 적극적으로 대회를 참가하기 시작했고, 그 덕분인지 최근 한 달(1.22 ~ 2.24) 동안 rating +241이라는 나름 의미있는 성과를 거두면서 승급에 성공했다. 이제 박제해야지\n최근 대회를 진행하면서 느꼈던 건 (당연한 소리겠지만) 풀 수 있어야 하는 문제를 못 푸는 일은 없어야 한다는 점이었다. 개인적으로는 Div.2 D,E (=Div.1 B,C) 정도를 기준으로 잡았다. 대바대(대회 by 대회)라고는 하지만 이 난이도에서는 대체로 “잘 알려진”, 혹은 규칙을 파악하면 코딩 자체는 어렵지 않은 문제들이 많다. 아이디어성 문제들은 어쩔 수 없다고 해도, 대회가 끝나고 나서 “이걸 왜 두시간동안 못 떠올렸지\"라는 아쉬움을 없애자고 생각했다. 특히 최근 에듀코포 D에서 대회 종료 직전이 되어서야 풀이를 찾아내면서 이런 생각을 굳혔다. 다행히 지난 라운드에선 F의 밸런스 붕괴에 힘입어 그런 아쉬움 없이 28등으로 끝마칠 수 있었다.\n","title":"Codeforces 오렌지(Master) 달성","type":"posts"},{"content":" Polymorphic Lists 이전 장에서 natlist, 다시 말해 nat으로 이루어진 리스트에 대해 다뤘다. 마찬가지 방법으로 문자열 리스트, bool 리스트, 리스트의 리스트 등 다양한 리스트를 정의할 수 있다. 예를 들어 bool 리스트는\nInductive boollist : Type := | bool_nil | bool_cons (b : bool) (l : boollist). 로 정의된다.\n그런데 매번 새로운 타입의 리스트가 필요할 때마다 (거의) 동일한 정의를 다시 써주는 건 낭비일 것이다. 새로운 타입에 맞게 정의를 다시 쓴다는 말은 단순히 nil이나 cons의 문제가 아니라, length, reverse, append 등 앞서 natlist에서 정의했던 모든 함수를 전부 새로 작성해야 한다는 의미가 된다. 이런 불필요한 중복을 피하기 위해 Coq에서는 다형적(polymorphic) 타입을 지원한다.\nInductive list (X : Type) : Type := | nil | cons (x : X) (l : list X). 기존 natlist와 거의 유사하지만 한 가지가 더 추가됐다. 이렇게 정의한 list는 단일 타입보다는 Type에서 Inductive 정의(Type)로 가는 함수로 작용한다. 임의의 타입 X에 대해 list X는 타입X의 원소를 요소로 가지는 리스트에 대한 inductive한 정의가 된다.\n\u0026gt;\u0026gt; Check list. list : Type -\u0026gt; Type list 정의에서 등장한 인자 X는 자동적으로 nil, cons의 인자가 된다. 다시 말해, nil과 cons는 _다형 생성자(polymorphic constructor)_가 되며, 생성규칙을 통해 요소를 만들 때 타입 X를 인자로 포함시켜줘야 한다. 예시를 통해 살펴보자.\n\u0026gt;\u0026gt; Check (nil nat). nil nat : list nat \u0026gt;\u0026gt; Check (cons nat 3 (nil nat)). cons nat 3 (nil nat) : list nat 그러면 nil nat이 아닌 그냥 nil은 어떤 타입이라고 할 수 있을까? 정의상으로는 list X 타입이어야 하지만 여기선 타입 X를 지정(bind)하지 않았다. 따라서 단순히 \\(Type \\rightarrow list\\,X\\) 라고 하기에도 무리가 있다. 굳이 표현하자면 \\((X\\,:\\,Type) \\rightarrow list\\,X\\) 정도가 그나마 가까울 것이다. Coq에서는 이런 경우를 설명하기 위해 \\(\\forall X\\,:\\,Type, \\,\\,list\\,X\\)라는 표현을 사용한다.\n\u0026gt;\u0026gt; Check nil. nil : forall X : Type, list X 마찬가지로 cons의 타입 역시 비슷하게 나타난다.\n\u0026gt;\u0026gt; Check cons. cons : forall X : Type, X -\u0026gt; list X -\u0026gt; list X 같은 방법을 통해 기존에 만들었던 함수들을 다시 polymorphic 버전으로 작성할 수 있다.\nFixpoint repeat (X:Type) (x:X) (count:nat) : list X := match count with | O =\u0026gt; nil X | S count\u0026#39; =\u0026gt; cons X x (repeat X x count\u0026#39;) end. \u0026gt;\u0026gt; Compute repeat nat 5 3. cons nat 5 (cons nat 5 (cons nat 5 (nil nat))) : list nat Type Annotation Inference 이번에는 위와 동일한 코드를 인자에 대한 타입 정보만 빼고 다시 작성해보자.\nFixpoint repeat_noType X x count : list X := match count with | O =\u0026gt; nil X | S count\u0026#39; =\u0026gt; cons X x (repeat_noType X x count\u0026#39;) end. 그런데 repeat_noType과 앞서 작성했던 repeat을 비교하면 동일하게 나타난다.\n\u0026gt;\u0026gt; Check repeat. forall X : Type, X -\u0026gt; nat -\u0026gt; list X \u0026gt;\u0026gt; Check repeat_noType. forall X : Type, X -\u0026gt; nat -\u0026gt; list X \u0026gt;\u0026gt; Compute repeat_noType nat 4 2. cons nat 4 (cons nat 4 (nil nat)) : list nat Coq는 명시적으로 지정되지 않은 타입들에 대해 _타입 추론(Type Inference)_을 수행한다. repeat_noType에서 X는 cons의 첫 번째 인자로 들어갔으므로 Type이어야 하고, O나 S 생성자로부터 count가 nat타입임을 추론해내는 것이다. 이런 기능 덕에 굳이 명시적으로 타입을 지정할 필요성은 없지만, 코드 이해를 돕기 위해 일부 핵심적인 인자에는 타입을 표기하는 편이 권장된다.\nType Argument Synthesis 앞서 굳이 타입을 명시하지 않아도 앞뒤 문맥을 통해 각각이 어떤 타입인지를 추론해낼 수 있었다. 마찬가지 추론을 조금 다른 시각에서 사용해보자. 모든 인자를 전부 명시하는 대신 **_**를 사용해 표현하는 것이다. Coq는 \u0026ldquo;_\u0026ldquo;를 만나면 해당 시점에서 가능한 모든 정보들을 활용해 그 공백을 채우려 시도한다. 다시 말해 앞뒤 문맥으로부터 공백( _ )에 들어갈 대상이 명백한 경우 단순히 **_**로 표현할 수 있는 것이다. 다음 예시를 살펴보자\nFixpoint repeat2 X x count : list X := match count with | 0 =\u0026gt; nil _ | S count\u0026#39; =\u0026gt; cons _ x (repeat2 _ x count\u0026#39;) end. 위 정의의 nil _나 cons _ x (...)에서 공백을 채워보자. repeat_는 list X 타입을 반환해야 하므로 nil의 인자로는 X (: Type) 밖에 들어갈 수 없고, 같은 이유로 cons에 붙은 _도 마찬가지로 채울 수 있다.\n물론 여기까지만 해서는 딱히 줄어든 느낌이 나지 않는다. X가 _로 대체되었을 뿐 같은 1글자이므로 타이핑 수에 있어서는 별다른 차이가 없는 것은 사실이다. 그렇다면 가독성을 비교해보자.\nDefinition list123 := cons nat 1 (cons nat 2 (cons nat 3 (nil nat))). Definition list123_ := cons _ 1 (cons _ 2 (cons _ 3 (nil _))). 힘의 차이가 느껴지십니까 형식적인 인자(nat)가 사라진 아래쪽에서는 좀더 내용 파악이 손쉬워진 것을 확인할 수 있다.\nImplicit Arguments 여기서 더 나아나서, _로나마 표현하는 대신 아예 생략해버리는 것 또한 가능하다. Coq의 Arguments 명령어(directives) 뒤에 함수의 이름과 그 인자를 나열하고, 이때 implicit하게 만들고 싶은 인자들을 {, } 로 둘러싸면 된다. 마찬가지로 이 경우에도 인자 이름 대신 _를 사용할 수 있다.\nArguments nil {X}. Arguments cons {X} _ _. Arguments repeat {X} x count. Compute를 통해 확인해보자.\n\u0026gt;\u0026gt; Compute cons 1 (cons 2 (cons 3 nil)). : cons 1 (cons 2 (cons 3 nil)) : list nat \u0026gt;\u0026gt; Compute repeat 5 3. : cons 5 (cons 5 (cons 5 nil)) : list nat 혹은 함수를 정의하는 단계에서부터 인자를 implicit하게 만들 수도 있다.\nFixpoint repeat3 {X : Type} (x : X) (count : nat) : list X := match count with | 0 =\u0026gt; nil | S count\u0026#39; =\u0026gt; cons x (repeat3 x count\u0026#39;) end. 그런데 이를 타입 정의에 사용할 때는 주의해야 한다. 이 글 처음에 정의했던 list 타입(polymorphic list)을 다음과 같이 정의했다고 해보자.\nInductive list\u0026#39; {X:Type} : Type := | nil\u0026#39; | cons\u0026#39; (x : X) (l : list\u0026#39;). 인자로 들어가는 X를 implicit하게 정의했기 때문에 이후 list와 관여된 모든 서술을 list nat, list bool처럼 특정하지 않고 list로 일반화해야 한다. polymorphic type에 대한 일반적인 서술이 이론적으로 불가능한 것은 아니지만 대부분의 경우 굉장히 먼 길을 돌아가야 하고 그만큼 핵심을 벗어나기 쉽다.\nSupplying Type Arguments Explicitly 앞서 아무 인자나 implicit하게 만드는 것이 어떻게 위험할 수 있는지 설명했다. 그렇다면 모든 인자를 explicit하게 유지해야만 할까? 인자를 implicit하게 만드는 법이 있다면 반대로 implicit하게 된 인자를 다시 explicit하게 명시할 수 있는 방법이 존재해야 하지 않을까? 결론부터 말하자면 그렇다.\n다음 문장을 실행하려고 시도해보자. Coq는 이에 대해 implicit 인자 X를 추론할 수 없다는 에러를 내뱉는다.\n\u0026gt;\u0026gt; Definition mynil := nil. : (ERR) Cannot infer the implicit parameter X of nil whose type is \u0026#34;Type\u0026#34;. 이를 해결하기 위해 Coq가 타입 X를 추론할 수 있도록 힌트를 제공할 수 있다.\n\u0026gt;\u0026gt; Definition mynil : list nat := nil. 이는 완곡어법이라고 생각할 수도 있다. 조금 더 직접적인 방법을 알아보자. Coq에서는 이미 일부 인자가 implicit화 된 nil의 explicit 버전(모든 인자를 명시적으로 제공해야 함)을 @nil이라는 이름으로 사용할 수 있다.\n\u0026gt;\u0026gt; Definition mynil\u0026#39; = @nil nat. @는 repeat이나 cons 등에도 마찬가지로 적용할 수 있다. Check nil과 Check @nil을 통해 둘이 어떻게 다른지 감을 잡아보자.\n이렇게 list nat에서 작성했던 함수들을 다시 polymorphic 버전으로 만들고 적절한 implicit argument를 추가했다면, 다시 편리했던 notation으로 돌아가자.\nNotation \u0026#34;x :: y\u0026#34; := (cons x y) (at level 60, right associativity). Notation \u0026#34;[ ]\u0026#34; := nil. Notation \u0026#34;[ x ; .. ; y ]\u0026#34; := (cons x .. (cons y []) ..). Notation \u0026#34;x ++ y\u0026#34; := (app x y) (at level 60, right associativity). Polymorphic Pairs pair도 다시 polymorphic하게 만들어보자. 이전에는 nat타입 인자 둘을 묶는 데 사용했지만 이젠 두 타입이 꼭 같을 필요는 없다. 이렇게 정의된 polymorphic pair는 일반적으로 product라는 이름으로 불린다.\nInductive prod (X Y : Type) : Type := | pair (x : X) (y : Y). Arguments pair {X} {Y} _ _. 마찬가지로 notation을 추가하자.\nNotation \u0026#34;( x , y )\u0026#34; := (pair x y). Notation \u0026#34;X * Y\u0026#34; := (prod X Y) : type_scope. 여기서 : type_scope는 X*Y가 타입일 때의 곱셈과 다른 원소일 때(ex: nat)의 곱셈을 구분하기 위해 명시했다. 타입끼리의 product를 집합에서의 cartesian product로 생각하면 좀더 쉽게 이해할 수 있다.\n예시 함수를 하나 작성해보자. 파이썬을 비롯한 대부분의 언어에서 zip이라는 이름으로 익숙한, 두 리스트를 하나로 합쳐주는 함수이다. 여기에서는 Coq라이브러리와의 일관성을 위해 combine이라는 이름을 사용했다.\nFixpoint combine {X Y:Type} (lx:list X) (ly:list Y) : list (X*Y) := match lx, ly with | [], _ =\u0026gt; [] | _, [] =\u0026gt; [] | x :: tx, y :: ty =\u0026gt; (x, y) :: (combine tx ty) end. Polymorphic Options 이전 natoption을 다룰 때 모나드(monad)에 대해 잠깐 언급했다. 엄밀히 말해 natoption 자체는 단일 타입이므로 모나드라고 할 수 없다. (추가 영상에 설명된) maybe monad의 역할을 떠올리면서 아래 정의를 보자.\nInductive option (X:Type) : Type := | Some (x : X) | None. Arguments Some {X} _. Arguments None {X}. 이렇게 만들어진 option은 타입 X를 받아 option X 타입으로 만들어주는 모나드라고 할 수 있다. 타입 option X가 가지는 의미는 이전에 natoption을 통해 설명했으니 여기에서는 생략한다. 마지막으로 nth_error 함수의 polymorphic 버전을 작성하면서 설명을 마친다.\nFixpoint nth_error {X:Type} (l:list X) (n:nat) : option X := match l with | [] =\u0026gt; None | a :: l\u0026#39; =\u0026gt; if n =? O then Some a else nth_error l\u0026#39; (pred n) end. \u0026gt;\u0026gt; Compute nth_error [4;5;6;7] 0. : Some 4 : option nat \u0026gt;\u0026gt; Compute nth_error [[1];[2]] 1. : Some [2] : option (list nat) \u0026gt;\u0026gt; Compute nth_error [true] 2. : None : option bool ","date":"2019년 2월 17일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch04-1/","section":"Posts","summary":"Polymorphic Lists 이전 장에서 natlist, 다시 말해 nat으로 이루어진 리스트에 대해 다뤘다. 마찬가지 방법으로 문자열 리스트, bool 리스트, 리스트의 리스트 등 다양한 리스트를 정의할 수 있다. 예를 들어 bool 리스트는\nInductive boollist : Type := | bool_nil | bool_cons (b : bool) (l : boollist). 로 정의된다.\n그런데 매번 새로운 타입의 리스트가 필요할 때마다 (거의) 동일한 정의를 다시 써주는 건 낭비일 것이다. 새로운 타입에 맞게 정의를 다시 쓴다는 말은 단순히 nil이나 cons의 문제가 아니라, length, reverse, append 등 앞서 natlist에서 정의했던 모든 함수를 전부 새로 작성해야 한다는 의미가 된다. 이런 불필요한 중복을 피하기 위해 Coq에서는 다형적(polymorphic) 타입을 지원한다.\n","title":"[Coq 입문] Ch04. Polymorphism \u0026 Higher-Order Functions (1)","type":"posts"},{"content":" Reasoning About Lists 리스트에 대한 간단한 정리를 증명해보자.\nTheorem nil_app : forall l:natlist, [] ++ l = l. Proof. reflexivity. Qed. 대상이 natlist라는 점만 제외하면 이전의 forall n:nat, 0+n=n과 같은 형태이다. nat에서와 마찬가지로 natlist도 destruct를 통해 생성규칙에 따라 경우를 나눌 수 있다.\nTheorem tl_length_pred : forall l:natlist, pred (length l) = length (tl l). Proof. intros l. destruct l as [| n l\u0026#39;] eqn:E. - (* l = [] *) reflexivity. - (* l = n::l\u0026#39; *) reflexivity. 마찬가지로 수학적 귀납법(induction tactic) 역시 적용 가능하다. nat에서의 귀납법과 마찬가지로 base step과 induction step으로 이루어져 있으며, 각각\nbase step : l = []인 경우, nat에서 n = 0인 경우에 해당한다. induction step : l = n :: l'인 경우, nat에서 n = S n'인 경우에 해당한다. 이때 n::l', 다시 말해 cons n l'은 2개의 인자를 받는다는 점에 유의하자. 예시를 통해 natlist에서의 induction이 어떻게 이루어지는지 살펴보자.\nTheorem app_assoc : forall (l1 l2 l3 : natlist), (l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3). Proof. intros l1 l2 l3. induction l1 as [| n l1\u0026#39; IH1]. - (* l1 = [] *) reflexivity. - (* l1 = n :: l1\u0026#39; *) simpl. rewrite -\u0026gt; IH1. reflexivity. Qed. nat에서와 거의 동일한 방식으로 증명이 끝났다. 그러면 이제 그렇지 않은 경우를 통해 Coq를 어떤 식으로 사용하는지 감을 잡아보자.\n이전 글에서 natlist를 뒤집어주는 rev함수를 정의했다. 편의를 위해 아래에 코드를 다시 가져왔다.\nFixpoint rev (l : natlist) : natlist := match l with | nil =\u0026gt; nil | h::t =\u0026gt; rev t ++ [h] end. 증명하고자 하는 정리(성질)는 다음과 같다.\n\\( \\forall l:natlist,\\,\\mathrm\\{length\\}\\,(\\mathrm\\{rev\\}\\,l) = \\mathrm\\{length\\}\\,l \\)\n그런데 지금까지 배운 것들을 이용해 증명을 시도하면 도중에 막히는 부분을 마주하게 된다.\nTheorem rev_length : forall l:natlist, length (rev l) = length l. Proof. intros l. induction l as [| n l\u0026#39; IH]. - (* l = [] *) reflexivity. - (* l = n::l\u0026#39; *) simpl. rewrite \u0026lt;- IH. Abort. 이 시점의 subgoal을 살펴보자.\n1 subgoal n : nat l\u0026#39; : natlist IH : length (rev l\u0026#39;) = length l\u0026#39; ______________________________________(1/1) length (rev l\u0026#39; ++ [n]) = S (length (rev l\u0026#39;)) 이 subgoal을 해결하기 위해서는 length 와 ++ 연산(app) 사이의 연관관계를 이용할 수 있어야 한다. 그런데 현재로서는 이에 대해 별다른 유의미한 성질을 가지고 있지 않다. 그렇다면 유의미한 성질을 찾아 증명을 추가하자.\n직관적으로 다음 성질을 생각해 볼 수 있다. 증명은 induction을 이용하면 비교적 간단하다.\nTheorem app_length : forall (l1 l2 : natlist), length (l1 ++ l2) = (length l1) + (length l2). Proof. intros l1 l2. induction l1 as [| n l1\u0026#39; IH]. - (* l1 = [] *) reflexivity. - (* l1 = n::l1\u0026#39; *) simpl. rewrite -\u0026gt; IH. reflexivity. Qed. 이제 이 성질을 이용해 rev_length를 다시 증명해보자.\nTheorem rev_length : forall l : natlist, length (rev l) = length l. Proof. intros l. induction l as [| n l\u0026#39; IH1]. - (* l = [] *) reflexivity. - (* l = n::l\u0026#39; *) simpl. rewrite -\u0026gt; app_length, plus_comm. simpl. rewrite -\u0026gt; IH1. reflexivity. Qed. 먼저 app_length 를 사용해 앞서 막혔던 subgoal인 length (rev l\u0026rsquo; ++ [n]) = S (length (rev l\u0026rsquo;)) 을 length (rev l\u0026rsquo;) + length [n] = S (length (rev l\u0026rsquo;)) 로 바꿀 수 있고, length [n]은 1이므로 subgoal은 다시 **length (rev l\u0026rsquo;) + 1 = S (length (rev l\u0026rsquo;))**이 된다. 이제 nat에서의 +연산은 교환법칙을 만족한다는 사실(plus_comm)과 induction hypothesis를 이용하면 나머지는 쉽게 증명할 수 있다.\nSearch 그런데 app_length나 plus_comm의 경우 지난 글들에서 증명해본 정리들이기 때문에 어렵지 않게 내용을 다시 떠올리고 증명에 이용할 수 있다. 그렇다면 작성한 지 오래되었거나 다른 사람이 만들어 둔 라이브러리를 가져온다면 그 안에 있는 내용들을 전부 알고 있어야 할까? 그래서는 의미가 없다. Coq에서는 Search를 통해 사용할 수 있는 정리들에 대한 검색을 지원한다. 이해를 돕기 위해 plus 를 검색해봤다.\n오른쪽 아래에서 검색 결과를 확인할 수 있다.\nOptions 잠시 natlist의 기본적인 함수인 head(hd)와 함께, 리스트의 n번째 원소를 가져오는 get_nth 를 작성해보자. 이때 \u0026rsquo;n번째\u0026rsquo;는 0-based index를 의미한다.\nDefinition hd (l:natlist) : nat := match l with | nil =\u0026gt; O | h::t =\u0026gt; h end. Fixpoint get_nth (l:natlist) (n:nat) : nat := match l with | nil =\u0026gt; O | h::t =\u0026gt; match n with | O =\u0026gt; h | S n\u0026#39; =\u0026gt; get_nth t n\u0026#39; end end. 위 두 함수는 같은 문제점을 가진다. 아래 경우를 생각해 보자.\n\u0026gt;\u0026gt; Compute get_nth [1;4;2;0;3;5] 3. 0 : nat \u0026gt;\u0026gt; Compute get_nth [1;4;2] 5. 0 : nat 값이 존재하지 않는 경우 기본값으로 0을 반환하도록 설정했기 때문에, \u0026ldquo;정말 해당 자리에 0이 있는 경우\u0026quot;와 \u0026ldquo;값이 존재하지 않는 경우\u0026quot;를 구분하지 못한다. 0이 아닌 다른 어떤 값을 기본값으로 정해도 비슷한 문제가 발생한다. 이 문제를 해결하기 위해 단순히 nat의 정의에 Null을 추가하는 방법을 떠올려볼 수 있다.\nInductive nullable_nat : Type := | NULL | O | S (n : nat). 0과 Null을 구분할 수 있지만 어딘가 석연치 않다. 군더더기가 붙은 느낌은 둘째치고 S (S NULL) 을 비롯해 의도하지 않은 nat 요소들이 만들어질 수 있다는 새로운 문제가 생겼다. 그렇다면 어떻게 해야 할까? 다음과 같이 nat을 한 차례 포장하는 방법을 생각해보자.\nInductive natoption : Type := | None | Some (n : nat). 그러면 get_nth는 natoption을 사용해 다시 쓸 수 있다.\nFixpoint get_nth (l:natlist) (n:nat) : natoption := match l with | nil =\u0026gt; None | h::t =\u0026gt; match n with | O =\u0026gt; Some h | S n\u0026#39; =\u0026gt; get_nth t n\u0026#39; end end. 이제 Some h(의미를 가진 nat 반환값)와 None(의미를 가지지 않는 디폴트값)이 명확히 구분된다. 그런데 상황에 따라 무의미한 경우에 None 대신 기본값을 사용하더라도 natoption이 아닌 nat이 필요한 순간이 올 수 있다. 아래에 작성한 option_elim을 통해 주어진 natoptoin 인자가 유의미한 값인 경우 해당하는 nat으로, None인 경우 디폴트 값(d : nat)으로 변환할 수 있다.\nDefinition option_elim (d : nat) (o : natoption) : nat := match o with | Some n =\u0026gt; n | None =\u0026gt; d end. 이렇게 option을 이용한 방식은 비단 nat뿐만이 아니라 다른 어떤 타입에도 적용시켜 \u0026lsquo;유의미한 값\u0026rsquo;과 \u0026lsquo;무의미한 값\u0026rsquo;을 구분할 수 있다. 이는 하스켈에서 Maybe monad가 동작하는 방식과 정확히 일치한다. 하스켈에서는 이를 다음과 같이 설명하고 있다.\nThe Maybe datatype provides a way to make a safety wrapper around partial functions, that is, functions which can fail to work for a range of arguments. For example, head and tail only work with non-empty lists. Another typical case, which we will explore in this section, are mathematical functions like sqrt and log; (as far as real numbers are concerned) these are only defined for non-negative arguments. (원문)\n모나드가 무엇인지, 어떻게 응용할 수 있는지에 대한 자세한 설명은 이번 글에서 다루는 범위를 벗어난다. 열정적인 독자(만약 있다면)를 위해 유투브 Computerphile 채널에서 모나드에 대해 간략히 설명한 영상을 첨부한다.\n","date":"2019년 2월 11일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch03-2/","section":"Posts","summary":"Reasoning About Lists 리스트에 대한 간단한 정리를 증명해보자.\nTheorem nil_app : forall l:natlist, [] ++ l = l. Proof. reflexivity. Qed. 대상이 natlist라는 점만 제외하면 이전의 forall n:nat, 0+n=n과 같은 형태이다. nat에서와 마찬가지로 natlist도 destruct를 통해 생성규칙에 따라 경우를 나눌 수 있다.\nTheorem tl_length_pred : forall l:natlist, pred (length l) = length (tl l). Proof. intros l. destruct l as [| n l'] eqn:E. - (* l = [] *) reflexivity. - (* l = n::l' *) reflexivity. 마찬가지로 수학적 귀납법(induction tactic) 역시 적용 가능하다. nat에서의 귀납법과 마찬가지로 base step과 induction step으로 이루어져 있으며, 각각\n","title":"[Coq 입문] Ch03. Working with Structured Data (2)","type":"posts"},{"content":"본 챕터에서는 Structured Data, 그중에서도 List에 대해 다룬다.\nPairs of Numbers 지난번 nybble을 정의할 때 언급했듯이, Coq의 생성자(constructor)는 여러 개의 인자를 받을 수 있다.\nInductive natprod : Type := | pair (n1 n2 : nat). 이렇게 정의된 natprod에는 오직 하나의 생성규칙만 존재한다.(ex: pair 3 5)\n이제 몇 가지 함수와 함께 새로운 notation을 정의해보자. fst, snd는 각각 natprod의 첫 번째, 두 번째 인자를 추출하는 함수가 된다.\nDefinition fst (p : natprod) : nat := match p with | pair x y =\u0026gt; x end. Definition snd (p : natprod) : nat := match p with | pair x y =\u0026gt; y end. Notation \u0026#34;( x , y )\u0026#34; := (pair x y). 이렇게 정의된 notation은 함수 정의에서도 사용할 수 있다. fst, snd를 (x,y) notation을 사용해 다시 작성해보자.\nDefinition fst2 (p : natprod) : nat := match p with | (x,y) =\u0026gt; x end. Definition snd2 (p : natprod) : nat := match p with | (x,y) =\u0026gt; y end. 작성이 끝나면 Compute를 통해 잘 동작하는지 확인해볼 수 있다.\n\u0026gt;\u0026gt; Compute fst (pair 3 5). (* fst2도 동일 *) 3 : nat \u0026gt;\u0026gt; Compute snd (3,5). (* snd2도 동일 *) 5 : nat 여기서 fst2, snd2를 정의할 때 사용한 패턴 매칭은 얼핏 x, y 두 요소를 매칭하는 다중 매칭처럼 보이지만 실제로는 pair x y 하나에 대응되는 단일 매칭임에 유의해야 한다. (C++ 의 pair STL 기준으로 void f(int x, int y) 와 void f(pair\u0026lt;int,int\u0026gt; p) 의 차이라고 이해하면 된다.)\n다음으로 natprod에 대한 간단한 명제를 증명해보자.\nTheorem surjective_pairing : forall (n m : nat), (n,m) = (fst (n,m), snd(n,m)). Proof. reflexivity. Qed. 그런데 정리하고자 하는 명제를 서술할 때 (x,y) notation을 사용하지 않고 바로 natprod를 이용해 작성하면 reflexivity tactic이 이를 인식하지 못한다.\nTheorem surjective_pairing2 : forall (p : natprod), p = (fst p, snd p). Proof. reflexivity. (* ERROR *) 이런 경우 p : natprod가 ( (n : nat), (m : nat) ) 형태임을 인식시켜줘야 한다. 이는 destruct tactic을 사용해 해결할 수 있다. 이번 기회에 destruct의 정확한 기능을 알아보자.\nTheorem surjective_pairing2 : forall (p : natprod), p = (fst p, snd p). Proof. intros p. destruct p as [n m]. reflexivity. Qed. destruct p as [n m].을 적용하는 순간 subgoal의 p가 (n,m)으로 바뀌고, reflexivity가 적용 가능해지는 것을 볼 수 있다. 이전 글에서 destruct tactic을 단순히 \u0026ldquo;경우를 나누는\u0026rdquo; 용법이라고 설명했는데, 구체적으로는 가능한 생성규칙별로 경우를 나누고, 인자가 있는 경우 [ ] 에 제시된 순서대로 대응시키는 기능이라고 이해할 수 있다. 따라서, natprod는 유일한 생성규칙이 pair (n1 n2 : nat)이었으므로 2개의 인자명(n,m)을 받아 destruct p as [n m]이, nat의 경우 2개의 생성규칙별로 [ ](O는 0개의 인자)과 [n\u0026rsquo;] (S (n:nat)은 1개의 인자)이 되어 전체 destruct n as [ | n']이 되는 것이다.\nLists of Numbers pair에서 한발 더 나아가, 임의 개수의 원소를 가질 수 있는 list를 만들어보자. 원문의 표현을 빌리면 \u0026ldquo;A list is either the empty list or else a pair of a number and another list.\u0026ldquo;라고 한다.\nInductive natlist : Type := | nil | cons (n : nat) (l : natlist). 이 생성규칙으로 만들어지는 예를 하나 살펴보자.\n\u0026gt;\u0026gt; Check (cons 1 (cons 2 (cons 3 nil))). : natlist 구조는 이해했지만 매번 이렇게 작성하기는 너무 길고 번거롭다. 좀더 편리하게 사용할 수 있도록 새로운 notation을 정의하자.\nNotation \u0026#34;x :: l\u0026#34; := (cons x l) (at level 60, right associativity). Notation \u0026#34;[ ]\u0026#34; := nil. Notation \u0026#34;[ x ; .. ; y ]\u0026#34; := (cons x .. (cons y nil) ..). 그러면 다음 Definition들은 모두 동일한 의미를 지닌다.\nDefinition mylist1 := (cons 1 (cons 2 (cons 3 (cons 4 nil)))). Definition mylist2 := 1::(2::(3::(4::nil))). Definition mylist3 := 1::2::3::4::nil (* 1::2::3::4::[] *). Definition mylist4 := [1;2;3;4]. Definition mylist5 := 1::[2;3;4]. Definition mylist6 := 1::2::[3;4]. Functions on Lists 이제 natlist를 다루는 함수를 만들 수 있다. 하스켈 프로그래밍 언어를 사용해봤다면 대부분 익숙한 함수들일 것이다.\nHead \u0026amp; Tail 먼저 pair의 fst, snd와 유사하게 hd(head), tl(tail) 함수를 정의하자. 이때 hd nil에 기본값(default)으로 O을 정의해줬음에 유의하자.\nDefinition hd (l:natlist) : nat := match l with | nil =\u0026gt; O | h::t =\u0026gt; h end. Definition tl (l:natlist) : natlist := match l with | nil =\u0026gt; nil | h::t =\u0026gt; t end. Repeat n, count를 받아 count개 만큼의 n을 원소로 가지는 natlist를 생성한다.\nFixpoint repeat (n count : nat) : natlist := match count with | O =\u0026gt; nil | S count\u0026#39; =\u0026gt; n :: (repeat n count\u0026#39;) end. \u0026gt;\u0026gt; Compute repeat 5 3. [5; 5; 5] : natlist Length 주어진 리스트의 길이를 계산한다.\nFixpoint length (l : natlist) : nat := match l with | nil =\u0026gt; O | h::t =\u0026gt; S (length t) end. \u0026gt;\u0026gt; length [1;2;3;4;5]. 5 : nat Append 주어진 두 리스트를 연결(concatenate)한다.\nFixpoint app (l1 l2 : natlist) : natlist := match l1 with | nil =\u0026gt; l2 | h::t =\u0026gt; h::(app t l2) end. \u0026gt;\u0026gt; Compute app [1;3;5] [2;4]. [1; 3; 5; 2; 4] : natlist append는 왠지 쓸모가 많아보인다. infix notation을 추가하자.\nNotation \u0026#34;x ++ y\u0026#34; := (app x y) (right associativity, at level 60). p.s. 파이썬을 사용해본 입장에서 리스트 더하기가 concatenate 연산인 건 자연스러워도, append함수의 기능을 고려해봤을 때 app이라는 이름 대신 concat 이 더 자연스러워 보인다. 하지만 Coq에서 제공하는 함수 중 범용 타입(polymorphic) 리스트에 동일한 기능을 제공하는 함수가 이미 app이라는 이름으로 존재하므로 통일성을 위해 해당 명칭을 그대로 사용했다.\nReverse 주어진 리스트를 뒤집는다.\nFixpoint rev (l : natlist) : natlist := match l with | nil =\u0026gt; nil | h::t =\u0026gt; rev t ++ [h] end. \u0026gt;\u0026gt; Compute rev [1;2;3;4;5]. [5; 4; 3; 2; 1] : natlist Bag(multiset) 리스트를 이용해 multiset(동일 원소가 여러 개인 경우를 허용하는 집합)을 구현할 수 있다.\nDefinition bag := natlist. 연습삼아 주어진 bag 안에 특정 원소가 얼마나 포함되어있는지를 계산하는 count 를 작성해보자. 이를 위해 먼저 이전에 작성했던 is_equal을 가져와야 한다. 다중 매칭을 이용한 짧은 버전을 아래에 다시 작성했다.\nFixpoint is_equal (n m : nat) : bool := match n, m with | O, O =\u0026gt; true | S n\u0026#39;, S m\u0026#39; =\u0026gt; is_equal n\u0026#39; m\u0026#39; | _, _ =\u0026gt; false end. Notation \u0026#34;x =? y\u0026#34; := (is_equal x y) (at level 70). 이제 count 함수를 마저 작성하자.\nFixpoint count (v:nat) (b:bag) : nat := match b with | nil =\u0026gt; O | h::t =\u0026gt; match (v =? h) with | true =\u0026gt; S (count v t) | false =\u0026gt; (count v t) end end. \u0026gt;\u0026gt; count 3 [1;2;3;4;5;4;3;2]. 2 : nat \u0026gt;\u0026gt; count 5 [1;2;3;4]. O : nat \u0026gt;\u0026gt; count 2 [2;2;2;2;2]. 5 : nat 그런데 count 함수의 정의에서 두 번째 match...with을 보면 bool 타입에서 true / false 2가지 경우만을 매칭시킨다. 이런 경우 굳이 match까지 쓸 필요가 있을까? Coq 역시 일반적인 프로그래밍 언어에서와 마찬가지로 if 가 존재한다. 이를 이용하면 count 함수는 다음과 같이 다시 쓸 수 있다.\nFixpoint count2 (v:nat) (b:bag) : nat := match b with | nil =\u0026gt; O | h::t =\u0026gt; if (v =? h) then S (count2 v t) else (count2 v t) end. 그런데 true, false는 앞서 bool 타입을 정의할 때 만들어준 생성자일뿐 여기에 참/거짓이라는 의미를 덧붙여준 기억은 없다. 사실 굳이 bool이 아니어도 Coq에서는 어떤 Inductive하게 정의된 타입이 2개의 생성자를 가지기만 하면 if를 사용할 수 있으며, 이때 if에 주어진 표현식이 첫 번째 생성자와 매칭될 경우 then 다음 부분이, 두 번째 생성자와 매칭될 경우 else 다음 부분이 최종 결과가 된다.\n이번 글에서는 nat들의 리스트, natlist 타입을 정의하는 방법과 이를 다루는 함수들을 작성해봤다. 다음 글에서는 지금까지 정의한 함수들의 성질에 대해 논증(reasoning)을 진행해보자.\n","date":"2019년 2월 9일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch03-1/","section":"Posts","summary":"본 챕터에서는 Structured Data, 그중에서도 List에 대해 다룬다.\nPairs of Numbers 지난번 nybble을 정의할 때 언급했듯이, Coq의 생성자(constructor)는 여러 개의 인자를 받을 수 있다.\nInductive natprod : Type := | pair (n1 n2 : nat). 이렇게 정의된 natprod에는 오직 하나의 생성규칙만 존재한다.(ex: pair 3 5)\n","title":"[Coq 입문] Ch03. Working with Structured Data (1)","type":"posts"},{"content":" Proof by Case Analysis 다뤄야 하는 대상이 복잡해지면 simpl이나 rewrite만으로는 충분하지 않은 경우가 있다. 잠시 예전 글에서 정의했던 is_equal을 가져오면서 여기에 새로운 Notation을 추가하자.\nFixpoint is_equal (n m : nat) : bool := match n with | O =\u0026gt; match m with | O =\u0026gt; true | S m\u0026#39; =\u0026gt; false end | S n\u0026#39; =\u0026gt; match m with | O =\u0026gt; false | S m\u0026#39; =\u0026gt; is_equal n\u0026#39; m\u0026#39; end end. Notation \u0026#34;x =? y\u0026#34; := (eqb x y) (at level 70) : nat_scope. 이제 거의 비슷하게 생긴 다음 두 명제를 증명해보자.\n\\( \\forall n : nat, 1+n \\neq 0 \\) \\( \\forall n : nat, n+1 \\neq 0 \\) 먼저 1+n은 plus와 notation의 정의로부터 S n으로 simplified될 수 있고, 이를 이용하면 위의 명제는 simpl. 로 증명이 끝난다.\nTheorem O_cannot_be_1_n : forall n:nat, (1+n =? 0) = false. Proof. intros n. simpl. reflexivity. Qed. 하지만 n+1은 simpl.을 시도해도 아무런 변화가 일어나지 않는다. 그런데 reflexivity.를 적용하(려고 시도하)면 Unable to unify \u0026ldquo;false\u0026rdquo; with \u0026ldquo;n + 1 =? 0\u0026rdquo; 라는 오류를 볼 수 있다. 1+n과 달리 n+1은 n이 O인지 S n' 형태인지에 따라 결과값이 달라지기 때문에 Coq가 한번에 처리할 수 있는 역량을 벗어난다. 그렇다면 경우를 나누어서 처리하는 방법을 떠올릴 수 있다. destruct tactic이 그 역할을 담당한다.\nTheorem O_cannot_be_n_1 : forall n:nat, n+1 =? 0 = false. intros n. simpl. destruct n. 여기까지 적용했다면 각 경우가 나뉘어지면서 2개의 subgoal이 나타난다.\n2 subgoals ______________________________________(1/2) (0 + 1 =? 0) = false ______________________________________(2/2) (S n + 1 =? 0) = false 이어서 증명을 진행해도 되지만, Coq에서는 각 subgoal에 대한 증명임을 명확히 하기 위해 보조 마크를 사용할 수 있다 (bullet을 이용한 개조식 서술을 생각하면 된다). 이번 경우 각 subgoal은 reflexivity.로 바로 증명이 끝난다.\nTheorem O_cannot_be_n_1 : forall n:nat, n+1 =? 0 = false. intros n. simpl. destruct n as [ | n\u0026#39;] eqn:E. - reflexivity. - reflexivity. Qed. 여기서 as [ | n']은 n을 경우에 따라 나누면서 만들어지는 새로운 변수에 이름을 제공하기 위해 사용했다 (O는 새로운 변수가 필요하지 않지만 S xxx 의 경우는 xxx 자리에 사용할 새로운 변수가 필요). as를 사용하지 않아도 Coq에서 자동으로 이름을 제공하지만, 의미를 명확히 하기 위해 가능하면 직접 이름을 제시하는 것이 좋다.\n마찬가지로 eqn:E 역시 추가적인 가독성을 제공한다. 조금 전 n을 경우에 따라 나누었지만 그게 n = O인 경우와, n = S n'인 2가지라는 사실을 알기 위해서는 (디테일을 기억하고 있지 않다면) nat의 정의까지 다시 올라가야 한다.destruct tactic을 사용할 때 eqn:(name)을 덧붙이면 각 경우를 가정으로 표기해 현재 다루고 있는 subgoal이 어떤 경우에 해당하는지를 조금 더 친절하게 알려준다. subgoals 창에 나타나는 메시지를 보면 이를 좀더 잘 이해할 수 있다.\n1 subgoal n : nat E : n = 0 __________________________(1/1) (0 + 1 =? 0) = false 1 subgoal n, n' : nat E : n = S n' __________________________(1/1) (S n' + 1 =? 0) = false 나뉘어진 subgoal에 대해 증명을 진행하면서, -를 수행하는 순간 subgoal 창에 나타난 목표가 하나로 한정되는 것을 볼 수 있다. -나 +, *같은 bullet이 일종의 scope 한정자 역할을 하는 셈이다. scope의 활용을 좀더 이해하기 위해 다른 예제를 살펴보자.\nTheorem andb_commutative : forall b c, andb b c = andb c b. Proof. intros b c. destruct b. eqn:Eb. - destruct c eqn:Ec. + reflexivity. + reflexivity. - destruct c eqn:Ec. + reflexivity. + reflexivity. Qed. 중첩이 조금 더 깊게(nested) 이루어지는 상황을 대비해 {, }을 사용해서도 scope 표기가 가능하다.\nTheorem andb_commutative\u0026#39; : ∀b c, andb b c = andb c b. Proof. intros b c. destruct b eqn:Eb. { destruct c eqn:Ec. { reflexivity. } { reflexivity. } } { destruct c eqn:Ec. { reflexivity. } { reflexivity. } } Qed. Proof by Induction 다음으로 이전에 다뤘던 plus_0_n과 유사하지만 다른 tactic을 사용해야 하는 경우를 알아보자.\nTheorem plus_n_0 : forall n:nat, n+0=n. Proof. intros n. (* TODO *) Qed. simpl.은 아무런 변화가 없고, destruct n as [|n']을 적용하자 무언가 진척이 있어보인다.\n2 subgoals ______________________________________(1/2) 0 + 0 = 0 ______________________________________(2/2) S n\u0026#39; + 0 = S n\u0026#39; 첫 번째는 reflexivity로 간단히 해결 가능하지만 두 번째 subgoal에 simpl.을 적용하면 다음과 같이 변한다.\n1 subgoal n\u0026#39; : nat ______________________________________(1/1) S (n\u0026#39; + 0) = S n\u0026#39; 뭐지 데자뷰인가 다시 destruct를 적용해 시도해봤지만 같은 패턴이 반복된다.\n그러면 이 무한한 경우의 수를 모두 하나씩 보여야 할까? 이는 애초에 불가능하다. 그렇다면 어떤 접근을 사용할 수 있을까.\n\\( \\forall P(-)((P(0)\\land \\forall n (P(n) \\implies P(n+1))) \\implies \\forall n P(n)) \\)\n다루고자 하는 대상이 자연수(nat)이므로 수학적 귀납법을 생각해볼 수 있다. 위 수식에 나타나 있듯이 자연수에 대한 성질 \\(P(n)\\)에 대해,\n\\(P(0)\\)이 성립하고 \\(\\forall n, P(n) \\implies P(n+1)\\) 이면 모든 자연수 n에 대해 P(n)이 성립, 다시 말해 \\(\\forall n, P(n)\\) 이 된다.\n이전 destruct때와 유사하게induction tactic을 적용하면 2개의 subgoal이 나타난다. 여기서 n'은 destruct때와 마찬가지 의미를, IH는 Induction Hypothesis에 IH라는 이름을 붙였음을 뜻한다.\ninduction n as [|n\u0026#39; IH]. 첫 번째 subgoal(0+0=0)을 reflexivity로 끝내고 2번째 subgoal인 induction step에 집중하자. 이때 앞서 이름붙인 IH를 사용할 수 있다.\n1 subgoal n\u0026#39; : nat IH : n\u0026#39; + 0 = n\u0026#39; ______________________________________(1/1) S n\u0026#39; + 0 = S n\u0026#39; simpl.을 적용하면 goal은 S (n' + 0) = S n'으로 변하고, 여기에 rewrite IH를 적용하면 증명을 끝낼 수 있다. 전체 코드는 아래와 같다.\nTheorem plus_n_0 : forall n:nat, n+0 = n. Proof. intros n. induction n as [|n\u0026#39; IH]. - (* 0 + 0 = 0 *) reflexivity. - (* S n\u0026#39; + 0 = S n\u0026#39; *) simpl. rewrite -\u0026gt; IH. reflexivity. Qed. Proofs within Proofs 앞서 Induction Step을 보일 때 중간에 전제조건으로 주어진 IH를 이용해 손쉽게(?) 증명을 마쳤다. 이처럼 증명 도중에 사용할 수 있는 정리들이 있다면 간결함과 가독성을 높이는 데 큰 도움이 된다. 바깥에서 정의된 Lemma나 Corollary 등을 사용할 수도 있지만, 그러기엔 사소한 경우 assert를 이용해 증명 도중에 작은 보조정리들을 추가할 수 있다.\nTheorem mult_0_plus : forall n m : nat, (0 + n) * m = n * m. Proof. intros n m. assert (H: 0 + n = n). assert 를 추가하면 2개의 subgoal이 나타난다. 첫 번째는 assert로 추가된 명제, 두 번째는 원래 목표였던 subgoal이다. assert로 추가한 내용을 증명하고 나면 induction에서와 마찬가지로 이를 이용할 수 있다.\n지금까지 Coq의 기초적인 tactic들에 대해 알아보았다.\n","date":"2019년 2월 3일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch02-2/","section":"Posts","summary":"Proof by Case Analysis 다뤄야 하는 대상이 복잡해지면 simpl이나 rewrite만으로는 충분하지 않은 경우가 있다. 잠시 예전 글에서 정의했던 is_equal을 가져오면서 여기에 새로운 Notation을 추가하자.\nFixpoint is_equal (n m : nat) : bool := match n with | O =\u003e match m with | O =\u003e true | S m' =\u003e false end | S n' =\u003e match m with | O =\u003e false | S m' =\u003e is_equal n' m' end end. Notation \"x =? y\" := (eqb x y) (at level 70) : nat_scope. 이제 거의 비슷하게 생긴 다음 두 명제를 증명해보자.\n","title":"[Coq 입문] Ch02. Proof by tactics (2)","type":"posts"},{"content":"이제 Coq를 이용해 간단한 증명을 직접 작성해보자.\nNotation 본론을 시작하기에 앞서 전에 만들었던 plus, mult 에 infix notation을 정의하자.\nNotation \u0026#34;x + y\u0026#34; := (plus x y) (at level 50, left associativity) : nat_scope. Notation \u0026#34;x * y\u0026#34; := (mult x y) (at level 40, left associativity) : nat_scope. 이전에 bool에 대해 x \u0026amp;\u0026amp; y 를 정의하던 때보다 뭔가 이것저것 늘어났다. prefix, postfix notation과는 달리 infix notation에서는 모호한(ambiguous) 문장이 생길 수 있다. 일반적으로 (x+y*z)라는 표현식을 쓸 때 우리는 ((x+y)*z)가 아닌 (x+(y*z))를 의도한다. 위 코드에서의 역할을 간단히 요약하면\nlevel (0 ~ 100 사이의 정수) (x+y)의 level이 50, (x*y)의 level이 40으로, (x+y*z)가 (x+(y*z))로 해석되도록 한다. associativity (left associativity) (x+y+z)가 (x+(y+z))가 아닌 ((x+y)+z)로 해석되도록 한다. 가 된다. 사실 여기까지는 Coq에 이미 정의되어 있으므로 굳이 새로 타이핑할 필요는 없다\nProof by simplification 이제 Coq로 수학적 정리를 표현해보자. Coq에서 정리는 다음 형태를 가진다. (Theorem, Lemma, Corollary, Example 모두 동일한 형태인데 이것들이 어떻게 다른지는 아직 잘 모르겠다.)\nTheorem (name) : (statement). Proof. (tactics) Qed. 그럼 앞서 정의된(혹은 이미 Coq에 정의되어 있는) nat 타입에 대해 다음 문장을 생각해보자\nO은 nat에서 덧셈에 대한 왼쪽 항등원이다. (i.e. \\( \\forall n:nat, 0+n=n \\) )\n참고를 위해 plus의 정의를 다시 가져왔다.\nFixpoint plus (n m : nat) := match n with | O =\u0026gt; m | S n\u0026#39; =\u0026gt; S (plus n\u0026#39; m) end. 먼저 증명하고자 하는 명제를 서술하자. 이름은 plus_0_n으로 지었다.\nTheorem plus_0_n : forall n:nat, 0 + n = n. 다음으로 증명 시작을 나타내는 Proof.를 쓴다.\nProof. 그럼 (CoqIDE 기준) 오른쪽 위에 subgoal이 나타난 것을 확인할 수 있다.\nsubgoal에서 n 앞에 forall 한정자(quantifier)가 붙어있다. 종이에 펜으로 증명을 시작할 때와 마찬가지로 \u0026ldquo;어떤 자연수 n에 대해\u0026rdquo; 를 쓰면서 forall을 떼놓고 생각하자. Coq에서는 intros tactic이 그 역할을 수행한다.\nintros n. subgoal이 어떻게 변했는지 살펴보자. 다음으로 simpl. 을 적용하면 subgoal 창의 메시지는 다음과 같이 변한다.\n(simpl. 적용 이전)\n1 subgoal n : nat ______________________________________(1/1) 0 + n = n (simpl. 적용 이후)\n1 subgoal n : nat ______________________________________(1/1) n = n 등호 왼쪽이 바뀌었다. Coq에서 0 + n, 다시 말해 plus O n을 인식하고 plus의 정의에 따라 이를 n으로 단순화시킨 것이다. (plus n m에서 n이 O이면 match...with 에 따라 결과는 m이 된다. 이해가 잘 가지 않는다면 plus의 정의를 다시 살펴보자)\n마지막으로 이정도는 맞다고 자동으로 넘어가줘도 될거같지만 reflexivity.를 이용하자. 그러면 n=n임이 받아들여지면서 \u0026ldquo;No more subgoals.\u0026rdquo; 라는 메시지가 보인다. 이제 Qed.를 통해 증명을 끝낼 수 있다.\n그런데 위 코드에서 simpl.을 지워도 Coq는 정상적으로 동작하고 plus_0_n이 증명되었음을 받아들인다. 사실 reflexivity.는 simpl.과 유사한 방식으로 치환을 수행할 수 있으며, 실제로 simpl.보다 조금 더 넓은 범위를 수행한다. 그런데 왜 굳이 simpl.을 썼을까?\n이는 두 tactic의 목적 차이에서 나온다. reflexivity는 주로 a=b 형태의 subgoal을 만족시키는 데 사용하고, 치환/확장을 수행해 양변이 일치하면 subgoal을 달성시킨다. 반면 simpl.은 subgoal 자체를 달성시키려는 기능 없이, 사용자의 이해를 돕는 선까지만 치환/확장을 수행한다고 이해할 수 있다.\nProof by rewriting 이제 다음 정리를 증명해보자.\n\\(\\forall n\\,m\\,:\\,nat,\\,n=m\\rightarrow n+n=m+m \\)\n이전에 설명된 내용을 통해 여기까지는 쉽게 작성할 수 있다.\nTheorem plus_same : forall n m:nat, n=m -\u0026gt; n+n=m+m. Proof. intros n m. 그런데 simpl.을 적용해도 아무런 차이가 나타나지 않는다. 이전의 0+n은 plus의 정의에서 바로 치환이 가능했지만 이번엔 딱히 변화가 없다. 다시 종이와 펜을 들고있다고 상상해보자. \\(P \\rightarrow Q\\) 를 증명할 때 \\(P \\rightarrow Q\\)임을 바로 보이는 것과, 일단 \\(P\\)를 참이라고 가정하고 \\(Q\\)가 참임을 보이는 것 중 어느 쪽이 편리할까? 후자의 방법을 사용하기 위해 n = m을 전제조건으로 포함시키자.\nintros H. 그러면 subgoal 창은 다음과 같이 변한다.\n1 subgoal n, m : nat H : n = m ______________________________________(1/1) n + n = m + m 전제조건 H에서 n=m이라고 했으므로 n을 m으로 바꿔써도 되지 않을까? Coq에서는 이를 수행하는 rewrite tactic이 있다.\nrewrite -\u0026gt; H. 이를 적용하면 등호 왼쪽의 n이 m으로 바뀌면서 subgoal이 아래처럼 변한다.\n1 subgoal n, m : nat H : n = m ______________________________________(1/1) m + m = m + m 이제 등호를 기준으로 양쪽이 일치하므로 reflexivity.를 이용해 증명을 끝낼 수 있다. Qed.까지 끝났다면 rewrite -\u0026gt; H.를 rewrite \u0026lt;- H.로 바꿔보고 rewrite가 어떤 식으로 동작하는지 감을 잡아보자.\n","date":"2019년 2월 1일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch02-1/","section":"Posts","summary":"이제 Coq를 이용해 간단한 증명을 직접 작성해보자.\nNotation 본론을 시작하기에 앞서 전에 만들었던 plus, mult 에 infix notation을 정의하자.\nNotation \"x + y\" := (plus x y) (at level 50, left associativity) : nat_scope. Notation \"x * y\" := (mult x y) (at level 40, left associativity) : nat_scope. 이전에 bool에 대해 x \u0026\u0026 y 를 정의하던 때보다 뭔가 이것저것 늘어났다. prefix, postfix notation과는 달리 infix notation에서는 모호한(ambiguous) 문장이 생길 수 있다. 일반적으로 (x+y*z)라는 표현식을 쓸 때 우리는 ((x+y)*z)가 아닌 (x+(y*z))를 의도한다. 위 코드에서의 역할을 간단히 요약하면\n","title":"[Coq 입문] Ch02. Proof by tactics (1)","type":"posts"},{"content":" Define Numbers 이전 글에서는 day, bool, color 와 같이 원소의 개수가 유한한 타입, 다시 말해 타입 \\(T\\)에 대해 \\(\\{x \\| x \\mathrm\\{\\,has\\,type\\,\\}T\\} \\)가 유한집합인 경우만을 다뤘다. 무한한 원소를 가지는 타입을 정의하려면 어떻게 해야 할까? 집합론에서와 마찬가지로 자연수에서 시작해보자. 페아노 공리계(Peano axioms)에서 필요한 부분을 빌려오자. 크게 중요하지 않은 공리들은 생략했다\n1. 0 is a natural number.\n5. For every natural number n, S(n) is a natural number.\n이제 이 정의를 그대로 Coq로 옮겨보자.\nInductive nat : Type := | O | S (n : nat). 이제 O, S 0, S (S 0), S (S (S 0))으로 이어지는 모든 패턴은 nat 타입이 된다. 위 코드는 실제 Coq에서 자연수 nat이 정의되는 방식과 동일하다. 정의를 살펴보면 O, S 0, S (S 0), S (S (S 0)), \u0026hellip;이 각각 0, 1, 2, 3, \u0026hellip;에 대응된다는 사실을 쉽게 눈치챌 수 있다. Coq에서는 이를 반영해 S (S (S ...)) 대신 알아보기 편한 syntactic sugar를 제공한다. 이를 적용하기 위해 앞서 작성한 nat 정의를 지우고 Check로 확인해보자.\n\u0026gt;\u0026gt; Check (S (S (S (S O)))). 4 : nat 이제 nat 타입을 받는 함수를 만들어보자. predecessor 함수 pred는 다음과 같이 정의할 수 있다.\nDefinition pred (n : nat) := match n with | O =\u0026gt; 0 | S n\u0026#39; =\u0026gt; n\u0026#39; end. 0의 predecessor는 0으로 정의했음에 유의하자(결과물도 nat 범위 안에 있으려면 -1은 나올 수 없다). 마찬가지로 minusTwo 함수를 만들 수 있다.\nDefinition minusTwo (n : nat) := match n with | O =\u0026gt; O | S O =\u0026gt; O | S (S n\u0026#39;) =\u0026gt; n\u0026#39; end. Compute를 통해 함수가 의도한 대로 만들어졌는지 확인하자.\n\u0026gt;\u0026gt; Compute (pred 4). 3 : nat \u0026gt;\u0026gt; Compute (minusTwo 4). 2 : nat \u0026gt;\u0026gt; Check minusTwo. minusTwo : nat -\u0026gt; nat 여기서 한 가지 짚고 넘어가야 할 사실이 있다. pred, minusTwo, S 모두 Check를 통해 확인해보면 nat -\u0026gt; nat 타입으로 동일하고, S도 나머지 둘과 마찬가지로 Compute가 된다.\n\u0026gt;\u0026gt; Compute (S 3). 4 : nat 하지만 S는 pred, minusTwo와 근본적으로 다르다. 구체적으로 pred는 일종의 **계산규칙(computation rule)**을 가지고 nat 위에서 정의된 함수이고 S는 nat의 정의에서 등장하는 생성자라는 차이를 가진다. pred의 정의에 따라 pred 4는 3으로 simplified될 수 있지만 S 3이 4가 되는 건 Coq에서 제공하는 syntactic sugar일 뿐 simplified가 아니다. 잠시 10진법을 머리에서 지우고 S와 O을 이용한 표기로 돌아가보면 그 차이를 이해할 수 있다.\n\u0026gt;\u0026gt; Compute (pred (S (S (S (S O))))). S (S (S O)) : nat \u0026gt;\u0026gt; Compute (minusTwo (S (S (S (S O))))). S (S O) : nat \u0026gt;\u0026gt; Compute (S (S (S (S O)))). S (S (S (S O))) : nat S는 computation이 아니라는 사실이 좀더 명확하게 보인다. trivial하죠?\nFixpoint 이제 지금까지와는 조금 다른 함수를 정의해보자. n이 짝수인지 확인하려면 어떻게 해야 할까? 아직 곱셈이나 나눗셈은 배우지 않았으므로 다음과 같은 노가다스러운 방법을 떠올릴 수 있다.\n0은 짝수이다. 1은 짝수가 아니다. 그 외의 경우, n의 홀짝성은 n-2 의 홀짝성과 같다. 이를 코드로 옮겨보자.\nDefinition is_even (n : nat) : bool := match n with | O =\u0026gt; true | S O =\u0026gt; false | S (S n\u0026#39;) =\u0026gt; is_even n\u0026#39; end. 그런데 이를 실행시키면 \u0026ldquo;The reference is_even was not found in the current environment.\u0026ldquo;라는 오류를 볼 수 있다. is_even의 정의에서 다시 is_even을 사용한 게 원인으로 Definition이 단순한 패턴 매칭이기 때문에 등장하는 오류이다. 생각해보면 is_even (S (S (S O)))를 is_even (S O)로 바꾼다고 계산이 끝나는 게 아니므로, 함수를 다시 **재귀적(recursive)**으로 적용하는 방법을 찾아야 한다. Coq에서는 Fixpoint가 그 역할을 수행한다.\nFixpoint is_even (n : nat) : bool := match n with | O =\u0026gt; true | S O =\u0026gt; false | S (S n\u0026#39;) =\u0026gt; is_even n\u0026#39; end. 아래 예시를 보면서 Definition과 Fixpoint의 차이에 대해 감을 잡아보자.\nDefinition is_odd (n : nat) : bool := negb (is_even n). 둘 이상의 인자를 가지는 함수도 마찬가지로 정의할 수 있다.\nFixpoint plus (n : nat) (m : nat) : nat := match n with | O =\u0026gt; m | S n\u0026#39; =\u0026gt; S (plus n\u0026#39; m) end. Coq에서는 plus처럼 n과 m이 모두 같은 타입(nat)인 경우 한번에 묶어서 표기가 가능하다.\nFixpoint mult (n m : nat) : nat := match n with | O =\u0026gt; O | S n\u0026#39; =\u0026gt; plus m (mult n\u0026#39; m) end. 이전 글에서 여러 요소를 한번에 매칭시키는 법을 알아봤다 (※ 이전 글의 Tuples 단락 참조). 이 방식 외에도 2개 이상의 변수를 case by case로 나눌 때 match...with을 중첩해 사용할 수 있다. 두 자연수 n, m이 동일한지 판단하는 함수 is_equal을 작성해봤다.\nFixpoint is_equal (n m : nat) : bool := match n with | O =\u0026gt; match m with | O =\u0026gt; true | S m\u0026#39; =\u0026gt; false end | S n\u0026#39; =\u0026gt; match m with | O =\u0026gt; false | S m\u0026#39; =\u0026gt; is_equal n\u0026#39; m\u0026#39; end end. 지금까지 작성한 함수들을 Compute를 통해 확인해보자.\n\u0026gt;\u0026gt; Compute plus 3 5. 8 : nat \u0026gt;\u0026gt; Compute mult 3 5. 15 : nat \u0026gt;\u0026gt; Compute is_equal 2 2. true : bool \u0026gt;\u0026gt; Compute is_equal 3 5. false : bool Fixpoint (extra) CoqIDE를 사용해 실습을 진행했다면 Fixpoint를 사용해 함수를 정의할 때마다 결과창에 눈길을 끄는 문구가 출력됐을 것이다. 아니라면 다시 plus를 예시로 들어보자.\n다음 두 함수를 작성하고 나오는 메시지를 확인하자.\nFixpoint plus (n m : nat) : nat := match n with | O =\u0026gt; m | S n\u0026#39; =\u0026gt; S (plus n\u0026#39; m) end. Fixpoint another_plus (n m : nat) : nat := | match m with | O =\u0026gt; n | S m\u0026#39; =\u0026gt; S (another_plus n m\u0026#39;) end. 올바르게 수행했다면 결과창에서 각각 아래와 같은 메시지를 볼 수 있다.\nplus is defined plus is recursively defined (decreasing on 1st argument)\nanother_plus is defined another_plus is recursively defined (decreasing on 2nd argument)\n앞서 Fixpoint를 이용해 함수를 재귀적으로 정의할 수 있다고 했으므로 xxx is recursively defined 부분은 별로 놀랍지 않다. 그런데 이어진 괄호 안의 문장은 어떤 의미일까?\nCoq에서 함수를 정의할 때 가장 중요한 점은 **\u0026ldquo;모든 함수는 언젠가 종료되어야 한다\u0026rdquo;**라는 사실이다. 다시 말해, 무한 루프를 비롯해 끝나지 않을 가능성이 있는 함수의 정의는 원칙적으로 허용되지 않는다. 이를 위해서는 정의된 함수가 항상 유한 시간 내에 끝나는지 판단하는 방법이 있어야 한다.\n음? 함수가 유한 시간 내에 계산이 끝나는지를 판단이라고 하면 튜링머신에서 다뤘던 정지 문제(Halting Problem)가 떠오른다. 당황스러운 점은 이건 계산 불가능한 문제로 잘 알려져 있다는 사실이다. 그럼 Coq는 대체 어떻게 하는 걸까.\nCoq에서는 항상 종료되는 함수를 보장하기 위해 조금 엄격한 조건을 적용한다. \u0026lt;software foundations\u0026gt;에서는 structural recursion 이라는 말로 이를 설명한다. 즉,\n함수의 인자에 따른 base case가 존재하고 함수의 인자들 중 매 재귀마다 decreasing하는 게 있다면 해당 Fixpoint 정의는 모든 경우에 항상 종료된다고 확신하는 것이다. 이를 벤 다이어그램으로 나타내면 다음과 같다.\n실제로 아래와 같이 정의한 함수 always_zero는 모든 입력에 대해 O를 반환하지만 Coq에서는 오류를 내뱉는다.\n여기서 무한루프의 위험이 있는 계산을 수행할 수 없다와 무한한 요소를 다루는 논리를 펼칠 수 없다 는 다른 의미라는 것을 이해하자. 실제로 계산을 수행하지 않고도 Coq는 충분히 다양한 논리를 표현할 수 있다.\n","date":"2019년 1월 31일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch01-2/","section":"Posts","summary":"Define Numbers 이전 글에서는 day, bool, color 와 같이 원소의 개수가 유한한 타입, 다시 말해 타입 \\(T\\)에 대해 \\(\\{x \\| x \\mathrm\\{\\,has\\,type\\,\\}T\\} \\)가 유한집합인 경우만을 다뤘다. 무한한 원소를 가지는 타입을 정의하려면 어떻게 해야 할까? 집합론에서와 마찬가지로 자연수에서 시작해보자. 페아노 공리계(Peano axioms)에서 필요한 부분을 빌려오자. 크게 중요하지 않은 공리들은 생략했다\n","title":"[Coq 입문] Ch01. Functional Programming in Coq (2)","type":"posts"},{"content":" Introduction 여기서는 Coq에서 사용되는 Galina라는 functional programming language의 기초적인 사용법과, Coq에서 증명에 실제 활용되는 기초적인 tactic에 대해 다룬다.\nData and Functions Days of the Week 먼저 Coq에서 타입을 선언하는 법을 알아보자. 모든 Coq 구문은 .(마침표)로 끝남에 유의하자\nInductive day : Type := | monday | tuesday | wednesday | thursday | friday | saturday | sunday. day 타입에는 monday, tuesday 등의 원소가 있다. 편의상 원소라는 용어를 사용하지만 엄밀히 말해 \u0026lsquo;원소\u0026rsquo;라는 표현보단는 \u0026lsquo;인자 0개를 가지는 생성자\u0026rsquo;로 이해하는 편이 좋다.\n이제 day 타입을 다루는 함수를 하나 정의하자\nDefinition next_weekday (d:day) : day := match d with | monday =\u0026gt; tuesday | tuesday =\u0026gt; wednesday | wednesday =\u0026gt; thursday | thursday =\u0026gt; friday | friday =\u0026gt; monday | saturday =\u0026gt; monday | sunday =\u0026gt; monday end. next_weekday 함수는 day 타입의 인자 d를 받아 day 타입을 반환한다. Compute 를 통해 함수를 실행시켜볼 수 있다.\n공간상의 문제로 앞으로는 간단히 아래처럼 표기하자. \u0026gt;\u0026gt; 가 있는 줄은 입력. 빈 줄 이후에 이어지는 내용은 그에 대한 출력을 의미한다.\n\u0026gt;\u0026gt; Compute (next_weekday (next_weekday friday)). = tuesday : day Example 키워드로 함수의 동작 예시에 이름을 붙여 기록할 수 있다.\nExample test_next_weekday: (next_weekday (next_weekday saturday)) = tuesday. Example도 증명이 필요하다. 아래쪽에 다음을 추가해 test_next_weekday에 대한 증명을 추가할 수 있다.\nProof. simpl. reflexivity. Qed. Proof, Qed, simpl 등 키워드에 대해서는 다시 설명하겠지만 잠시 과거의 잔재를 통해 어떻게 동작하는지 감을 잡을 수 있다.\nBooleans 앞서 day 타입을 정의한 것과 유사하게, 이번엔 bool 타입을 정의해보자. 상식을 거스르지 않고 이번에 만들 bool 타입도 2개의 원소를 가진다.\nInductive bool : Type := | true | false. day 때와 비슷하게 함수를 정의할 수 있다. 2변수 함수를 정의하는 부분에 유의해서 살펴보자. (orb는 andb와 거의 동일하므로 생략)\nDefinition negb (b:bool) : bool := match b with | true =\u0026gt; false | false =\u0026gt; true end. Definition andb (b1:bool) (b2:bool) : bool := match b1 with | true =\u0026gt; b2 | false =\u0026gt; false end. Coq에서는 Notation 키워드를 통해 함수에 새로운 표기를 추가할 수 있다.\nNotation \u0026#34;x \u0026amp;\u0026amp; y\u0026#34; := (andb x y). Notation \u0026#34;x || y\u0026#34; := (orb x y). 그러면 추가된 표기를 이용한 표현이 가능하다.\n\u0026gt;\u0026gt; Compute (true || (false \u0026amp;\u0026amp; true)). true : bool Types 모든 expression은 타입을 가지고 있다. Check 키워드를 통해 기존에 만든 원소들의 타입을 확인해보자.\n\u0026gt;\u0026gt; Check true. true : bool \u0026gt;\u0026gt; Check andb. andb : bool -\u0026gt; bool -\u0026gt; bool New Types from Old 기존에 정의된 타입을 이용해 새로운 타입을 정의할 수 있다. \u0026lsquo;생성자(constructor)\u0026lsquo;라는 용어를 염두에 두고 아래 정의를 살펴보자.\nInductive rgb : Type := | red | green | blue. Inductive color : Type := | black | white | primary (p : rgb). red, green, blue는 rgb타입의 원소이고, black, white는 color타입의 원소가 된다. 또한 primary red는 color 타입의 원소가 된다. 마찬가지로 primary green, primary blue는 color 타입이 되지만, primary true는 color 타입이 아니다.\n이렇게 만들어진 타입에 대해서도 함수를 정의할 수 있으며, 하스켈과 유사하게 _를 더미(dummy)로 하는 패턴 매칭을 사용할 수 있다.\nDefinition isred (c:color) : bool := match c with | black =\u0026gt; false | white =\u0026gt; false | primary red =\u0026gt; true | primary _ =\u0026gt; false end. isred가 color -\u0026gt; bool인 함수이므로 (isred red)는 올바른 expression이 아님에 주의하자. (단, (isred (primary red))는 올바른 expression이다)\nTuples 한 constructor가 여러 인자를 가질 수도, 패턴 매칭에서 여러 인자를 한번에 매칭시킬 수도 있다.\nInductive bit : Type := | B0 | B1. Inductive nybble : Type := | bits (b0 b1 b2 b3 : bit). Definition all_zero (nb:nybble) : bool := match nb with | (bits B0 B0 B0 B0) =\u0026gt; true | (bits _ _ _ _) =\u0026gt; false end. Check, Compute를 통해 생각한 대로 동작하는지 확인해보자.\nModules 지금까지 작성한 모든 코드는 전역(global scope)에 속한다. 단순히 예제를 따라해보는 거라면 몰라도 코드가 복잡해질수록 모든 걸 전역으로 두는 대신 영역을 나눠야 할 필요성이 커진다. 자바의 패키지, C++의 네임스페이스처럼 Coq에서는 모듈을 제공한다.\nModule XXX. (* code *) End XXX. 이렇게 모듈 내부에 정의된 요소들은 모듈 바깥에서 XXX.Y 로 불러올 수 있다.\n","date":"2019년 1월 29일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch01-1/","section":"Posts","summary":"Introduction 여기서는 Coq에서 사용되는 Galina라는 functional programming language의 기초적인 사용법과, Coq에서 증명에 실제 활용되는 기초적인 tactic에 대해 다룬다.\nData and Functions Days of the Week 먼저 Coq에서 타입을 선언하는 법을 알아보자. 모든 Coq 구문은 .(마침표)로 끝남에 유의하자\n","title":"[Coq 입문] Ch01. Functional Programming in Coq (1)","type":"posts"},{"content":"Coq를 공부해보겠다는 막연한 목표와 함께 이런저런 자료를 찾아보고 그만두기를 반복하던 중, DeepSpec Summer School 세미나에서 나온 영상을 발견했다. 유투브에서 찾은 다른 영상들이 4,5년 전 자료였던 반면 DeepSpec에서는 최근인 2017, 2018년까지 꾸준히 학습자료와 세미나 영상이 업로드되고 있다.\n사실 예전 LaTeX을 익힐 때에도 이것저것 찾아보기만 하다가 결국 학교 과제를 LaTeX으로 작성해보고서야 익숙해졌는데, 이번에도 실 사용 없이 공부하는게 얼마나 갈지는 잘 모르겠다.. 자료 초반에 \u0026ldquo;All the core chapters are suitable for both upper-level undergraduate and graduate students.\u0026rdquo; 라는 말이 있던데, 물론 저런 말들이 다 그렇듯이 전부 거짓말이겠지만 이젠 undergraduate 신분이라는 핑계도 사라졌으니 좀더 확실히 짚고 넘어가야겠다는 생각도 든다.\nOverview \u0026lt;Software Foundations\u0026gt; 라는 제목에서 짐작할 수 있듯이, 이런 것들을 하는 주된 목표는 \u0026ldquo;Building reliable software\u0026quot;라고 할 수 있다. 현대 사회에서 소프트웨어의 영향력이나 그만큼 커진 보안의 중요성에 대해서는 굳이 이 글이 아니어도 여러 곳에서 들었을 테니 생략하고, 다음 질문인 \u0026ldquo;그럼 어떻게 하면 reliable한 소프트웨어를 만들 수 있는가\u0026rdquo; 로 넘어가자. 다양한 시각에서 다양한 해결책이 제시되었다.\n소프트웨어를 제작하는 사람을 중심으로 생각 익스트림 프로그래밍, 애자일, 폭포수 모델, ⋯ SW 라이브러리를 디자인하는 방식 제안 MVC모델, publish-subcribe, ⋯ 프로그래밍 언어 자체의 설계철학을 제시 OOP, aspect-oriented, functional, ⋯ 소프트웨어의 성질을 수학적으로 논증(reasoning) SMT solver, Isabelle, Coq, ⋯ 여기서 다루고자 하는 건 마지막 접근방식이다. 특히, \u0026lt;Software Foundations\u0026gt;에서는 크게 다음 3가지를 중점적으로 다룬다.\n프로그램의 성질을 정밀(precise)하게 묘사하기 위한 Logic 엄밀한 논증을 위해 proof assistant를 사용하는 방법 프로그래밍과 논리를 잇는 연결고리로서의 functional programming Proof Assistant 논리학이 CS에 제공하는 게 많아보이지만 의외로 CS가 논리학에 준 도움 또한 존재한다. CTF를 해봤거나 퍼즐 문제를 좋아한다면 한번쯤 들어봤을 SMT solver를 비롯해 다양한 툴이 있으며, 이는 크게 두 부류로 나뉘어진다\nAutomated Theorem Prover 명제를 입력하고 작동시키면 True 혹은 False를 리턴해주고, 필요한 경우 어떻게 그 결과가 나왔는지 근거를 제공한다. 예를 들어 \\( A \\land (B \\lor \\neg C)\\)라는 명제에 True와 함께 A:True, B:True, C:False를 출력하는 식이다. 다룰 수 있는 범위에 다소 제약이 있으며, 모든 경우를 해결해주지는 못한다(ex: ran out of time 에러). SAT solver나 SMT solver, model checker 등이 있다.\nProof Assistant proof assistant는 사람의 개입을 가능하게 해 직관을 더하면서도 증명에서 논증의 단단함은 잃지 않도록 한 일종의 하이브리드 도구라고 할 수 있다. 툴과 사람이 상호작용을 통해 증명을 완성해가며, Isabelle이나 ACL2, Coq 등이 있다.\nCoq 활용 사례 Coq는 현재 전산학과 수학의 여러 분야에 폭넓게 활용되고 있다.\n프로그래밍 언어의 모델링 플랫폼 JavaCard 플랫폼의 보안성을 증명해 CC인증을 받는 데 활용됨 증명된(formally certified) SW나 HW의 개발환경 CompCert(fully-verified C optimizing compiler), CertiKos(fully-verified hypervisor), CertiCrypt 등 dependent type을 이용한 함수형 프로그래밍 환경 제공\nhigher-order logic에서의 proof assistant 4색 정리, Feit-Thompson 정리의 증명 및 검증 ","date":"2019년 1월 28일","externalUrl":null,"permalink":"/blog/SW-Foundations-Ch00/","section":"Posts","summary":"Coq를 공부해보겠다는 막연한 목표와 함께 이런저런 자료를 찾아보고 그만두기를 반복하던 중, DeepSpec Summer School 세미나에서 나온 영상을 발견했다. 유투브에서 찾은 다른 영상들이 4,5년 전 자료였던 반면 DeepSpec에서는 최근인 2017, 2018년까지 꾸준히 학습자료와 세미나 영상이 업로드되고 있다.\n사실 예전 LaTeX을 익힐 때에도 이것저것 찾아보기만 하다가 결국 학교 과제를 LaTeX으로 작성해보고서야 익숙해졌는데, 이번에도 실 사용 없이 공부하는게 얼마나 갈지는 잘 모르겠다.. 자료 초반에 “All the core chapters are suitable for both upper-level undergraduate and graduate students.” 라는 말이 있던데, 물론 저런 말들이 다 그렇듯이 전부 거짓말이겠지만 이젠 undergraduate 신분이라는 핑계도 사라졌으니 좀더 확실히 짚고 넘어가야겠다는 생각도 든다.\n","title":"[Coq 입문] Ch00. Overview","type":"posts"},{"content":"동아리 톡방에서 이데일리 코딩대회에 대한 소식을 들었다. 학부를 졸업했지만 아직 대학원생은 아니라는 애매한 신분 때문에 대부분의 대회를 참가하지 못했었는데, 이번 대회는 청소년부/성인부로만 나뉘어져 있어 참가신청을 할 수 있었다.\n예선 10월 26일 온라인 예선이 다가오자 안내문자가 날아왔는데, 처음엔 눈을 의심했다. 48시간 대회라는 점도 특이했지만 120문제라는 엄청난 문제 수에선 말이 나오지 않았다. 다른 대회들이 보통 3~5시간 동안 5문제, 팀으로 진행하는 ICPC도 많아야 12,13문제를 내는데 예선에서부터 120문제라는 공지를 보고 과연 어떤 문제들이 나올지 기대(?)되기도 했다.\n솔직히 말하면 예선의 퀄리티는 그다지 높지 못했다. A+B처럼 아주 기초적인 문제는 시스템 테스트를 위해 냈다고 생각해도 별찍기류의 문제가 여럿 등장하는 부분에서는 **\u0026ldquo;차라리 이런 문제들을 빼고 문제 수를 줄이는 게 더 좋겠다\u0026rdquo;**라는 생각까지 들었을 정도로 아쉬운 부분들이 꽤 있었다. 특히 온라인대회의 한계 + 공정성을 위해서라고는 하지만 문제에 대한 Clarification 수단이 부재했다는 점이 이런 불만을 더 크게 느끼게 만들었다. 예선 중 QnA게시판을 통해 문제 오류에 대한 많은 질문들이 올라왔지만, 모든 답변은 아래와 동일했다.\n내년에는 이런 점들은 조금 보완했으면 좋겠다.\n본선 예선을 통과하고 서울대학교 글로벌컨벤션센터에서 열리는 본선대회에 참가했다.\n익숙한 풍경과 함께 테이블마다 노트북이 세팅되어있었다.\n대회는 3시간동안 20문제를 해결하는 방식이었다. 다행히 생각보다는 현실적인(?) 숫자였고 대회 시작 후 간단히 문제들을 훑어보니 예선보다는 검수가 꽤 된 문제들이라는 생각이 들었다. 배점이 낮은 문제부터 먼저 해결한 후 배점이 높은 문제 순으로 하나씩 코딩을 진행했다.\n쉬운 문제들은 간단한 DFS,BFS로 금방 해결할 수 있는 난이도였지만 어려운 문제들은 플로우나 FFT까지 다루는 문제들이 나왔다. 플로우(이분매칭)는 입력 사이즈가 작은 걸 보고 그냥 Ford–Fulkerson 알고리즘을 돌렸고, FFT는 다행히 원리를 기억하고 있었기에 팀노트 없이도 어떻게든 대회가 끝나기 전에 유도해낼 수 있었다. 개인적으로는 통계, 기댓값에 관한 1번문제가 제일 어려웠는데 대회가 끝날 때까지 고민하다가 결국 답안을 제출하지는 못했다.\n그리고 성인부 대상을 받았다(?)\n이번 대회는 ICPC나 SCPC와 달리 대회 중간에 답안을 제출해도 맞았는지 여부를 알려주지 않아 결과 발표 전까지 본인 점수를 확신할 수 없었다. 막연히 \u0026ldquo;그래도 19문제쯤 제출했으면 뭐라도 하나 건져가지 않을까\u0026quot;라는 생각은 했지만 대상까지는 예상을 못했기에 이름이 불렸을 때 꽤나 놀랐다. 나중에 알고보니 대회 운영진에게는 실시간 스코어보드가 보였는데, 대회 초반까지는 내 이름이 순위권에 없다가 점점 올라가더니 끝나기 10분 전에 2등(당시까지 1등)을 제쳤다는 말을 들었다.\n사실 이날 세종대에서 ICPC 서울리저널이 있었고, 이데일리는 현역 대학생들은 대부분 빠진 빈집털이(\u0026hellip;)가 된 꼴이라 어디가서 1등이라고 자랑하기는 약간 민망하다. 그래도 대회 마지막까지 던지지 않고 계속한 게 결국 +100만원이라는 의미있는 결과를 냈다고 생각하면 꽤 보람있는 대회였다.(어쨌든 내가 상받으면 좋은 대회)\n","date":"2018년 11월 7일","externalUrl":null,"permalink":"/blog/edaily-Coding-Challenge-2018-review/","section":"Posts","summary":"동아리 톡방에서 이데일리 코딩대회에 대한 소식을 들었다. 학부를 졸업했지만 아직 대학원생은 아니라는 애매한 신분 때문에 대부분의 대회를 참가하지 못했었는데, 이번 대회는 청소년부/성인부로만 나뉘어져 있어 참가신청을 할 수 있었다.\n예선 10월 26일 온라인 예선이 다가오자 안내문자가 날아왔는데, 처음엔 눈을 의심했다. 48시간 대회라는 점도 특이했지만 120문제라는 엄청난 문제 수에선 말이 나오지 않았다. 다른 대회들이 보통 3~5시간 동안 5문제, 팀으로 진행하는 ICPC도 많아야 12,13문제를 내는데 예선에서부터 120문제라는 공지를 보고 과연 어떤 문제들이 나올지 기대(?)되기도 했다.\n","title":"이데일리 Coding Challenge 후기","type":"posts"},{"content":"어째 코드잼 관련 글만 계속 올라오는 것 같다.. 여튼 이틀 전 Code Jam Kickstart Round G가 있길래 참가했다. 일요일 오후 10시라는 시간대가 조금 걸렸지만 월요일 연차휴가라는 사실에 힘입어 대회를 치르기로 했다.\n대회 초반에 빠르게 문제를 풀고 중간 등수가 6등까지 올라갔다. 남은 1시간 반 동안 C 라지를 마저 풀까 하다가 exponential 한 알고리즘밖에 떠오르지 않길래 gg 후 웹툰으로 넘어갔다.\n따로 문제를 더 풀진 않았으니 대회가 진행될수록 스코어보드에서 등수는 점점 내려갔다. 종료 직전엔 38등까지 떨어진 걸 보고 이번 판은 망했구나 싶었\u0026hellip;는데 C 라지에서 sysfail의 철퇴를 맞은 사람들이 대거 떨어지면서 최종 등수는 19등으로 끝났다.\n전체 문제는 여기 에서 볼 수 있다.\nProblem A. Product Triplets 배열에 등장하는 수들이 20만 이하이므로 충분히 count 배열을 잡을 수 있다. a*b=c 에서 a,b,c가 0이나 1인 경우에 주의하면서 O(N^2) 가지 가능성을 전부 카운트해주면 된다.\nProblem B. Combining Classes [L, R] 구간에 1을 더하는 range query를 수행한 후, 최종 결과에서 K번째 값을 찾는 문제이다. 구간 더하기가 모두 수행된 후 탐색이 진행되므로 range query를 prefix sum 형태로 변형할 수 있다. 먼저 L, R들을 내림차순으로 정렬하고 R 지점마다 +1, (L-1)지점마다 -1을 세팅해준 후 prefix sum 값이 K가 되는 첫 지점들을 찾으면 된다. 두 가지 방법을 떠올렸다\n1) L, R지점들만 값을 구한 후, 나머지는 규칙성을 이용한다\n점수가 L-1, R 인 포인트를 제외하면 각 \u0026lsquo;포인트\u0026rsquo;들 사이에서는 점수별 인원수가 동일하므로 이진탐색으로 K번째 점수가 포함되는 구간을 찾은 후, K번째 점수를 내삽할 수 있다. 좌표 압축과 유사하게 진행되며 이 경우 각 탐색을 O(logN) 에 수행할 수 있다. 2) 답은 g++ -O3다 갓-컴파일러님의 최적화를 믿자\n사실 위 방법은 코딩이 조금 귀찮다. 그런데 최대 점수가 10억 이하이므로 \u0026ldquo;이거 잘만 하면 O(10억) 으로 가능하지 않을까?\u0026rdquo; 라는 야매스러운 아이디어가 떠오른다. prefix sum을 위한 배열을 실제로 만드는 대신, 10억부터 아래로 카운트하면서 내려가면 된다. 대략 아래와 같은 방법이라고 생각하면 된다. (vec은 {R, +1}, {L-1, -1}을 내림차순으로 정렬한 벡터) cnt = inc = 0; idx = 0; for(int score=1000000000;score\u0026gt;=0;score--) { while(idx \u0026lt; vec.size() \u0026amp;\u0026amp; vec[idx].first==score) { inc += vec[idx].second; idx++; } cnt += inc; } 그러면 cnt는 해당 시점의 score가 속한 최대 등수를 나타낸다. 여기에 더해 모든 탐색대상 K를 오프라인으로 저장한 후 정렬해주면 위 루프를 돌면서 답을 구할 수 있다. -O3 옵션으로 컴파일해보니 small, large 모두 2-3분쯤 걸려 결과를 출력했다.\nProblem C. Cave Escape small은 \u0026ldquo;설마 함정을 두번 건너는 최적해가 있겠어\u0026quot;라는 (조금만 생각해보면 당연한) 전제 하에, 함정에 양수 가중치를 두고 최단경로를 탐색하면 된다. large는 함정이 많아야 15개라는 걸 이용해 2^15가지 경우의 수를 모두 시뮬레이션하는 걸 생각해봤는데, 여기서 더 최적화를 하지 못해 접었다. 이후 풀이를 보니 몇 가지 전처리가 들어가긴 하지만 결국 모든 경우의 수를 탐색하는 게 정해라서 조금 아쉬웠다.. 스코어보드를 보니 2시간 40분을 넘겨서도 계속 제출해서 맞은 사람들이 꽤 되던데 끈기가 대단하다고 느꼈다. 앞으로는 안풀린다고 바로 접지 말고 좀더 생각해보는 자세를 길러야겠다\n최종 스코어보드는 여기에서 볼 수 있다.\n","date":"2018년 10월 23일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2018-RoundG-review/","section":"Posts","summary":"어째 코드잼 관련 글만 계속 올라오는 것 같다.. 여튼 이틀 전 Code Jam Kickstart Round G가 있길래 참가했다. 일요일 오후 10시라는 시간대가 조금 걸렸지만 월요일 연차휴가라는 사실에 힘입어 대회를 치르기로 했다.\n대회 초반에 빠르게 문제를 풀고 중간 등수가 6등까지 올라갔다. 남은 1시간 반 동안 C 라지를 마저 풀까 하다가 exponential 한 알고리즘밖에 떠오르지 않길래 gg 후 웹툰으로 넘어갔다.\n","title":"Code Jam Kickstart 2018 Round G 후기","type":"posts"},{"content":"TL;DR : 로컬의 문제점을 깨닫지 못하고 폭사(\u0026hellip;)\n페이스북 해커컵이 끝났다. 구글 코드잼 본대회는 대회 당시 인터넷을 쓸 수 없는 환경(\u0026hellip;)에 있어서 아쉬운 마음이었는데, 다행히 해커컵은 일정이 맞아 모두 참가할 수 있었다. 생각치 못한 실수로 Round 2에서 탈락했지만 각 라운드를 간단히 요약해봤다.\nQualification Round 당시 아직 \u0026lsquo;전직\u0026rsquo;을 한 상태가 아니었지만 주말이라 외출을 할 수 있었다. 대전의 한 PC방에서 파이썬3을 다운받아 대회를 진행했다.\nFriends 스코어보드를 보니 익숙한~~(+군대에 있어야 할)~~ 이름이 보였는데 알고보니 사지방에서 대회를 진행했다고 한다. 구닌 둘이 제일 먼저 대회함 문제 자체는 크게 어렵지 않았고, 일찍 일어난 덕인지 패널티에서 큰 이득을 봐 (별 의미는 없었지만) 등수 뻥튀기를 경험할 수 있었다.\np.s. B번은 연산자 우선순위를 이상하게 정의한 문제였는데, 입력받는 부분을 제외하면 O(1)로 풀리는 퍼즐문제에 가까워서 그리 마음에 들진 않았다.\nRound 1 대회 시점이 일요일에서 월요일로 넘어가는 밤이었고 다음날 출근해야 했던 터라 걱정이 됐다. A,B번을 제출하고 끝낼까 싶었지만 대회 종료까지 정답 여부가 공개되지 않아 혹시나 하는 마음에 C번까지 건드려 봤다. 다행히 parametric search를 이용한 풀이가 금방 떠올랐고, 결과적으로 A B C 모두 정답으로 1라운드를 통과했다.\nRound 2 이 단계에서 500등 안에 들면 티셔츠를 받을 수 있었다. 학부 재학 시절 코드잼이나 해커컵같은 대회들과는 연이 없던 터라 이번만큼은 티셔츠를 받겠다는 목표가 있었다. 결과부터 말하자면 실패했다(\u0026hellip;)\nA는 조건에 맞는 그래프를 construction하는 문제였다. 대략적인 윤곽은 그려졌지만 \u0026lsquo;설마 이게 답일까\u0026rsquo;라는 생각에 잠시 접어두고 B를 읽어봤다. 얼마 전 읽어봤던 Euler Tour가 생각났고, 구체적인 알고리즘이 떠오르자 A를 마저 코딩하는 쪽이 나아보였다.\nA 제출이 끝난 후 B 풀이에서 부족한 점이 없는지 살펴봤고, euler tour와 함께 크기가 작은 서브트리(=depth가 깊은 노드)부터 따지면 충분히 해결할 수 있을 것 같았다. 예제가 나오는 걸 확인하고, 입력 파일을 다운받아 자신있게 돌려보니 코드가 터졌다(\u0026hellip;) 처음 몇 번은 현실을 받아들이지 못하고 아인슈타인의 명언을 그대로 따랐다.\n\u0026ldquo;미친 짓이란, 똑같은 일을 반복하면서 다른 결과를 기대하는 것을 말한다. \u0026ldquo;\n다시 생각해보니 euler tour를 하면서 트리를 dfs로 순회하는 부분이 있었다. 스택 사이즈 제한이 없는 대부분의 채점서버와 달리 내 노트북에선 스택 메모리 사이즈에 제한이 있을 터였고, skewed된 트리에서는 연속된 재귀호출로 인해 스택이 넘칠 수 있다는 사실을 깨달았다. 급하게 g++ 스택 사이즈 제한 해제 같은 검색어로 검색을 시도했다. 많은 팁이 있었지만 대부분 리눅스 상에서 ulimit -s를 사용하거나 Visual Studio에서만 적용 가능한 내용이었고, MinGW로 윈도우에서 g++ 설치해 쓰는 과거의 나를 원망하며 제출란이 Expired로 바뀌는 것을 지켜볼 수밖에 없었다..\np.s. 대회가 끝나고 dfs 부분을 수정해 다시 돌려보니 맞았다고 뜬다(\u0026hellip;) 6분동안 검색 대신 STL stack을 사용해 코드를 바꿨으면 어떨까라는 아쉬움이 남지만 이미 지난 일 뭐 어쩌겠는가ㅠ\n","date":"2018년 8월 5일","externalUrl":null,"permalink":"/blog/facebook-hackercup-2018-review/","section":"Posts","summary":"TL;DR : 로컬의 문제점을 깨닫지 못하고 폭사(…)\n페이스북 해커컵이 끝났다. 구글 코드잼 본대회는 대회 당시 인터넷을 쓸 수 없는 환경(…)에 있어서 아쉬운 마음이었는데, 다행히 해커컵은 일정이 맞아 모두 참가할 수 있었다. 생각치 못한 실수로 Round 2에서 탈락했지만 각 라운드를 간단히 요약해봤다.\nQualification Round 당시 아직 ‘전직’을 한 상태가 아니었지만 주말이라 외출을 할 수 있었다. 대전의 한 PC방에서 파이썬3을 다운받아 대회를 진행했다.\n","title":"Facebook Hacker Cup 2018 후기","type":"posts"},{"content":"공식적으로 전직(\u0026hellip;)을 했지만 가능한 한 PS대회는 계속 나가보고 싶었다. SCPC나 UCPC는 이제 대학생이 아니라 참가를 못했지만, 지난 주말에 Code Jam Kickstart Round D가 있어 여기 참가해볼 수 있었다. 코드잼 킥스타트에 대한 설명은 Round C 후기에서 한번 작성한 바 있으니 이 글에서는 생략했다.\n이날 대회는 오후 2시부터 열렸는데, 내가 3시부터 세미나가 있어 그전까지는 약속장소 근처 스타벅스, 이후엔 세미나실에서 틈틈히 코딩을 진행했다. 저번 Round C를 1시간 20분만에 올클했었기에 이번에도 비슷하게 걸리지 않을까라는 근자감이 가득한 예상을 했지만 A를 보는 순간 그런 기대는 깔끔하게 접혔다. 결국 C에 와서는 \u0026ldquo;그냥 small만 통과하자\u0026quot;라는 마음으로 small 전용 코드를 짜서 제출하고 다시 세미나에 합류했다.\n쉬는시간에 최종 결과를 확인해보니 23등으로 나름 선방(?)한 걸 확인할 수 있었다.\n문제는 여기 에서 볼 수 있다. 이미 analysis까지 올라온 상황이니 풀이보다는 당시 심정을 중점으로 서술했다\nProblem A. Candies 문제를 읽고 \u0026ldquo;이거 A번 맞나\u0026quot;라는 생각이 들었다. odd 조건을 만족시킬 방법은 쉽게 떠올렸는데 max sweet 제한을 벗어나지 않으면서 가장 큰 값을 찾는 게 쉽지 않았다. BBST를 구현하면 금방 해결될 문제였지만 그러긴 싫었고, C++ STL multiset을 찾아봤다. 레퍼런스 설명을 잘못 읽으면서 헤멘 시간이 많아 40분이 지난 시점에서야 Correct를 띄울 수 있었다.\nProblem B. Paragliding 기둥들을 기준으로 양방향 45도 아래로 직각이등변삼각형을 그리면 그 영역에 포함된 풍선의 개수가 답이 된다. 문제를 보는 순간 데자뷰가 느껴졌다.\n위는 이번 문제에서 제공된 그림, 아래는 작년 Facebook에서 열렸던 Hackercup 2017 Round 2의 Big Top이라는 문제의 그림이다. 작년에 저 문제를 못 풀었고, 그때 정해가 BBST를 직접 구현하는 거였기에 \u0026ldquo;A에서 BBST 구현할걸\u0026quot;이라는 후회를 잠깐 했다. 이후 BBST 코딩을 정말 하기 싫어서 천천히 다시 생각해보니 기둥을 실시간으로 세우는 게 아니기에 정렬만 하면 굳이 BBST 없이도 해결이 가능하다는 걸 깨달았고, 기쁜 마음으로(?) 코딩을 진행했다.\nProblem C. Funniest Word Search 뭔가 어렴풋이 떠오를 것 같았는데 구체화까지는 아직 멀어보였고, 이때 세미나가 진행중이어서 계속 문제만 붙들고 있기로 어려운 상황이었다. small은 prefix sum으로 금방 해결할 수 있어보여서 여기까지만 풀고 대회를 끝냈다. 나중에 analysis를 읽어보니 내 방식은 small에만 적용 가능하고 large를 위해서는 좀더 확장해야 한다고 하던데 한번 자세히 읽어봐야겠다.\n최종 스코어보드는 여기에서 볼 수 있다.\n","date":"2018년 8월 3일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2018-RoundD-review/","section":"Posts","summary":"공식적으로 전직(…)을 했지만 가능한 한 PS대회는 계속 나가보고 싶었다. SCPC나 UCPC는 이제 대학생이 아니라 참가를 못했지만, 지난 주말에 Code Jam Kickstart Round D가 있어 여기 참가해볼 수 있었다. 코드잼 킥스타트에 대한 설명은 Round C 후기에서 한번 작성한 바 있으니 이 글에서는 생략했다.\n이날 대회는 오후 2시부터 열렸는데, 내가 3시부터 세미나가 있어 그전까지는 약속장소 근처 스타벅스, 이후엔 세미나실에서 틈틈히 코딩을 진행했다. 저번 Round C를 1시간 20분만에 올클했었기에 이번에도 비슷하게 걸리지 않을까라는 근자감이 가득한 예상을 했지만 A를 보는 순간 그런 기대는 깔끔하게 접혔다. 결국 C에 와서는 “그냥 small만 통과하자\"라는 마음으로 small 전용 코드를 짜서 제출하고 다시 세미나에 합류했다.\n","title":"Code Jam Kickstart 2018 Round D 후기","type":"posts"},{"content":"한동안 개인 사정으로 인해 다른 일을 할 수 없어 Google Code Jam 본대회 Qualification Round를 불참했는데, 마침 오늘 Code Jam Kickstart 가 있길래 참가해봤다.\nKickstart는 CodeJam 본대회와 달리 비교적 자주 진행되고(2017년 기준 7회), 다양한 시간대에 분포해 있어 코포처럼 새벽까지 깨어있지 않아도 참가할 수 있다. 대회 자체적으로 상을 주지는 않지만 좋은 성적을 낼 경우 구글에서 인턴/입사 인터뷰 메일이 오기도 하니 시간이 된다면 참가해보자. 실제로 2017년 Kickstart Round G 때 15등 찍어서 메일 받아봄.\n한동안 코딩을 하지 않아 걱정했는데 다행히 풀이들이 비교적 금방 떠올랐고, 최종 14등으로 대회를 마칠 수 있었다.\n문제는 여기 에서 볼 수 있다. 어차피 며칠 후면 analysis가 올라오겠지만 내 풀이를 간단히 적어봤다.\nProblem A. Planet Distance 사이클이 하나 있는 그래프가 주어지면, 각 정점에서 사이클까지의 거리를 구하는 문제이다. Cycle-Detection을 진행하고 어떤 정점들이 사이클에 속하는지 구해 BFS를 돌리면 된다. 코딩이 조금 까다로울 것이라 생각했으나 Large의 N제한이 1000인 걸 보고 그냥 O(N^2)으로 구현했다.\nProblem B. Fairies and Witches 주어진 그래프의 각 간선이 막대라고 가정하자. 이들 중 다음을 만족하는 막대들의 집합의 수를 구하는 문제이다.\n집합의 어떤 두 막대도 원 그래프에서 인접(=끝점을 공유)하지 않는다. 집합의 막대들을 모두 사용해 넓이가 0이 아닌 Convex Polygon을 만들 수 있다. 이중 Convex Polygon에 대한 두 번째 조건은 다음과 같이 바꿀 수 있다.\n집합에 속한 막대의 수가 3 이상이다. 집합에 속한 막대의 총 길이 합은 가장 긴 막대길이의 2배를 넘어야 한다. 이제 첫 번째 조건을 만족하는 집합을 생성하고 위 조건을 고려하면 된다. 이는 N제한이 Small에서 6, Large에서 15로 주어졌기에 완전탐색으로 풀 수 있다고 생각했다. Small의 경우 O(2^E)로도 가능했으나 Large를 해결하기엔 약간 부족했고, 집합에서 \u0026ldquo;간선끼리 공유하는 정점이 없다\u0026rdquo; = \u0026ldquo;각 정점에서 최대 하나의 인접간선만 뽑을 수 있다\u0026quot;는 점에 착안해 고려해야 하는 경우의 수를 크게 줄일 수 있었다.\nProblem C. Kickstart Alarm 문제 링크에 공식이 나와 있어 따로 작성하지는 않는다. 주어진 공식으로 POWER_i라는 값이 정의될 때, i가 1~K까지 모든 POWER의 합을 구하는 문제이다. 식을 정리하면 결과적으로 다음을 구하라는 문제로 바뀐다.\nA[1], A[2], \u0026hellip;, A[N]에 대해\nA[i] x (N-i+1) x ( (1^1+1^2+\u0026hellip;+1^K) + (2^1+2^2+\u0026hellip;+2^K) + \u0026hellip; + (i^1+i^2+\u0026hellip;+i^K))\n들의 합을 구하라.\na^1 + a^2 + a^3 + \u0026hellip; + a^K를 등비수열의 합 공식을 써 구할 수 있다는 것만 파악하면 O(N logK)에 해결할 수 있다.\n해당 라운드의 최종 스코어보드 에서 각 참가자가 작성한 코드를 다운받을 수 있으니 참고하자.\n","date":"2018년 5월 27일","externalUrl":null,"permalink":"/blog/CodeJam-Kickstart-2018-RoundC-review/","section":"Posts","summary":"한동안 개인 사정으로 인해 다른 일을 할 수 없어 Google Code Jam 본대회 Qualification Round를 불참했는데, 마침 오늘 Code Jam Kickstart 가 있길래 참가해봤다.\nKickstart는 CodeJam 본대회와 달리 비교적 자주 진행되고(2017년 기준 7회), 다양한 시간대에 분포해 있어 코포처럼 새벽까지 깨어있지 않아도 참가할 수 있다. 대회 자체적으로 상을 주지는 않지만 좋은 성적을 낼 경우 구글에서 인턴/입사 인터뷰 메일이 오기도 하니 시간이 된다면 참가해보자. 실제로 2017년 Kickstart Round G 때 15등 찍어서 메일 받아봄.\n","title":"Code Jam Kickstart 2018 Round C 후기","type":"posts"},{"content":"대부분의 reference가 그렇듯이 읽다보면 쉽게 지루해진다. 지루함을 덜하고 Coq에 익숙해지기 위해 일단 예제를 조금씩 따라해보면서 익혀가기로 했다. 자바를 처음 배울 때 *public static void main(int argv, char **argc)*가 정확히 뭔지는 몰라도 \u0026ldquo;Hello World\u0026rdquo; 부터 찍어보는 그런 마음으로 진행해봤다.\nCoqIDE 실행 프로그램을 처음 실행하면 다음과 같은 화면을 볼 수 있다. 크게 3가지 창으로 나뉘어져 있으며, 왼쪽은 코드 작성을, 오른쪽 위/아래는 각각 Proof 보조/결과 출력을 담당하는 역할이었다.\n자연수 집합 mynat 정의 이제 자연수 집합을 정의해보자. 사실 Coq에서는 이미 nat이라는 형태로 자연수를 제공하지만, 여기서는 mynat이라는 이름으로 처음부터 정의해나가기로 하자. 자연수 집합은 아래와 같이 Inductive하게 정의할 수 있다.\nInductive mynat : Set := | O : mynat | Succ : mynat-\u0026gt;mynat. 위 코드를 한줄씩 살펴보자\nInductive mynat : Set := \u0026ldquo;mynat이라는 집합(Set)을 Inductive하게 정의하자\u0026rdquo;\nO : mynat \u0026ldquo;O(숫자 0이 아닌 알파벳 O임에 유의)은 mynat에 속한다\u0026rdquo;\nSucc : mynat-\u0026gt;mynat \u0026ldquo;Succ은 mynat 에서 mynat으로 가는 함수이다.\u0026rdquo;\n🡒 어떤 x가 mynat에 속한다면, (Succ x) 도 mynat의 원소이다.\nCoq의 모든 statement는 .(period)으로 끝난다는 점에 유의해야 한다.\n여기서 Succ 함수는 보통 \u0026ldquo;다음 자연수를 제공하는 함수\u0026quot;로 해석해 (Succ 0) = 1, (Succ 1) = 2, \u0026hellip; 등으로 해석되지만 이는 반대로 이해해야 한다. 코드의 Succ 정의로부터 알 수 있는 사실은 오직 **\u0026ldquo;x가 mynat일 때 (Succ x)도 mynat이다\u0026rdquo;**라는 점일 뿐 자연수들 사이의 순서에 대해서는 말하지 않았다. 오히려 1, 2, 3을 각각 (Succ O), (Succ 1)(혹은 (Succ (Succ O))), (Succ (Succ (Succ O)))을 나타내는 syntactic sugar라고 받아들이는 편이 더 적절하다.\n여기까지 실행시키면 오른쪽 아래 창에 다음과 같은 출력을 볼 수 있다.\n분명 mynat만 정의했는데 mynat_rect, mynat_ind, mynat_rec이라는 것들이 함께 정의된 것을 볼 수 있다. 이는 Inductive 키워드를 사용해 귀납적으로 mynat을 정의한 결과인데, 잠시 후 덧셈의 결합법칙 증명에서 이를 사용할 것이다.\nmynat에서의 덧셈 정의 \u0026ldquo;덧셈의 결합법칙\u0026quot;을 말하기 위해선 먼저 덧셈이 무엇인지부터 정의해야 한다. 덧셈은 mynat 에 속하는 인자 2개(a, b)를 받는 함수로 다음과 같이 정의될 수 있다.\nDefinition add (a b : mynat) := match a with | O =\u0026gt; b | Succ c =\u0026gt; Succ (add c b) end. 코드에서 match a with ... 부분을 풀어 쓰면 아래와 같다. 앞서 Succ의 해석에 유의하자는 말을 했으나 여기서는 편의상 \u0026lsquo;다음 수\u0026rsquo;라는 용어를 사용했다.\n0 =\u0026gt; b 0 과 b를 더한 결과는 b이다 Succ c =\u0026gt; Succ (add c b) a가 어떤 mynat (c라고 하자)의 \u0026lsquo;다음 수\u0026rsquo;였다면 a와 b를 더한 결과는 \u0026lsquo;(c와 b를 더한 결과)의 다음 수\u0026rsquo;가 된다. 그런데 실제로 위 코드를 실행하면 Error: The reference add was not found in the current environment. 라는 에러가 발생한다. add의 정의에서는 아직 add가 정의되지 않았기 때문인데, Definition 대신 Fixpoint를 사용해 해결할 수 있다.\n덧셈의 결합법칙 증명 이제 \u0026ldquo;덧셈의 결합법칙이란 무엇인가\u0026quot;를 서술하는데 필요한 재료가 모두 갖추어졌다. 알다시피 결합법칙은 (a+(b+c))=((a+b)+c) 를 의미하며, 아래 코드에 이를 Theorem으로 서술했다. 증명해야 할 내용이 오른쪽 위에 subgoals라는 이름으로 나타난 것을 확인할 수 있다.\n이제 증명을 시작하자. mynat에 속하는 모든 원소 a b c에 대해 (a+(b+c))=((a+b)+c)임을 보이려면 어떻게 해야 할까? 가장 처음은 mynat에 속하는 a b c가 주어졌다고 가정하는 것에서 시작한다.\n이제 a에 대해 수학적 귀납법을 적용하자. 앞서 mynat을 Inductive하게 정의했으므로 Coq는 이를 인식해 수학적 귀납법을 위한 틀을 제공해준다. subgoals를 보자.\n이제 subgoal들을 하나씩 보이자. simpl. 을 사용해 첫 subgoal을 간단히 하면 다음과 같이 변한다.\n2 subgoal b : mynat c : mynat ______________________________________(1/2) add b c = add b c ______________________________________(2/2) add (Succ a) (add b c) = add (add (Succ a) b) c 딱 보면 왼쪽이랑 오른쪽이 같은데 왜 아직 안 끝났지 reflexivity. 다시 말해 x=x는 참이라는 사실을 이용하자. 그러면 subgoal이 하나 해결되고 Induction Hypothesis가 추가된 것을 확인할 수 있다.\n1 subgoals a : mynat b : mynat c : mynat IHa : add a (add b c) = add (add a b) c ______________________________________(1/1) add (Succ a) (add b c) = add (add (Succ a) b) c 다시 simpl.을 적용하자.\n1 subgoals a : mynat b : mynat c : mynat IHa : add a (add b c) = add (add a b) c ______________________________________(1/1) Succ (add a (add b c)) = Succ (add (add a b) c) subgoal을 보면 어디서 많이 그러니까 IHa같은 데서 본 형태가 나타났다. rewrite -\u0026gt; IHa.를 이용해 IHa의 좌변을 모두 우변으로 바꿔 써 보자.\n1 subgoals a : mynat b : mynat c : mynat IHa : add a (add b c) = add (add a b) c ______________________________________(1/1) Succ (add (add a b) c) = Succ (add (add a b) c) 조금 전과 같이 reflexivity.를 사용하면 no more subgoals 라는 안내를 볼 수 있다. 이제 Qed.로 증명을 끝내면 add_associativity is defined 라는 메시지가 나오면서 증명이 완료된 것을 확인할 수 있다. 완성된 증명을 Print로 살펴보자.\n증명 대신 fun 키워드로 시작하는 함수가 나타났다. 이는 프로그램과 (그 프로그램의) 타입 사이의 관계는 증명과 (그 증명이 증명하는) 명제 사이의 관계에 대응될 수 있다 라는 Curry-Howard isomorphism과 관련이 있다고 하는데, 이에 대해서는 차차 공부해나가기로 하자.\n","date":"2018년 1월 8일","externalUrl":null,"permalink":"/blog/Coq-02-mynat/","section":"Posts","summary":"대부분의 reference가 그렇듯이 읽다보면 쉽게 지루해진다. 지루함을 덜하고 Coq에 익숙해지기 위해 일단 예제를 조금씩 따라해보면서 익혀가기로 했다. 자바를 처음 배울 때 *public static void main(int argv, char **argc)*가 정확히 뭔지는 몰라도 “Hello World” 부터 찍어보는 그런 마음으로 진행해봤다.\nCoqIDE 실행 프로그램을 처음 실행하면 다음과 같은 화면을 볼 수 있다. 크게 3가지 창으로 나뉘어져 있으며, 왼쪽은 코드 작성을, 오른쪽 위/아래는 각각 Proof 보조/결과 출력을 담당하는 역할이었다.\n","title":"Coq 02 - 자연수 덧셈의 결합법칙 증명","type":"posts"},{"content":"\u0026lt;Interactive Theorem Proving and Program Development\u0026gt;라는 책을 추천받아 조금씩 읽어보기로 했다. 첫 장에서는 Coq에 대한 간단한 안내를 진행한다. 책의 순서를 따라가면서 하나씩 알아보자\n타입 먼저 Coq에서는 Gallina라는 언어를 사용해 요구사항(Specification)을 서술한다. 대부분의 다른 프로그래밍 언어들과 마찬가지로 타입은 declaration과 typing rule에 의해 정해진다.\ndeclaration : type Z 에 대해 이 타입의 변수 z를 선언 typing rule : type Z에 속하는 변수 z와 -6이 있으면 -6z는 type Z 또한 기본 타입(primitive type) A, B로부터 새로운 타입을 만들어내는 것 또한 가능하다\ntype A x B : a가 type A, b가 type B일 때 *(a, b)*의 타입 type A🡒B : type A에 속하는 a를 받아 type B에 속하는 b를 내놓는 함수의 타입 하스켈의 Currying에 익숙하다면 다음과 같이 좀더 복잡한 타입을 정의할 수도 있다.\nQ. \\(f:\\mathrm{N}\\rightarrow \\mathrm{Z}, n \\in \\mathrm{N}\\)을 받아 \\( \\sum_{i=0}^{i=n} f(i) \\)를 반환하는 함수의 타입은?\nA. (nat🡒Z)🡒nat🡒Z\nProposition과 Proof Proposition Coq가 다른 프로그래밍 언어들과 다른 점은 다루고자 하는 대상에 대한 표명(assertion)을 표현할 수 있다는 점이다. 단순한 수학적 object에서부터 프로그램 자체에 이르기까지 다양한 대상에 대한 표현이 가능하다는 점에 유의하자. 이런 assertion(혹은 proposition)의 예로는 다음을 들 수 있다.\n3 ≤ 8\n8 ≤ 3\n\u0026ldquo;for all n\u0026gt;1, the sequence of integers defined by\n$$ {\\begin{matrix} u_0 = n \\\\ u_{i+1}={\\begin{Bmatrix}{u_i \\over 2},\u0026{\\mbox{if }}u_i{\\mbox{ is even}}\\\\3u_i+1,\u0026{\\mbox{if }}u_i{\\mbox{ is odd}}\\end{Bmatrix}} \\end{matrix}} $$ultimately reaches the value 1\u0026rdquo;\n\u0026ldquo;list concatenation is associative\u0026rdquo;\n\u0026ldquo;the algorithm insertion_sort is a correct sorting method\u0026rdquo;\n이들 중 일부는 참, 거짓 여부를 쉽게 알 수 있고 일부는 아직 참 여부가 밝혀지지 않았지만 모두 명확한 명제(well-formed proposition)이다. 그렇다면 이런 명제들이 참임은 어떻게 알 수 있을까?\nProof 어떤 명제가 참임을 보이는 가장 좋은 방법은 그 명제에 대한 증명을 보여주는 것이다. 사실 Sound한 체계에서만 그렇다. 문제는, 제시된 증명이 올바른지 판단하려면 해당 증명은 complete하고 readable해야 한다는 점이다. 그리고 대부분의 경우 자연어로 된 수많은 증명은 이를 만족하지 않는다. 이런 문제를 해결하기 위해 다음과 같은 해결책이 제시되었다.\n자연어는 모호하다\n🡒 증명을 위한 formal language를 설계하고, proof theory의 rule들을 이용해 증명을 서술한다\n그렇게 formal language로 서술된 증명은 (너무 길어서) 검증이 수고스럽다\n🡒 formal rule이 제대로 사용되었는지 검증하는 verification 과정을 자동화한다\nformal language로 서술된 증명은 (너무 길어서) 처음 증명을 작성하기도 힘들다\n🡒 formal proof를 작성하는 단계에서부터 자동화 도구(proof assistant)의 도움을 받는다\n자동화 도구에서 proof generator가 아니라 proof assistant 라는 표현에 유의하자. 안타깝게도 증명의 모든 과정을 자동화할 수 있는 방법은 존재하지 않는다. 따라서 Coq는 사용자가 전제 증명을 작은 lemma들로 쪼개고 각각에 대해 적절한 증명전략(tactic)을 선택하는 interactive한 방식으로 동작하게 된다.\nProposition과 Type 증명을 작성할 때는 어떤 언어를 사용하는 것이 좋을까. 증명(proof)과 프로그램(program)은 같은 formal한 방법으로 서술될 수 있다. 양쪽 모두 Church가 고안해낸 typed λ-calculus를 통해 서술될 수 있으며, Coq는 이 typed λ-calculus에서 좀더 다양한 서술을 가능하게 만든 Calculus of Inductive Constructions라는 체계를 사용한다.\n책의 3장에서 Curry-Howard isomorphism을 통해 proof와 program의 관계를 설명하지만 그건 일단 나중으로 미루고, program과 (그 program의) type 사이의 관계는 proof와 (그 proof가 증명하는) statement의 관계와 같다는 점에 유의하자. 이를 이용하면 proof를 검증하는 과정은 type verification algorithm과 크게 다를 바가 없음을 이해할 수 있다.\n모든 type은 해당 타입의 term(객체)을 가진다. Calculus of Inductive Costruction의 핵심은 **\u0026ldquo;type은 type이면서 그 자체로 어떤 type을 가지는 term이다\u0026rdquo;**라는 말을 이해하는 것이다. proposition의 타입을 Prop이라고 하고 예시를 통해 이 말의 의미를 살펴보자\nProposition \u0026ldquo;3 ≤ 7\u0026quot;은 type Prop을 가지는 term 이면서 (3≤7임을 보이는 증명)의 type 이 된다. 이때 (3≤7임을 보이는 어떤 증명 A)는 type 3≤7의 term 이 된다 또한 술어논리의 술어(predicate)에도 적절한 타입을 부여할 수 있다.\n\u0026ldquo;to be a prime number\u0026quot;의 타입 : nat🡒Prop \u0026ldquo;to be a sorted list\u0026quot;의 타입 : (list Z)🡒Prop binary relation ≤ 의 타입 : Z🡒Z🡒Prop \u0026ldquo;to be a transitive relation\u0026quot;의 타입 : (Z🡒Z🡒Prop)🡒Prop 여기서는 type Z를 예시로 사용했지만, 임의의 타입을 변수 A로 나타내 polymorphic type을 표현하는 것 또한 가능함을 알아두자.\nSpecification과 Certified Programs Coq에서 프로그램의 타입은 데이터가 지켜야 할 제약조건들을 propositions으로 나타내 포함할 수 있다. 예로 \u0026ldquo;a prime number tha divides n(of type nat)\u0026ldquo;이라는 type을 보자. 이는 다음과 같이 두 부분으로 나눌 수 있다.\ncomputation-related part : \u0026ldquo;a value of type nat\u0026rdquo; logical part : \u0026ldquo;this value is prime and divides n\u0026rdquo; 이는 결국 n에 의존하게 되며, 이런 종류의 타입을 dependent type이라고 한다. Coq의 표현력에는 이런 dependent type의 존재가 큰 몫을 차지한다고 한다. 책을 좀더 읽어봐야 이해할 수 있을듯.\n같은 맥락에서 \u0026ldquo;n\u0026gt;1을 받아 n의 소인수를 반환하는 함수\u0026quot;의 타입을 Coq 안에서 표현할 수 있다. 이 타입에 해당하는 함수는 입력 n이 조건(nat 타입이고 n\u0026gt;1)을 만족하면 언제나 n의 소인수를 반환한다. Coq의 interactive help를 이용해 이런 함수를 실제로 만들어낼 수 있으며, 이렇게 만들어진 결과물을 certified program이라고 한다. certified program은 크게 두 부분으로 이루어져 있다.\ncomputing information : 어떻게 n의 소인수를 계산해낼 것인가 proof : 그렇게 계산하면 왜 n의 소인수가 나오는가 일단 certified program을 만들고 나면 extraction algorithm을 통해 이를 실제 돌아가는 프로그램의 코드(주로 OCAML 등을 이용한다)로 변환해낼 수 있다. 이렇게 변환된 프로그램은 (증명을 했으므로) 안전함을 보장받을 수 있으며, 최종 결과물에는 proof에 대한 논리전개는 모두 제거되고 실제 동작에 필요한 \u0026lsquo;계산을 어떻게 수행하는가\u0026rsquo;에 대한 정보만 남게 된다.\n","date":"2018년 1월 4일","externalUrl":null,"permalink":"/blog/Coq-01-intro/","section":"Posts","summary":"\u003cInteractive Theorem Proving and Program Development\u003e라는 책을 추천받아 조금씩 읽어보기로 했다. 첫 장에서는 Coq에 대한 간단한 안내를 진행한다. 책의 순서를 따라가면서 하나씩 알아보자\n타입 먼저 Coq에서는 Gallina라는 언어를 사용해 요구사항(Specification)을 서술한다. 대부분의 다른 프로그래밍 언어들과 마찬가지로 타입은 declaration과 typing rule에 의해 정해진다.\n","title":"Coq 01 - Introduction","type":"posts"},{"content":"지난 8월 17일 삼성전자 서울 R\u0026amp;D캠퍼스에서 열린 SCPC(Samsung Collegiate Programming Cup) 본선대회에 참가했다. 2015년 1회 대회부터 계속 본선까지만 진출하고 막상 수상은 못했어서 이번이 사실상 마지막 기회였는데, 졸업 전 마지막 대회라 운이 좋았는지 4등상(+200만원)을 받아갈 수 있었다.\n개인별로 사진과 같은 환경이 제공되었고 저기서 노트북이랑 책상 빼고는 전부 집으로 가져갈 수 있었다. 메인은 역시 기계식키보드\n대회장까지 멀기도 하고 점심시간이 애매해서 간단히 아침 겸 점심을 먹고 출발했는데 막상 가보니 운영측에서 \u0026ldquo;간단한 다과\u0026quot;라는 이름으로 저런 걸 제공했다. 나름 맛있었지만 요새 다이어트를 시도중이어서 조금만 집어먹었다.\n총 4개의 문제 중 123번에서 만점, 4번 문제에서 부분점수 37점을 받았다. 자세한 문제는 어차피 나중에 codeground 사이트에 공개될테니 여기선 생략하자. 굳이 소감을 말하자면 대회 종료 후 문제 출제를 담당하신 교수님께서 출제의도를 설명하셨는데, 4번문제를 설명하실 때 하신 \u0026ldquo;생각하셔야 할 게 5가지 내지 6가지 정도 있는데, 하나라도 빠뜨리면 통과를 못 하도록 설계했어요\u0026rdquo; 라는 말을 듣고 잠깐 넋이 나갔다.\n대회가 끝나고 전체 13등으로 4등상을 받았는데, 나중에 듣기로 3등상 컷이 점수는 같았지만 제출횟수의 차이로 갈렸다고 한다. 약간 아쉽기도 했지만 그렇게 보자면 나보다 더 아쉬울 사람들도 많았고 졸업 전에 결국 상 하나는 받아간다는게 기뻐서 별 생각은 없었다.\n그동안 나름 PS공부를 해봤다고는 하지만 대학 입학 후 이렇다 할 실적이 없는 건 사실이었고, (상 자체가 목적이 될 수 없다는 걸 알면서도) SCPC, LG대회, ICPC 등 나가는 대회마다 참교육당하던 터라 대회 전날까지도 ㅌ모 SNS에 뻘글을 쓰면서 멘탈이 나가기도 했다. 다행히 대회를 진행하는 동안 문제가 꽤 잘 풀렸고 끝나고 나서도 애초에 4번은 인간이 풀라고 낸 문제가 아니었으니까 별 아쉬움 없이 개운한 심정으로 마무리할 수 있었다.\n이제 내년부턴 못 나간다ㅠㅠ\np.s. 끝나고 삼성에서 수상자들한테 한우 사줌. 이쯤 되니 다이어트에 대한 생각은 접고 그냥 막 시켰다. 업진살은 살살 녹았고 살치살은 그보다 조금 더 맛있었다.\n","date":"2017년 8월 19일","externalUrl":null,"permalink":"/blog/SCPC-2017-review/","section":"Posts","summary":"지난 8월 17일 삼성전자 서울 R\u0026D캠퍼스에서 열린 SCPC(Samsung Collegiate Programming Cup) 본선대회에 참가했다. 2015년 1회 대회부터 계속 본선까지만 진출하고 막상 수상은 못했어서 이번이 사실상 마지막 기회였는데, 졸업 전 마지막 대회라 운이 좋았는지 4등상(+200만원)을 받아갈 수 있었다.\n개인별로 사진과 같은 환경이 제공되었고 저기서 노트북이랑 책상 빼고는 전부 집으로 가져갈 수 있었다. 메인은 역시 기계식키보드\n","title":"SCPC 2017 후기","type":"posts"},{"content":"이번 SCTF(Samsung CTF)의 700점짜리 문제로 튜링머신 설계가 나왔다. 총 4개의 문제 중 3개는 일반적인 오토마타 교과서에서 다루는 언어들(ex: \\(L=\\{ 0^n1^n | n \\geq 0 \\}\\))이었지만 마지막 단계인 \\(L=\\{0^p | p \\, \\mathrm{\\,is\\,prime}\\}\\)가 다소 많은 시간을 잡아먹었다. 사실상 소수 판별을 하는 튜링 머신을 설계하라는 문제였는데, 기억상 다른 교재들에서도 이에 대한 명확한 실례는 나와있지 않아 따로 정리했다. 아래 등장하는 그래프의 화살표에서 A/B . P는 \u0026ldquo;현재 state에서 알파벳 A를 읽었을 때, 테이프에 B를 쓰고 P방향(L, 또는 R)으로 이동한다\u0026quot;라는 의미를 가진다. 이제 필요한 서브루틴을 하나씩 구성해 보자.\nstep 1. \\(L_1 = \\{1^n\\#0^m\\ | n은\\,m의\\,약수\\}\\) 먼저 이런 언어를 인식할 수 있는 루틴을 생각해보자. 튜링머신의 테이프를 기준으로 생각하면 다음과 같은 로직을 떠올릴 수 있다.\n가장 왼쪽의 1을 2로 바꾼다 가장 오른쪽의 0을 3으로 바꾼다 모든 0이 3으로 바뀌었는지 확인한다(=마지막으로 바꾼 3의 왼쪽이 #인지 확인) (case 1. 모든 0이 3으로 바뀐 경우) 모든 1이 2로 바뀌었는지 확인한다. → 모두 바뀌었으면 n은 m의 약수, 아직 1이 남아있으면 n은 m의 약수가 아님 (case 2. 아직 0이 남아있는 경우) 더 지울 수 있는 1이 남아있는지 확인한다. → 아직 남아있으면 1. 로, 1이 모두 2로 바뀌어있으면 2들을 모두 1로 바꾼 후 1. 로 돌아간다. 다시 말해, 위 과정은 결국\n1과 0을 하나씩 쌍으로 지워가면서 1이 먼저 다 떨어지면 n개의 1을 다시 리필하고 둘이 동시에 떨어진 경우 n이 m의 약수라고 판정한다 라고 요약할 수 있다. 이를 구현하면 다음과 같다.\n와 ㅁㅊ 내가 해냈어 이제 \\(1^n\\#0^m\\)형식의 문자열을 입력받아 위 흐름을 따라가면, 도달하는 지점이 Yes인지 No인지에 따라 n이 m의 약수인지 여부를 판별할 수 있다.\nStep 2. 문자열 전처리 그런데 입력 문자열은 \\(w=0^p\\)만이 들어온다. 따라서 이를 다루기 편한(?) \\(1^n\\#0^m\\)형태로 바꾸기 위해 전처리 로직을 만들어야 한다. 전처리 로직에서는 입력 문자열의 왼쪽에 \u0026ldquo;\\(1^n\\#\\)\u0026ldquo;를 이어붙이는 역할을 수행하며, 효율성을 고려해 n은 p/2(처음 0 개수의 절반)로 설정했다. 전처리가 완료되면 begin(실질적 시작점)으로 이동한다.\nStep 3. 소수 판별 이제 준비가 거의 끝났다. 전처리기와 약수 판별기가 있으므로 다음과 같은 로직을 수행할 수 있다.\n전처리를 수행해 문자열을 \u0026ldquo;11\u0026hellip;1#00\u0026hellip;0\u0026quot;으로 바꾼다 만들어진 \\(1^n\\#0^p\\) 문자열에서 n이 p의 약수인지 확인한다. 약수가 아니라고 판정되면 제일 왼쪽의 1을 하나 지우고(n-=1), 다시 1. 로 돌아간다 약수라고 판정되면 n=1인지 확인한다. p는 p/2 이하의 약수를 적어도 하나(=1) 가지므로 이 로직은 언젠가 3. 에 도달하게 되고, 이때 n=1인지 여부를 판정함으로써 p가 소수인지 판별할 수 있다. 이를 튜링머신으로 표현하면 아래와 같다.\nS : 실제 시작점\nbegin : 전처리 종료 후 도달상태 = step1 튜링머신의 시작점 S\nYes, No : step1 튜링머신에서의 Yes, No\n위 그림에서 ◎가 전체 튜링머신의 유일한 accept state가 된다.\n","date":"2017년 8월 5일","externalUrl":null,"permalink":"/blog/primality-test-Turing-Machine/","section":"Posts","summary":"이번 SCTF(Samsung CTF)의 700점짜리 문제로 튜링머신 설계가 나왔다. 총 4개의 문제 중 3개는 일반적인 오토마타 교과서에서 다루는 언어들(ex: \\(L=\\{ 0^n1^n | n \\geq 0 \\}\\))이었지만 마지막 단계인 \\(L=\\{0^p | p \\, \\mathrm{\\,is\\,prime}\\}\\)가 다소 많은 시간을 잡아먹었다. 사실상 소수 판별을 하는 튜링 머신을 설계하라는 문제였는데, 기억상 다른 교재들에서도 이에 대한 명확한 실례는 나와있지 않아 따로 정리했다. 아래 등장하는 그래프의 화살표에서 A/B . P는 “현재 state에서 알파벳 A를 읽었을 때, 테이프에 B를 쓰고 P방향(L, 또는 R)으로 이동한다\"라는 의미를 가진다. 이제 필요한 서브루틴을 하나씩 구성해 보자.\n","title":"소수 판별 튜링머신","type":"posts"},{"content":"한국어 · English\n컴퓨터\u0026수학 비슷한 거 공부하는 사람 논문 잘 쓰고 싶어요... Work Experience Quora Core Infrastructure Team Jun. 2025 ~ Present Software Engineer ROK Air Force(Cybersecurity Tech Officer) OOOOOO May. 2022 ~ Feb. 2025 (Non-Disclosure Agreement) Airforce Information System Management Group Jul. 2020 ~ May. 2022 CERT(Computer Emergency Response Team) for Airforce Headquarter Conducted penetration testing and cybersecurity assessments on internal IT systems Identified the impact scope of critical vulnerabilities(e.g. log4j) on the Airforce and supported initial response Performed security audits and compliance checks on systems, personnel, and management regulations Agency for Defense Development(ADD) Jul. 2018 ~ Jul. 2020 Performed research projects in cybersecurity Developed a cyber threat prediction model based on the MITRE ATT\u0026CK framework Education Korea University, School of Cybersecurity (M.S./Ph.D. Student) Sep. 2019 ~ now Korea University (B.S.) Mar. 2014 ~ Feb. 2018 Korea Science Academy of KAIST Mar. 2011 ~ Feb. 2014 High School, graduated Summa Cum Laude in Informatics KAIST Honor's Program Activities KITRI WhiteHat School Mentor Mar. 2025 ~ now International Olympiad in Informatics (IOI) Winter Camp Teaching Assistant Jan. 2018 ~ Jan. 2018 Samsung Software Membership Jun. 2017 ~ Feb. 2018 Samsung’s High Potential Programmer Management Program Conducted algorithm mentoring for university students KITRI Best of the Best Program Jul. 2016 ~ Feb. 2017 Cybersecurity Talent Development Program operated by KITRI(Korea Information Technology Research Institute) Korea University Algorithm Problem Solving Club(AlKor) Involved in founding the club and served as the first Vice President Awards \u0026 Contests Algorithm Samsung Collegiate Programming Cup(SCPC) |Samsung Electronics 2023, 2017 5th Prize(2023), 4th Prize(2017) HYUNDAI MOBIS Algorithm Contest |Hyundai MOBIS 2022, 2021 Bronze Prize(3rd place)(2022), 5th Prize(2021) Woori Coding Festival (Senior Div.) 3rd place |Woori FIS 2023 Military Coding Competition |OSAM 2022, 2020 3rd(2022), 3rd(2020) BRANDI Codename B 2nd place |BRANDI 2020 EDAILY Coding Challenge (Senior Div.) Top Prize |EDAILY 2018 Korean Collegiate Programming Competition Silver Prize (Team: asdf) |MSIT 2017 ACM International Collegiate Programming Contest(ICPC) 2017 Asia Ho Chi Minh City Regional Contest, Third Prize (Team: asdf) 2017 2017 Asia Daejeon Regional Contest, 3rd place (Team: asdf) 2017 * Advanced to 2018 ICPC World Finals(Not participated due to Military Service) 2016 Asia Daejeon Regional Contest, 11th place (Team: PUUUNGSEON) 2016 Security DEFCON CTF 32 Final, 9th place (Team: Cold Fusion) |DEFCON 2024 CCE Cyber Conflict Exercise |NIS, NSR 2024, 2023, 2022, 2021 Public Section, 4th(2024), 7th(2023), 4th(2022), 5th(2021) WhiteHat Contest |MND, COC 2024, 2022, 2021 Military Section, 3rd(2024), 2nd(2022), 5th(2021) Digital Forensic Challenge |KIISC 2022, 2021 Participation Prize(2022), Excellence Prize(2021) Military Cybersecurity Competition Excellence Prize(2nd place) |MND, DCC 2022 Airforce Cyber Warrior Contest Top Prize |ROK Airforce 2021 National Crypto Contest |KISA, NSR 2015, 2014, 2013, 2012 Excellence Prize(2015), Top Prize(2014), Excellence Prize(2013), Excellence Prize(2012) Army Hacking Defence Contest Participation Prize |ROK Army 2015 Others Military AI Contest(MAICON) Excellence Prize |MND 2024 Military Cyber Talpiot Hackathon Top Prize |MND, MSIT 2023 * MND: Ministry of National Defense /\u0026nbsp; MSIT: Ministry of Science and ICT /\u0026nbsp; NIS: National Intelligence Service /\u0026nbsp; NSR: National Security Research Institute /\u0026nbsp; KISA: Korea Internet \u0026 Security Agency /\u0026nbsp; COC: Cyber Operations Command /\u0026nbsp; DCC: Defense Counterintelligence Command /\u0026nbsp; KIISC: Korea Institute of Information Security \u0026 Cryptology Research Paper I'm Listening to your Location! Inferring User Location with Acoustic Side Channels. (WWW '18, 2nd Author) 🔗 Poster Attack Scenario Visualization for Situational Awareness in Cyber Defense Operation. (VizSec '19, 2nd Author) 🔗 ","externalUrl":null,"permalink":"/about/en/","section":"Nyan101 Blog","summary":"한국어 · English\n컴퓨터\u0026수학 비슷한 거 공부하는 사람 논문 잘 쓰고 싶어요... Work Experience Quora Core Infrastructure Team Jun. 2025 ~ Present Software Engineer ROK Air Force(Cybersecurity Tech Officer) OOOOOO May. 2022 ~ Feb. 2025 (Non-Disclosure Agreement) Airforce Information System Management Group Jul. 2020 ~ May. 2022 CERT(Computer Emergency Response Team) for Airforce Headquarter Conducted penetration testing and cybersecurity assessments on internal IT systems Identified the impact scope of critical vulnerabilities(e.g. log4j) on the Airforce and supported initial response Performed security audits and compliance checks on systems, personnel, and management regulations Agency for Defense Development(ADD) Jul. 2018 ~ Jul. 2020 Performed research projects in cybersecurity Developed a cyber threat prediction model based on the MITRE ATT\u0026CK framework Education Korea University, School of Cybersecurity (M.S./Ph.D. Student) Sep. 2019 ~ now Korea University (B.S.) Mar. 2014 ~ Feb. 2018 Korea Science Academy of KAIST Mar. 2011 ~ Feb. 2014 High School, graduated Summa Cum Laude in Informatics KAIST Honor's Program Activities KITRI WhiteHat School Mentor Mar. 2025 ~ now International Olympiad in Informatics (IOI) Winter Camp Teaching Assistant Jan. 2018 ~ Jan. 2018 Samsung Software Membership Jun. 2017 ~ Feb. 2018 Samsung’s High Potential Programmer Management Program Conducted algorithm mentoring for university students KITRI Best of the Best Program Jul. 2016 ~ Feb. 2017 Cybersecurity Talent Development Program operated by KITRI(Korea Information Technology Research Institute) Korea University Algorithm Problem Solving Club(AlKor) Involved in founding the club and served as the first Vice President Awards \u0026 Contests Algorithm Samsung Collegiate Programming Cup(SCPC) |Samsung Electronics 2023, 2017 5th Prize(2023), 4th Prize(2017) HYUNDAI MOBIS Algorithm Contest |Hyundai MOBIS 2022, 2021 Bronze Prize(3rd place)(2022), 5th Prize(2021) Woori Coding Festival (Senior Div.) 3rd place |Woori FIS 2023 Military Coding Competition |OSAM 2022, 2020 3rd(2022), 3rd(2020) BRANDI Codename B 2nd place |BRANDI 2020 EDAILY Coding Challenge (Senior Div.) Top Prize |EDAILY 2018 Korean Collegiate Programming Competition Silver Prize (Team: asdf) |MSIT 2017 ACM International Collegiate Programming Contest(ICPC) 2017 Asia Ho Chi Minh City Regional Contest, Third Prize (Team: asdf) 2017 2017 Asia Daejeon Regional Contest, 3rd place (Team: asdf) 2017 * Advanced to 2018 ICPC World Finals(Not participated due to Military Service) 2016 Asia Daejeon Regional Contest, 11th place (Team: PUUUNGSEON) 2016 Security DEFCON CTF 32 Final, 9th place (Team: Cold Fusion) |DEFCON 2024 CCE Cyber Conflict Exercise |NIS, NSR 2024, 2023, 2022, 2021 Public Section, 4th(2024), 7th(2023), 4th(2022), 5th(2021) WhiteHat Contest |MND, COC 2024, 2022, 2021 Military Section, 3rd(2024), 2nd(2022), 5th(2021) Digital Forensic Challenge |KIISC 2022, 2021 Participation Prize(2022), Excellence Prize(2021) Military Cybersecurity Competition Excellence Prize(2nd place) |MND, DCC 2022 Airforce Cyber Warrior Contest Top Prize |ROK Airforce 2021 National Crypto Contest |KISA, NSR 2015, 2014, 2013, 2012 Excellence Prize(2015), Top Prize(2014), Excellence Prize(2013), Excellence Prize(2012) Army Hacking Defence Contest Participation Prize |ROK Army 2015 Others Military AI Contest(MAICON) Excellence Prize |MND 2024 Military Cyber Talpiot Hackathon Top Prize |MND, MSIT 2023 * MND: Ministry of National Defense /  MSIT: Ministry of Science and ICT /  NIS: National Intelligence Service /  NSR: National Security Research Institute /  KISA: Korea Internet \u0026 Security Agency /  COC: Cyber Operations Command /  DCC: Defense Counterintelligence Command /  KIISC: Korea Institute of Information Security \u0026 Cryptology Research Paper I'm Listening to your Location! Inferring User Location with Acoustic Side Channels. (WWW '18, 2nd Author) 🔗 Poster Attack Scenario Visualization for Situational Awareness in Cyber Defense Operation. (VizSec '19, 2nd Author) 🔗 ","title":"About","type":"page"},{"content":"한국어 · English\n컴퓨터\u0026수학 비슷한 거 공부하는 사람 논문 잘 쓰고 싶어요... Work Experience 쿼라(Quora) Core Infrastructure Jun. 2025 ~ Present Software Engineer 대한민국 공군(사이버전문사관) OOOOOO May. 2022 ~ Feb. 2025 (보안규정에 따라 세부내용 비공개) 공군 정보체계관리단 Jul. 2020 ~ May. 2022 공군본부 및 직할부대 대상 CERT(침해사고대응팀) 내부 IT시스템에 대한 모의해킹 및 사이버보안 점검 수행 log4j등 주요 취약점 대상 공군 영향범위 식별 및 초기대응 지원 시스템 및 인원, 관리규정에 대한 보안감사 컴플라이언스 수행 국방과학연구소(ADD) Jul. 2018 ~ Jul. 2020 사이버보안 연구프로젝트 진행 MITRE ATT\u0026CK 프레임워크 기반 사이버위협 예측모델 개발 Education 고려대학교 정보보호대학원 정보보호학과 석박통합과정 Sep. 2019 ~ now 고려대학교 학부 Mar. 2014 ~ Feb. 2018 KAIST 부설 한국과학영재학교(KSA) Mar. 2011 ~ Feb. 2014 정보과학 분야 최우등 졸업 KAIST Honor's Program(대학과목 선이수 제도) 참여 Activities 교외 KITRI 화이트햇 스쿨 멘토 Mar. 2025 ~ now 정보올림피아드(International Olympiad in Informatics) 겨울 계절학교 조교 Jan. 2018 ~ Jan. 2018 삼성 소프트웨어 멤버십(Samsung Software Membership) 수료 Jun. 2017 ~ Feb. 2018 SW 역량 테스트 Expert(C형) 등급 인정 대학생 대상 알고리즘 멘토링 진행 KITRI 차세대 보안리더 양성 프로그램(Best of the Best) 5기 수료 Jul. 2016 ~ Feb. 2017 30인 선발 (취약점분석 Track) 교내 고려대학교 학과 알고리즘 동아리 AlKor 1대 부회장 Awards \u0026 Contests Algorithm 삼성 대학(원)생 프로그래밍 경진대회(SCPC) |삼성전자 2023, 2017 5등상(2023), 4등상(2017) 현대모비스(MOBIS) 알고리즘 경진대회 |현대모비스 2022, 2021 동상(3위)(2022), 5등상(2021) Woori 코딩 페스티벌 성인부 장려상(3위) |우리FIS 2023 군장병 코딩경진대회 |국방오픈소스아카데미 2022, 2020 3위(2022), 3위(2020) 브랜디 코딩대회 코드네임 B 2등상 |BRANDI 2020 이데일리 Coding Challenge 성인부 대상 |EDAILY 2018 한국 대학생 프로그래밍 경시대회 은상 (Team: asdf) |과학기술정보통신부 2017 ACM 대학생 프로그래밍 경시대회(ICPC) 2017 Asia Ho Chi Minh City Regional Contest, Third Prize (Team: asdf) 2017 2017 Asia Daejeon Regional Contest, 3rd place (Team: asdf) 2017 * 2018 ICPC World Finals 진출권 획득(군복무 문제로 미참여) 2016 Asia Daejeon Regional Contest, 11th place (Team: PUUUNGSEON) 2016 Security DEFCON CTF 32 Final, 9위 (Team: Cold Fusion) |DEFCON 2024 사이버공격방어대회(CCE) |국가정보원, NSR 2024, 2023, 2022, 2021 공공분야 4위(2024), 7위(2023), 4위(2022), 5위(2021) 대한민국 화이트햇 콘테스트 |국방부, 사이버작전사령부 2024, 2022, 2021 국방트랙 장려상(3위)(2024), 우수상(2위)(2022), 5위(2021) 디지털 포렌식 챌린지(DFC) |한국정보보호학회 2022, 2021 장려상(2022), 우수상(2021) 국방 사이버보안 경진대회 우수상(2위) |국방부, 군사안보지원사령부 2022 공군 사이버전사 경연대회 최우수상(1위) |공군본부 2021 암호분석경진대회 일반부 우수상 |국방암호기술특화연구센터 2020 부채널분석경진대회 일반부 최우수상 |ETRI, NSR 2020 국가암호공모전 |KISA, NSR 2015, 2014, 2013, 2012 우수상(2015), 최우수상(2014), 우수상(2013), 우수상(2012) 육군해킹방어대회 장려상 |육군본부 2015 Others MAICON 국방 AI 경진대회 우수상 |국방부 2024 군 사이버보안 전문인력 양성 해커톤 최우수상(1위) |국방부, 과학기술정보통신부 2023 공군 창의·혁신 아이디어 공모 해커톤 장려상 |공군본부 2022 Research Paper I'm Listening to your Location! Inferring User Location with Acoustic Side Channels. (WWW '18, 2nd Author) 🔗 Poster Attack Scenario Visualization for Situational Awareness in Cyber Defense Operation. (VizSec '19, 2nd Author) 🔗 ","externalUrl":null,"permalink":"/about/","section":"Nyan101 Blog","summary":"한국어 · English\n컴퓨터\u0026수학 비슷한 거 공부하는 사람 논문 잘 쓰고 싶어요... Work Experience 쿼라(Quora) Core Infrastructure Jun. 2025 ~ Present Software Engineer 대한민국 공군(사이버전문사관) OOOOOO May. 2022 ~ Feb. 2025 (보안규정에 따라 세부내용 비공개) 공군 정보체계관리단 Jul. 2020 ~ May. 2022 공군본부 및 직할부대 대상 CERT(침해사고대응팀) 내부 IT시스템에 대한 모의해킹 및 사이버보안 점검 수행 log4j등 주요 취약점 대상 공군 영향범위 식별 및 초기대응 지원 시스템 및 인원, 관리규정에 대한 보안감사 컴플라이언스 수행 국방과학연구소(ADD) Jul. 2018 ~ Jul. 2020 사이버보안 연구프로젝트 진행 MITRE ATT\u0026CK 프레임워크 기반 사이버위협 예측모델 개발 Education 고려대학교 정보보호대학원 정보보호학과 석박통합과정 Sep. 2019 ~ now 고려대학교 학부 Mar. 2014 ~ Feb. 2018 KAIST 부설 한국과학영재학교(KSA) Mar. 2011 ~ Feb. 2014 정보과학 분야 최우등 졸업 KAIST Honor's Program(대학과목 선이수 제도) 참여 Activities 교외 KITRI 화이트햇 스쿨 멘토 Mar. 2025 ~ now 정보올림피아드(International Olympiad in Informatics) 겨울 계절학교 조교 Jan. 2018 ~ Jan. 2018 삼성 소프트웨어 멤버십(Samsung Software Membership) 수료 Jun. 2017 ~ Feb. 2018 SW 역량 테스트 Expert(C형) 등급 인정 대학생 대상 알고리즘 멘토링 진행 KITRI 차세대 보안리더 양성 프로그램(Best of the Best) 5기 수료 Jul. 2016 ~ Feb. 2017 30인 선발 (취약점분석 Track) 교내 고려대학교 학과 알고리즘 동아리 AlKor 1대 부회장 Awards \u0026 Contests Algorithm 삼성 대학(원)생 프로그래밍 경진대회(SCPC) |삼성전자 2023, 2017 5등상(2023), 4등상(2017) 현대모비스(MOBIS) 알고리즘 경진대회 |현대모비스 2022, 2021 동상(3위)(2022), 5등상(2021) Woori 코딩 페스티벌 성인부 장려상(3위) |우리FIS 2023 군장병 코딩경진대회 |국방오픈소스아카데미 2022, 2020 3위(2022), 3위(2020) 브랜디 코딩대회 코드네임 B 2등상 |BRANDI 2020 이데일리 Coding Challenge 성인부 대상 |EDAILY 2018 한국 대학생 프로그래밍 경시대회 은상 (Team: asdf) |과학기술정보통신부 2017 ACM 대학생 프로그래밍 경시대회(ICPC) 2017 Asia Ho Chi Minh City Regional Contest, Third Prize (Team: asdf) 2017 2017 Asia Daejeon Regional Contest, 3rd place (Team: asdf) 2017 * 2018 ICPC World Finals 진출권 획득(군복무 문제로 미참여) 2016 Asia Daejeon Regional Contest, 11th place (Team: PUUUNGSEON) 2016 Security DEFCON CTF 32 Final, 9위 (Team: Cold Fusion) |DEFCON 2024 사이버공격방어대회(CCE) |국가정보원, NSR 2024, 2023, 2022, 2021 공공분야 4위(2024), 7위(2023), 4위(2022), 5위(2021) 대한민국 화이트햇 콘테스트 |국방부, 사이버작전사령부 2024, 2022, 2021 국방트랙 장려상(3위)(2024), 우수상(2위)(2022), 5위(2021) 디지털 포렌식 챌린지(DFC) |한국정보보호학회 2022, 2021 장려상(2022), 우수상(2021) 국방 사이버보안 경진대회 우수상(2위) |국방부, 군사안보지원사령부 2022 공군 사이버전사 경연대회 최우수상(1위) |공군본부 2021 암호분석경진대회 일반부 우수상 |국방암호기술특화연구센터 2020 부채널분석경진대회 일반부 최우수상 |ETRI, NSR 2020 국가암호공모전 |KISA, NSR 2015, 2014, 2013, 2012 우수상(2015), 최우수상(2014), 우수상(2013), 우수상(2012) 육군해킹방어대회 장려상 |육군본부 2015 Others MAICON 국방 AI 경진대회 우수상 |국방부 2024 군 사이버보안 전문인력 양성 해커톤 최우수상(1위) |국방부, 과학기술정보통신부 2023 공군 창의·혁신 아이디어 공모 해커톤 장려상 |공군본부 2022 Research Paper I'm Listening to your Location! Inferring User Location with Acoustic Side Channels. (WWW '18, 2nd Author) 🔗 Poster Attack Scenario Visualization for Situational Awareness in Cyber Defense Operation. (VizSec '19, 2nd Author) 🔗 ","title":"About","type":"page"}]