<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://dosp74.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dosp74.github.io/" rel="alternate" type="text/html" /><updated>2026-02-15T14:58:12+00:00</updated><id>https://dosp74.github.io/feed.xml</id><title type="html">개발 공부 기록</title><subtitle>개발과 성장을 기록하는 공간</subtitle><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><entry><title type="html">[알고리즘] 이분 탐색(Binary Search)과 매개변수 탐색(Parametric Search)</title><link href="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/binary-and-parametric-search/" rel="alternate" type="text/html" title="[알고리즘] 이분 탐색(Binary Search)과 매개변수 탐색(Parametric Search)" /><published>2026-02-15T13:14:00+00:00</published><updated>2026-02-15T13:14:00+00:00</updated><id>https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/binary-and-parametric-search</id><content type="html" xml:base="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/binary-and-parametric-search/"><![CDATA[<p>이분 탐색(Binary Search)은 탐색 공간을 절반씩 줄여가며 원하는 값을 찾는 알고리즘이다. <strong>정렬된 배열에서 값을 찾는 알고리즘</strong> 이라고 알고 있지만, 실제로 코딩 테스트에서 더 중요한 형태는 <code class="language-plaintext highlighter-rouge">매개변수 탐색(Parametric Search)</code>이다.</p>

<p>즉, 단순 배열 탐색이 아닌, <strong>결정 문제(Decision Problem)</strong> 를 반복적으로 풀어가며 최적해를 찾아야 하는 경우가 많다.</p>

<h2 id="이분-탐색binary-search">이분 탐색(Binary Search)</h2>

<p>정렬된 배열에서 특정 값을 찾는 기본적인 방식이다.</p>

<ul>
  <li>탐색 범위를 left ~ right로 설정</li>
  <li>mid를 기준으로 탐색 범위를 절반으로 줄임</li>
  <li>시간 복잡도: O(log n)</li>
</ul>

<p>이러한 형태의 알고리즘이 우리가 많이 아는 이분 탐색이라는 알고리즘인데, 배열이 아닌 값의 범위를 탐색해야 하는 문제로 발전시켜야 한다.</p>

<h2 id="매개변수-탐색parametric-search">매개변수 탐색(Parametric Search)</h2>

<p>매개변수 탐색은 다음과 같은 구조를 가진다.</p>

<ol>
  <li>어떤 값 <code class="language-plaintext highlighter-rouge">x</code>가 주어졌을 때</li>
  <li>조건을 만족하는지 판별하는 함수 <code class="language-plaintext highlighter-rouge">f(x)</code>가 존재하고</li>
  <li>그 판별 결과가 <strong>단조(monotonic)</strong> 일 때</li>
  <li>이분 탐색으로 최적의 x를 찾는다.</li>
</ol>

<p>즉,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>가능 가능 가능 가능 불가능 불가능 불가능
</code></pre></div></div>

<p>이런 구조를 찾는 것이 핵심이다.</p>

<h2 id="대표-예시">대표 예시</h2>

<p><img src="/assets/images/2026-02-15/2026-02-15-baekjoon-1654.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/1654">https://www.acmicpc.net/problem/1654</a></p>

<h3 id="문제-요약">문제 요약</h3>

<p>K개의 랜선을 길이 L로 잘라 N개 이상 만들 수 있을 때, 가능한 L의 최댓값을 구하는 문제이다.</p>

<p>랜선 길이를 L이라고 하면,</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>f(L) = sum(LANs[i] / L)
</code></pre></div></div>

<p>L이 커질수록 만들 수 있는 개수는 줄어든다.</p>

<p>즉, L이 커지면 f(L)은 줄어드는 단조 감소 함수의 형태이다. 따라서 조건 <code class="language-plaintext highlighter-rouge">f(L) &gt;= N을 만족하는 최대 L</code>을 찾으면 된다.</p>

<p>어떤 L에서 조건을 만족하면 그보다 작은 L은 항상 만족하고, 어떤 L에서 만족하지 않으면 그보다 큰 L도 항상 만족하지 않는다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    
    <span class="kt">int</span> <span class="n">K</span><span class="p">,</span> <span class="n">N</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">K</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span>
    
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">long</span> <span class="kt">long</span><span class="o">&gt;</span> <span class="n">LANs</span><span class="p">(</span><span class="n">K</span><span class="p">);</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">maxLength</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">K</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">LANs</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="n">maxLength</span> <span class="o">=</span> <span class="n">max</span><span class="p">(</span><span class="n">maxLength</span><span class="p">,</span> <span class="n">LANs</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
    <span class="p">}</span>
    
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">left</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">right</span> <span class="o">=</span> <span class="n">maxLength</span><span class="p">;</span>
    <span class="kt">long</span> <span class="kt">long</span> <span class="n">answer</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="k">while</span> <span class="p">(</span><span class="n">left</span> <span class="o">&lt;=</span> <span class="n">right</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">long</span> <span class="kt">long</span> <span class="n">mid</span> <span class="o">=</span> <span class="p">(</span><span class="n">left</span> <span class="o">+</span> <span class="n">right</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
        <span class="kt">long</span> <span class="kt">long</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        
        <span class="k">for</span> <span class="p">(</span><span class="kt">long</span> <span class="kt">long</span> <span class="n">l</span> <span class="o">:</span> <span class="n">LANs</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">cnt</span> <span class="o">+=</span> <span class="n">l</span> <span class="o">/</span> <span class="n">mid</span><span class="p">;</span>
        <span class="p">}</span>
        
        <span class="k">if</span> <span class="p">(</span><span class="n">cnt</span> <span class="o">&gt;=</span> <span class="n">N</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">answer</span> <span class="o">=</span> <span class="n">mid</span><span class="p">;</span>
            <span class="n">left</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="n">right</span> <span class="o">=</span> <span class="n">mid</span> <span class="o">-</span> <span class="mi">1</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">answer</span><span class="p">;</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>왜 정렬 없이 이분 탐색이 가능할까? 우리는 배열을 탐색하는 것이 아니라 랜선의 길이라는 값의 범위(1 ~ maxLength)를 탐색하고 있다.</p>

<p>또한 판별 함수 f(L)이 단조의 형태를 띄고 있기 때문에 mid 기준으로 탐색 범위를 안전하게 버릴 수 있다.</p>

<p>핵심은 문제 해결의 흐름에서 해를 구하는 과정이 단조인지 아닌지에 달려 있다.</p>

<p>이분 탐색은 단순한 알고리즘 문제 풀이에 사용하는 것을 넘어서, 서버 자원 할당 최적화, 네트워크의 대역폭 분배, 최대 처리량 산정과 같은 실무에도 연결하여 적용될 수 있다.</p>

<p>나열한 예시들은 전부 어떤 설정 값 x가 주어졌을 때 조건을 만족하는지를 검사하여 빠르게 최적해를 찾을 수 있는 구조를 지니고 있다.</p>

<p>최대 동시 접속자 수를 만족하는 최소 서버의 수, 지연 시간을 넘지 않는 최대 요청량, 일정 시간 내 처리 가능한 최소 인스턴스 수 등의 다양한 문제를 결정 문제와 단조 구조로 환원하면 해결할 수 있다. 물론, 대략적인 해를 구하는 데 이용하고 현실에서 발생할 수 있는 여러 가지 변수들 또한 고려해야 할 것이다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="알고리즘" /><category term="이분 탐색" /><category term="매개변수 탐색" /><category term="백준" /><category term="1654번" /><summary type="html"><![CDATA[이분 탐색(Binary Search)은 탐색 공간을 절반씩 줄여가며 원하는 값을 찾는 알고리즘이다. 정렬된 배열에서 값을 찾는 알고리즘 이라고 알고 있지만, 실제로 코딩 테스트에서 더 중요한 형태는 매개변수 탐색(Parametric Search)이다.]]></summary></entry><entry><title type="html">[C++] STL unordered_map</title><link href="https://dosp74.github.io/c++/c++-stl-unordered_map/" rel="alternate" type="text/html" title="[C++] STL unordered_map" /><published>2026-01-19T05:21:00+00:00</published><updated>2026-01-19T05:21:00+00:00</updated><id>https://dosp74.github.io/c++/c++-stl-unordered_map</id><content type="html" xml:base="https://dosp74.github.io/c++/c++-stl-unordered_map/"><![CDATA[<p>C++ STL의 unordered_map은 <strong>Key-Value 형태의 데이터를 해시 기반으로 저장하는 연관 컨테이너</strong> 이다.
이전 포스팅에서 살펴본 <code class="language-plaintext highlighter-rouge">map</code>과 기능적으로는 유사하지만, <strong>정렬을 하지 않고 속도에 집중한 컨테이너</strong> 라는 점이 가장 큰 차이이다.</p>

<p><code class="language-plaintext highlighter-rouge">std::unordered_map</code>은 내부적으로 <strong>해시 테이블</strong> 을 사용한다.</p>

<h3 id="특징">특징</h3>

<ul>
  <li>Key 중복 X</li>
  <li>정렬 X</li>
  <li>삽입/삭제/탐색: O(1)</li>
  <li>최악의 경우: O(n) (해시 충돌이 심한 경우)</li>
</ul>

<p>속도는 빠르지만, 순서가 필요하면 사용하면 안 된다.</p>

<h3 id="unordered_map의-헤더와-선언">unordered_map의 헤더와 선언</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;unordered_map&gt;</span><span class="cp">
</span>
<span class="n">unordered_map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">um</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="주요-멤버-함수">주요 멤버 함수</h3>

<table>
  <thead>
    <tr>
      <th>함수명</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>insert({k, v})</td>
      <td>(k, v) 쌍을 삽입</td>
    </tr>
    <tr>
      <td>um[k]</td>
      <td>k에 해당하는 value 접근</td>
    </tr>
    <tr>
      <td>find(k)</td>
      <td>key k를 가리키는 iterator 반환</td>
    </tr>
    <tr>
      <td>erase(k)</td>
      <td>key k에 해당하는 원소 삭제</td>
    </tr>
    <tr>
      <td>size()</td>
      <td>저장된 원소 개수 반환</td>
    </tr>
    <tr>
      <td>empty()</td>
      <td>비어 있는지 확인</td>
    </tr>
  </tbody>
</table>

<p><code class="language-plaintext highlighter-rouge">map</code>과 인터페이스는 거의 동일하다.</p>

<h3 id="예시-코드">예시 코드</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;unordered_map&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">unordered_map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">um</span><span class="p">;</span>

    <span class="n">um</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="s">"apple"</span><span class="p">});</span>
    <span class="n">um</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">3</span><span class="p">,</span> <span class="s">"banana"</span><span class="p">});</span>
    <span class="n">um</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="s">"orange"</span><span class="p">});</span>

    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">:</span> <span class="n">um</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">": "</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>🔽 출력 :</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2: orange
3: banana
1: apple
</code></pre></div></div>

<p>출력 순서는 보장되지 않는다.</p>

<h3 id="operator-사용-시-주의점">operator[] 사용 시 주의점</h3>

<p>unordered_map에서도 operator[]는 존재하지 않는 key를 자동 생성한다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">unordered_map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">um</span><span class="p">;</span>

<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">um</span><span class="p">[</span><span class="mi">5</span><span class="p">];</span> <span class="c1">// 0 (자동 삽입)</span>
</code></pre></div></div>

<p>따라서 map과 마찬가지로 단순 조회 목적인 경우에는 find()를 사용하는 것이 좋지만, 이는 map의 크기가 커지는 원인이 되기도 한다.</p>

<h2 id="unordered_map이-빠른-이유">unordered_map이 빠른 이유</h2>

<p>map은 트리 기반(O(logn))인 반면, unordered_map은 해시 기반(O(1))이다.
Key를 해시 함수에 넣어 바로 버킷 위치를 계산하기 때문에 트리를 타고 내려갈 필요가 없다.</p>

<p>다만 여러 Key가 같은 해시 값을 가질 때 해시 충돌이 발생할 수 있다.
충돌이 많아지면 성능이 저하되며, 최악의 경우 O(n) 시간 복잡도를 보이지만 STL의 기본 해시 함수는 대부분의 상황에서 충분히 안정적이다.</p>

<h2 id="map과-unordered_map-정리">map과 unordered_map 정리</h2>

<table>
  <thead>
    <tr>
      <th>항목</th>
      <th>map</th>
      <th>unordered_map</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>내부 구조</td>
      <td>트리</td>
      <td>해시 테이블</td>
    </tr>
    <tr>
      <td>정렬 여부</td>
      <td>O (Key 기준 자동 정렬)</td>
      <td>X</td>
    </tr>
    <tr>
      <td>탐색 속도</td>
      <td>O(logn)</td>
      <td>평균 O(1)</td>
    </tr>
    <tr>
      <td>순회 순서</td>
      <td>Key 오름차순으로 보장됨</td>
      <td>보장되지 않음</td>
    </tr>
    <tr>
      <td>범위 탐색</td>
      <td>가능</td>
      <td>불가능</td>
    </tr>
  </tbody>
</table>

<h3 id="unordered_map은-언제-쓰일까">unordered_map은 언제 쓰일까?</h3>

<ul>
  <li>정렬이 전혀 필요 없을 때</li>
  <li>빠른 삽입/탐색이 중요한 경우</li>
  <li>빈도 카운트, 존재 여부 체크 등</li>
</ul>

<h3 id="마무리">마무리</h3>

<p>unordered_map은 속도 최우선 컨테이너이다. 평균 O(1)의 빠른 연산을 보여주며, 순서가 필요하면 map, 속도가 중요하면 unordered_map을 사용하면 된다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="C++" /><category term="C++" /><category term="STL" /><category term="unordered_map" /><category term="해시 테이블" /><summary type="html"><![CDATA[C++ STL의 unordered_map은 Key-Value 형태의 데이터를 해시 기반으로 저장하는 연관 컨테이너 이다. 이전 포스팅에서 살펴본 map과 기능적으로는 유사하지만, 정렬을 하지 않고 속도에 집중한 컨테이너 라는 점이 가장 큰 차이이다.]]></summary></entry><entry><title type="html">[C++] STL map</title><link href="https://dosp74.github.io/c++/c++-stl-map/" rel="alternate" type="text/html" title="[C++] STL map" /><published>2026-01-18T07:47:00+00:00</published><updated>2026-01-18T07:47:00+00:00</updated><id>https://dosp74.github.io/c++/c++-stl-map</id><content type="html" xml:base="https://dosp74.github.io/c++/c++-stl-map/"><![CDATA[<p>C++ STL의 map은 Key-Value(키-값) 쌍을 저장하는 연관 컨테이너(associative container)이다. map을 제대로 이해하려면, 그 기반이 되는 <code class="language-plaintext highlighter-rouge">pair</code>부터 짚고 넘어가는 것이 좋다.</p>

<h2 id="pair">pair</h2>

<p><code class="language-plaintext highlighter-rouge">std::pair</code>는 서로 다른 두 개의 값을 하나의 객체로 묶어주는 구조체이다. STL에서 아주 자주 사용되며, map의 내부 요소 타입도 pair이다.</p>

<h3 id="pair의-선언과-사용">pair의 선언과 사용</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;utility&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">p</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="s">"apple"</span><span class="p">};</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>🔽 출력 :</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1
apple
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">first</code>: 첫 번째 값</p>

<p><code class="language-plaintext highlighter-rouge">second</code>: 두 번째 값</p>

<h3 id="pair는-언제-쓰일까">pair는 언제 쓰일까?</h3>

<ul>
  <li>두 값을 하나로 묶어 관리하고 싶을 때</li>
  <li>좌표 <strong>(x, y)</strong></li>
  <li>(인덱스, 값)</li>
  <li>(key, value) 형태의 데이터</li>
</ul>

<p>특히 map에서는 모든 원소가 pair&lt;const Key, Value&gt; 형태로 저장된다.</p>

<h2 id="map">map</h2>

<p><code class="language-plaintext highlighter-rouge">std::map</code>은 Key를 기준으로 정렬된 상태를 유지하는 컨테이너이다. 내부적으로는 이진 탐색 트리(보통 Red-Black Tree)로 구현되어 있다.</p>

<h3 id="핵심-특징">핵심 특징</h3>

<ul>
  <li>Key는 중복 불가</li>
  <li>자동 정렬 (기본: 오름차순)</li>
  <li>삽입/삭제/탐색: O(logn)</li>
</ul>

<h3 id="map의-헤더와-선언">map의 헤더와 선언</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp">
</span>
<span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">int</code>: Key</p>

<p><code class="language-plaintext highlighter-rouge">string</code>: Value</p>

<h3 id="주요-멤버-함수">주요 멤버 함수</h3>

<table>
  <thead>
    <tr>
      <th>함수명</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>insert({k, v})</td>
      <td>(k, v) 쌍을 map에 삽입</td>
    </tr>
    <tr>
      <td>m[k]</td>
      <td>k에 해당하는 value 접근</td>
    </tr>
    <tr>
      <td>find(k)</td>
      <td>key k를 가리키는 iterator 반환</td>
    </tr>
    <tr>
      <td>erase(k)</td>
      <td>key k에 해당하는 원소 삭제</td>
    </tr>
    <tr>
      <td>size()</td>
      <td>저장된 원소 개수 반환</td>
    </tr>
    <tr>
      <td>empty()</td>
      <td>map이 비어 있는지 확인</td>
    </tr>
    <tr>
      <td>begin() / end()</td>
      <td>시작 / 끝 iterator 반환</td>
    </tr>
  </tbody>
</table>

<h3 id="예시-코드">예시 코드</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;map&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="n">string</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>

    <span class="n">m</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">1</span><span class="p">,</span> <span class="s">"apple"</span><span class="p">});</span>
    <span class="n">m</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">3</span><span class="p">,</span> <span class="s">"banana"</span><span class="p">});</span>
    <span class="n">m</span><span class="p">.</span><span class="n">insert</span><span class="p">({</span><span class="mi">2</span><span class="p">,</span> <span class="s">"orange"</span><span class="p">});</span>

    <span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">it</span> <span class="o">=</span> <span class="n">m</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span> <span class="n">it</span> <span class="o">!=</span> <span class="n">m</span><span class="p">.</span><span class="n">end</span><span class="p">();</span> <span class="n">it</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">": "</span> <span class="o">&lt;&lt;</span> <span class="n">it</span><span class="o">-&gt;</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>🔽 출력 :</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1: apple
2: orange
3: banana
</code></pre></div></div>

<p>Key 기준으로 자동 정렬되어 출력되는 것을 확인할 수 있다.</p>

<h3 id="operator-사용-시-주의점">operator[] 사용 시 주의점</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>

<span class="n">m</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">m</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span> <span class="c1">// 5</span>
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">주의</code></p>

<p>m[key] 형태로 접근할 때, key가 존재하지 않을 경우 자동으로 생성된다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>

<span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">m</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span> <span class="c1">// 0 (자동 삽입)</span>
</code></pre></div></div>

<p>값 타입의 기본값으로 삽입되며, 단순 조회 목적이라면 find()를 사용하는 것이 안전하다.</p>

<h3 id="find-사용-예시">find() 사용 예시</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">m</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="o">!=</span> <span class="n">m</span><span class="p">.</span><span class="n">end</span><span class="p">())</span> <span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"존재함"</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"존재하지 않음"</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="map-순회-향상된-for-문">map 순회 (향상된 for 문)</h3>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(</span><span class="k">auto</span> <span class="n">p</span> <span class="o">:</span> <span class="n">m</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>여기서 <code class="language-plaintext highlighter-rouge">p</code>는 pair&lt;const Key, Value&gt; 타입이다.</p>

<h3 id="map-정렬-기준-바꾸기">map 정렬 기준 바꾸기</h3>

<p>기본은 오름차순이지만, 비교 함수를 지정할 수 있다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">map</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="p">,</span> <span class="n">greater</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">m</span><span class="p">;</span>
</code></pre></div></div>

<p>-&gt; Key 기준 내림차순 정렬</p>

<h2 id="실전">실전</h2>

<p><img src="/assets/images/2026-01-18/2026-01-18-baekjoon-20291.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/20291">https://www.acmicpc.net/problem/20291</a></p>

<p>map에 대한 개념을 점검해볼 수 있는 좋은 문제인 것 같다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    
    <span class="kt">int</span> <span class="n">N</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span>
    
    <span class="n">map</span><span class="o">&lt;</span><span class="n">string</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;</span> <span class="n">m</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">string</span> <span class="n">name</span><span class="p">;</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">name</span><span class="p">;</span>
        
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">name</span><span class="p">.</span><span class="n">length</span><span class="p">();</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">name</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="sc">'.'</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">string</span> <span class="n">extension</span> <span class="o">=</span> <span class="n">name</span><span class="p">.</span><span class="n">substr</span><span class="p">(</span><span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="n">name</span><span class="p">.</span><span class="n">length</span><span class="p">());</span>
                <span class="n">m</span><span class="p">[</span><span class="n">extension</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
                
                <span class="k">break</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="k">const</span> <span class="k">auto</span><span class="o">&amp;</span> <span class="n">p</span> <span class="o">:</span> <span class="n">m</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="마무리">마무리</h3>

<p><code class="language-plaintext highlighter-rouge">map</code>은 <code class="language-plaintext highlighter-rouge">pair</code> 기반의 연관 컨테이너이다. Key는 중복이 불가하며, 자동으로 Key 기준으로 정렬된다. 삽입/삭제/탐색 시간은 O(logn)이며, 단순 조회할 때는 find()가 안전하다.</p>

<p>Key로 정렬되는 사전(Dictionary)이라고 생각하면 편하다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="C++" /><category term="C++" /><category term="STL" /><category term="pair" /><category term="map" /><category term="백준" /><category term="20291번" /><summary type="html"><![CDATA[C++ STL의 map은 Key-Value(키-값) 쌍을 저장하는 연관 컨테이너(associative container)이다. map을 제대로 이해하려면, 그 기반이 되는 pair부터 짚고 넘어가는 것이 좋다.]]></summary></entry><entry><title type="html">[C++] STL deque</title><link href="https://dosp74.github.io/c++/c++-stl-deque/" rel="alternate" type="text/html" title="[C++] STL deque" /><published>2026-01-08T09:20:00+00:00</published><updated>2026-01-08T09:20:00+00:00</updated><id>https://dosp74.github.io/c++/c++-stl-deque</id><content type="html" xml:base="https://dosp74.github.io/c++/c++-stl-deque/"><![CDATA[<p>C++ STL의 <strong>deque</strong> 은 <strong>양쪽 끝에서 삽입과 삭제가 모두 가능한 컨테이너</strong> 이다.
이름은 <strong>double-ended queue</strong> 의 약자이며, vector와 queue의 특징을 적절히 섞은 자료구조라고 볼 수 있다.</p>

<hr />

<p><strong>std::deque</strong> 은 <strong>앞(front)과 뒤(back) 양쪽에서 모두 O(1) 시간에 삽입/삭제가 가능</strong> 하다.
또한 <code class="language-plaintext highlighter-rouge">vector</code>처럼 인덱스를 활용한 <strong>임의 접근(random access)</strong> 도 지원한다.</p>

<h3 id="특징-요약">특징 요약</h3>

<ul>
  <li>앞/뒤 삽입, 삭제 -&gt; 빠름</li>
  <li>인덱스 접근 -&gt; 가능</li>
</ul>

<h2 id="내부-구조">내부 구조</h2>

<p>deque은 단일 연속 메모리 블록이 아닌, 여러 개의 작은 블록을 관리하는 구조로 구현되어 있다. 이 덕분에 양쪽 끝에서의 연산이 효율적이지만, 메모리 구조는 vector보다 다소 복잡하다.</p>

<p>시간 복잡도를 정리하면 다음과 같다.</p>

<ul>
  <li>앞/뒤 삽입, 삭제: O(1)</li>
  <li>임의 접근: O(1)</li>
  <li>중간 삽입/삭제: O(n)</li>
</ul>

<h2 id="헤더-및-선언">헤더 및 선언</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;deque&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="n">deque</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dq</span><span class="p">;</span>
</code></pre></div></div>

<h2 id="주요-멤버-함수">주요 멤버 함수</h2>

<table>
  <thead>
    <tr>
      <th>함수명</th>
      <th>설명</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>push_back(val)</td>
      <td>뒤에 요소 추가</td>
    </tr>
    <tr>
      <td>push_front(val)</td>
      <td>앞에 요소 추가</td>
    </tr>
    <tr>
      <td>pop_back()</td>
      <td>뒤 요소 제거</td>
    </tr>
    <tr>
      <td>pop_front()</td>
      <td>앞 요소 제거</td>
    </tr>
    <tr>
      <td>front() / back()</td>
      <td>앞 / 뒤 요소 반환</td>
    </tr>
    <tr>
      <td>size()</td>
      <td>요소 개수 반환</td>
    </tr>
    <tr>
      <td>empty()</td>
      <td>비어 있는지 확인</td>
    </tr>
    <tr>
      <td>operator[]</td>
      <td>인덱스 기반 접근</td>
    </tr>
  </tbody>
</table>

<h2 id="예시-코드">예시 코드</h2>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;deque&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">deque</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">dq</span><span class="p">;</span>

    <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
    <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="mi">2</span><span class="p">);</span>
    <span class="n">dq</span><span class="p">.</span><span class="n">push_front</span><span class="p">(</span><span class="mi">10</span><span class="p">);</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"front: "</span> <span class="o">&lt;&lt;</span> <span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"back: "</span> <span class="o">&lt;&lt;</span> <span class="n">dq</span><span class="p">.</span><span class="n">back</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>

    <span class="n">dq</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">dq</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">dq</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>🔽 출력 :</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>front: 10
back: 2
1 2
</code></pre></div></div>

<h2 id="실전">실전</h2>

<p><img src="/assets/images/2026-01-08/2026-01-08-baekjoon-2346.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/2346">https://www.acmicpc.net/problem/2346</a></p>

<p>이 문제는 양쪽 회전이 가능한 자료구조가 필요하다.
앞과 뒤에서 모두 원소를 꺼내고 다시 넣어야 하므로, deque이 가장 적합하다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;deque&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="kt">int</span> <span class="n">N</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span><span class="p">;</span>

    <span class="n">deque</span><span class="o">&lt;</span><span class="n">pair</span><span class="o">&lt;</span><span class="kt">int</span><span class="p">,</span> <span class="kt">int</span><span class="o">&gt;&gt;</span> <span class="n">dq</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;=</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">number</span><span class="p">;</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">number</span><span class="p">;</span>
        <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">({</span><span class="n">number</span><span class="p">,</span> <span class="n">i</span><span class="p">});</span>
    <span class="p">}</span>

    <span class="kt">bool</span> <span class="n">frontTurn</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>

    <span class="k">while</span> <span class="p">(</span><span class="n">dq</span><span class="p">.</span><span class="n">size</span><span class="p">()</span> <span class="o">!=</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="kt">int</span> <span class="n">number</span><span class="p">;</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">frontTurn</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">();</span>
            <span class="n">number</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span><span class="p">;</span>
            <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
            <span class="n">dq</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="k">auto</span> <span class="n">p</span> <span class="o">=</span> <span class="n">dq</span><span class="p">.</span><span class="n">back</span><span class="p">();</span>
            <span class="n">number</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">first</span><span class="p">;</span>
            <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">p</span><span class="p">.</span><span class="n">second</span> <span class="o">&lt;&lt;</span> <span class="s">" "</span><span class="p">;</span>
            <span class="n">dq</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">number</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">number</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">dq</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">());</span>
                <span class="n">dq</span><span class="p">.</span><span class="n">pop_front</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">frontTurn</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&gt;</span> <span class="n">number</span><span class="p">;</span> <span class="n">i</span><span class="o">--</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">dq</span><span class="p">.</span><span class="n">push_front</span><span class="p">(</span><span class="n">dq</span><span class="p">.</span><span class="n">back</span><span class="p">());</span>
                <span class="n">dq</span><span class="p">.</span><span class="n">pop_back</span><span class="p">();</span>
            <span class="p">}</span>

            <span class="n">frontTurn</span> <span class="o">=</span> <span class="nb">false</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">dq</span><span class="p">.</span><span class="n">front</span><span class="p">().</span><span class="n">second</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>deque은 양방향 queue + 배열 접근이 필요할 때 쓰는 컨테이너라고 기억하면 편하다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="C++" /><category term="C++" /><category term="STL" /><category term="deque" /><category term="백준" /><category term="2346번" /><summary type="html"><![CDATA[C++ STL의 deque 은 양쪽 끝에서 삽입과 삭제가 모두 가능한 컨테이너 이다. 이름은 double-ended queue 의 약자이며, vector와 queue의 특징을 적절히 섞은 자료구조라고 볼 수 있다.]]></summary></entry><entry><title type="html">[컴퓨터 비전] Intensity Transformation - Histogram Equalization 구현하기</title><link href="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/histogram-equalization/" rel="alternate" type="text/html" title="[컴퓨터 비전] Intensity Transformation - Histogram Equalization 구현하기" /><published>2025-12-31T09:33:00+00:00</published><updated>2025-12-31T09:33:00+00:00</updated><id>https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/histogram-equalization</id><content type="html" xml:base="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/histogram-equalization/"><![CDATA[<p><img src="/assets/images/2025-12-31/2025-12-31-histogram-equalization.png" alt="Image" /></p>

<p>앞선 포스팅에서는 <strong>Gamma Transformation</strong> , <strong>Negative Transformation</strong> 과 같이 픽셀 단위로 밝기 값을 직접 변환하는 기법을 살펴보았다.</p>

<p>이번 포스팅에서는 이러한 단순 밝기 변환의 한계를 보완하는 <strong>Histogram Equalization</strong> 에 대해 다룬다.</p>

<h2 id="histogram-equalization이-필요한-이유">Histogram Equalization이 필요한 이유</h2>

<p>Gamma Transformation과 같은 기법은 모든 픽셀에 동일한 변환 함수를 적용한다.</p>

<p>이로 인해 특정 밝기 구간에 픽셀이 몰려 있는 영상, 전체적으로 어둡거나 밝은 영상에서는 대비가 충분히 살아나지 않거나, 노이즈까지 같이 증폭되는 문제가 발생할 수 있다.</p>

<p>Histogram Equalization은 <strong>영상의 밝기 분포 자체를 기준으로 변환 함수를 자동으로 생성</strong> 하여 전체 대비를 고르게 펼치는 것을 목표로 한다.</p>

<h2 id="histogram의-개념">Histogram의 개념</h2>

<p>히스토그램은 각 밝기 값이 영상에서 <strong>몇 번 등장했는지</strong> 를 나타낸다.</p>

<p>학습 단계이기 때문에 8비트 그레이스케일 영상과 0 ~ 255의 밝기 레벨을 기준으로 보고, 히스토그램은 다음과 같이 정의된다.</p>

<p><strong>Histogram[k] = 밝기 값 k를 가지는 픽셀의 개수</strong></p>

<p>이를 전체 픽셀 수로 나누면 <strong>정규화된 히스토그램</strong> 이 되며, 각 밝기 값이 등장할 확률을 의미한다.</p>

<h2 id="histogram-equalization의-핵심-아이디어">Histogram Equalization의 핵심 아이디어</h2>

<p>Histogram Equalization은 정규화된 히스토그램의 <strong>누적 분포 함수(CDF)</strong> 를 이용한다.</p>

<p>CDF는 다음과 같은 의미를 가진다.</p>

<p><strong>밝기 값 r 이하의 픽셀이 전체에서 차지하는 비율</strong></p>

<p>이를 이용해 변환 함수를 정의하면,</p>

<p><img src="/assets/images/2025-12-31/2025-12-31-histogram-equalization-formula.png" alt="Image" /></p>

<p>L은 밝기 레벨 개수를 의미하며, 8비트 영상에서는 256이다.</p>

<p>그리고 p_r은 정규화된 히스토그램이다.</p>

<p>결과적으로 출력 영상의 히스토그램은 가능한 한 <strong>균등한 분포</strong> 를 갖도록 변환된다.</p>

<hr />

<p>이번 예제는 두 단계로 구성된다.</p>

<h3 id="step-1-입력-영상-변환">Step 1: 입력 영상 변환</h3>

<ul>
  <li>원본 Lena 영상</li>
  <li>s = r / 2, 즉 전체적으로 어두운 영상 (lena1)</li>
  <li>s = 128 + r / 2, 즉 밝기 값이 중간 이상에 몰린 영상 (lena2)</li>
</ul>

<h3 id="step-2-histogram-equalization-적용">Step 2: Histogram Equalization 적용</h3>

<ul>
  <li>원본 Lena 영상</li>
  <li>lena1</li>
  <li>lena2</li>
</ul>

<p>각 영상에 Histogram Equalization을 적용하여 밝기 분포와 대비 변화를 비교한다.</p>

<p><img src="/assets/images/2025-12-31/2025-12-31-histogram-equalization.png" alt="Image" /></p>

<p>[소스 코드]</p>

<p>사용 언어: C++</p>

<p>환경: Microsoft Windows 10 Home, Visual Studio 2022, C:\opencv...</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;opencv2/opencv.hpp&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;vector&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">cv</span><span class="p">;</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="c1">// s = r/2</span>
<span class="n">Mat</span> <span class="nf">halfTransform</span><span class="p">(</span><span class="k">const</span> <span class="n">Mat</span><span class="o">&amp;</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">result</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">CV_8UC1</span><span class="p">);</span> <span class="c1">// CV_8UC1 = 0 ~ 255 값의 1채널 이미지(매크로/타입/채널)</span>

    <span class="c1">// 모든 픽셀을 2중 for 문으로 순회</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">uchar</span> <span class="n">r</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>

            <span class="c1">// 밝기 값을 기존 밝기 값의 절반(r/2)으로 줄여 어둡게 함</span>
            <span class="n">result</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">r</span> <span class="o">/</span> <span class="mi">2</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 변환한 이미지 반환</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// s = 128 + r/2</span>
<span class="n">Mat</span> <span class="n">halfPlus128Transform</span><span class="p">(</span><span class="k">const</span> <span class="n">Mat</span><span class="o">&amp;</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">result</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">CV_8UC1</span><span class="p">);</span> <span class="c1">// CV_8UC1 = 0 ~ 255 값의 1채널 이미지(매크로/타입/채널)</span>

    <span class="c1">// 모든 픽셀을 2중 for 문으로 순회</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">uchar</span> <span class="n">r</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
            <span class="kt">int</span> <span class="n">s</span> <span class="o">=</span> <span class="mi">128</span> <span class="o">+</span> <span class="n">r</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>

            <span class="c1">// 8비트 오버플로우 방어</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">s</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="c1">// 밝기 값을 주어진 조건대로 변환</span>
            <span class="n">result</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">s</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 변환한 이미지 반환</span>
    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="c1">// Histogram Equalization</span>
<span class="n">Mat</span> <span class="n">histogramEqualization</span><span class="p">(</span><span class="k">const</span> <span class="n">Mat</span><span class="o">&amp;</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// 1. Histogram for Image Processing</span>
    <span class="c1">// Histogram[k] = 밝기값 k가 이미지에서 등장한 횟수</span>
    <span class="c1">// Histogram[100] = 20이라면 밝기값이 100인 픽셀이 20개 있다는 뜻</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">Histogram</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">uchar</span> <span class="n">r</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
            <span class="n">Histogram</span><span class="p">[</span><span class="n">r</span><span class="p">]</span><span class="o">++</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="c1">// 2. 누적 히스토그램 구하기</span>
    <span class="c1">// Histogram[k] = 밝기값 k가 이미지에서 등장한 횟수로, 확률이 아님</span>
    <span class="c1">// 밝기값 0부터 i까지의 누적 픽셀 수 = 누적분포함수(Cumulative Distribution Function, CDF)를 구하는 과정</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">cdf</span><span class="p">(</span><span class="mi">256</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span>
    <span class="n">cdf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">Histogram</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>

    <span class="c1">// 히스토그램만 있으면 해당 밝기 값이 몇 번 나왔는지만 알 수 있음</span>
    <span class="c1">// 해당 픽셀보다 어두운 픽셀이 전체에서 몇 %인지 알아야 equalization을 할 수 있음</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cdf</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">cdf</span><span class="p">[</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">]</span> <span class="o">+</span> <span class="n">Histogram</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>

    <span class="c1">// 0 ~ r까지의 픽셀 개수를 더해두면, cdf[r] / 전체 픽셀 수 = 0 ~ 1 사이의 누적 확률이 됨</span>
    <span class="c1">// 전체 픽셀 수</span>
    <span class="kt">int</span> <span class="n">total</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span> <span class="o">*</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span>

    <span class="c1">// 3. equalization</span>
    <span class="c1">// s_k = (L - 1) * cdf[k] / total (total로 나눠주어야 확률이 됨)</span>
    <span class="c1">// s_k는 새 밝기 값이며 cdf[k]가 크면 픽셀이 많이 모인 구간이므로 더 넓게 분배하여 밝기 대비를 증가시킴</span>
    <span class="c1">// cdf[k]가 작으면 픽셀이 상대적으로 덜 모인 구간으로, 덜 펼치면 됨</span>

    <span class="c1">// L - 1 = 255(출력 밝기의 최댓값)로 두고, Look Up Table(lut)을 만든다.</span>
    <span class="c1">// lut는 밝기 k를 넣었을 때 바로 새 밝기 s_k가 나오는 변환표이다.</span>
    <span class="n">vector</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span> <span class="n">lut</span><span class="p">(</span><span class="mi">256</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="mi">256</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="c1">// 0 ~ 1 사이의 누적 확률을 다시 0 ~ 255의 밝기값으로 되돌림</span>
        <span class="kt">double</span> <span class="n">s</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span><span class="p">(</span><span class="n">cdf</span><span class="p">[</span><span class="n">k</span><span class="p">])</span> <span class="o">/</span> <span class="n">total</span> <span class="o">*</span> <span class="mf">255.0</span><span class="p">;</span>

        <span class="c1">// 8비트 언더/오버플로우 방어</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">s</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="k">if</span> <span class="p">(</span><span class="n">s</span> <span class="o">&gt;</span> <span class="mi">255</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">s</span> <span class="o">=</span> <span class="mi">255</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">lut</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">s</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">);</span> <span class="c1">// 반올림</span>
    <span class="p">}</span>

    <span class="c1">// 4. 픽셀 매핑</span>
    <span class="n">Mat</span> <span class="n">result</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">CV_8UC1</span><span class="p">);</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">uchar</span> <span class="n">r</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>
            <span class="n">result</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="n">lut</span><span class="p">[</span><span class="n">r</span><span class="p">];</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">src</span> <span class="o">=</span> <span class="n">imread</span><span class="p">(</span><span class="s">"Lena.png"</span><span class="p">,</span> <span class="n">IMREAD_GRAYSCALE</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"이미지를 불러올 수 없습니다."</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1">// Step #1 레나 영상 변환</span>
    <span class="n">Mat</span> <span class="n">lena1</span> <span class="o">=</span> <span class="n">halfTransform</span><span class="p">(</span><span class="n">src</span><span class="p">);</span>
    <span class="n">Mat</span> <span class="n">lena2</span> <span class="o">=</span> <span class="n">halfPlus128Transform</span><span class="p">(</span><span class="n">src</span><span class="p">);</span>

    <span class="c1">// Step #2 원본 레나, lena1, lena2에 histogram equalization 적용</span>
    <span class="n">Mat</span> <span class="n">src_hev</span> <span class="o">=</span> <span class="n">histogramEqualization</span><span class="p">(</span><span class="n">src</span><span class="p">);</span>
    <span class="n">Mat</span> <span class="n">lena1_hev</span> <span class="o">=</span> <span class="n">histogramEqualization</span><span class="p">(</span><span class="n">lena1</span><span class="p">);</span>
    <span class="n">Mat</span> <span class="n">lena2_hev</span> <span class="o">=</span> <span class="n">histogramEqualization</span><span class="p">(</span><span class="n">lena2</span><span class="p">);</span>

    <span class="n">imshow</span><span class="p">(</span><span class="s">"source"</span><span class="p">,</span> <span class="n">src</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"source_histogram_equalization_version"</span><span class="p">,</span> <span class="n">src_hev</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"lena1"</span><span class="p">,</span> <span class="n">lena1</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"lena1_histogram_equalization_version"</span><span class="p">,</span> <span class="n">lena1_hev</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"lena2"</span><span class="p">,</span> <span class="n">lena2</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"lena2_histogram_equalization_version"</span><span class="p">,</span> <span class="n">lena2_hev</span><span class="p">);</span>

    <span class="n">waitKey</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>코드에 주석을 상세히 달아놨으니 참고하면 좋을 것 같다.</p>

<p><br /></p>

<p>Histogram Equalization을 적용한 결과, 밝기 값이 특정 구간에 몰려 있던 영상에서도 전체 밝기 범위를 보다 고르게 사용하는 결과를 확인할 수 있었다.</p>

<p>특히 대비가 낮았던 lena1, lena2 영상에서도 경계와 디테일이 뚜렷하게 드러났다.</p>

<h3 id="마무리">마무리</h3>

<p>Histogram Equalization은 밝기 분포 자체를 기준으로 변환 함수를 설계하는 기법이다.</p>

<p>단순한 Intensity Transformation 기법들보다는 계산이 복잡하지만, 자동으로 대비를 개선할 수 있다는 장점이 있다.</p>

<p>다만 모든 영상에 항상 최적의 결과를 보장하지는 않으며, 국소적인 대비를 고려하는 기법이 추가로 사용되기도 한다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="컴퓨터 비전" /><category term="컴퓨터 비전" /><category term="opencv" /><category term="Visual Studio" /><category term="Intensity Transformation" /><category term="Histogram Equalization" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">[컴퓨터 비전] Intensity Transformation - Negative Transformation 구현하기</title><link href="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/negative-transformation/" rel="alternate" type="text/html" title="[컴퓨터 비전] Intensity Transformation - Negative Transformation 구현하기" /><published>2025-12-30T05:25:00+00:00</published><updated>2025-12-30T05:25:00+00:00</updated><id>https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/negative-transformation</id><content type="html" xml:base="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/negative-transformation/"><![CDATA[<p><img src="/assets/images/2025-12-30/2025-12-30-negative-transformation.png" alt="Image" /></p>

<p>영상 처리에서 <strong>밝기(Intensity)</strong> 를 직접 변환하는 기법을 <strong>Intensity Transformation</strong> 이라고 소개했는데, 이번 포스팅에서는 Intensity Transformation 중 가장 단순한 형태인 <strong>Negative Transformation(네거티브 변환)</strong> 에 대해 다뤄보고자 한다.</p>

<h2 id="negative-transformation">Negative Transformation</h2>

<p>Negative Transformation은 영상의 밝기 값을 반전시키는 변환이다.</p>

<p>8비트 그레이스케일 영상에서 픽셀 값의 범위는 <strong>0 ~ 255</strong> 이며, Negative Transformation은 다음과 같은 수식으로 정의된다.</p>

<p><strong>s = L - 1 - r</strong></p>

<ul>
  <li>r: 입력 픽셀의 밝기 값</li>
  <li>s: 출력 픽셀의 밝기 값</li>
  <li>L: 밝기 레벨의 개수 (8비트 영상에서는 256)</li>
</ul>

<p>즉, 밝은 영역은 어두워지고, 어두운 영역은 밝아지면서 영상의 <strong>흑백이 완전히 반전</strong> 된다.</p>

<p>이는 어두운 배경에 묻힌 밝은 구조를 강조할 때 유용하며, 공식이 단순하기 때문에 구현 또한 직관적이다.</p>

<hr />

<p>다음은 Lena.png 파일에 Negative Transformation(s = L - 1 - r)을 적용한 결과이다.</p>

<p><img src="/assets/images/2025-12-30/2025-12-30-negative-transformation.png" alt="Image" /></p>

<p>[소스 코드]</p>

<p>사용 언어: C++</p>

<p>환경: Microsoft Windows 10 Home, Visual Studio 2022, C:\opencv...</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;opencv2/opencv.hpp&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">cv</span><span class="p">;</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="n">Mat</span> <span class="nf">negativeTransform</span><span class="p">(</span><span class="k">const</span> <span class="n">Mat</span><span class="o">&amp;</span> <span class="n">src</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">result</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">CV_8UC1</span><span class="p">);</span> <span class="c1">// CV_8UC1 = 0 ~ 255 값의 1채널 이미지(매크로/타입/채널)</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">uchar</span> <span class="n">r</span> <span class="o">=</span> <span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">);</span>

            <span class="c1">// Negative Transformation s = L - 1 - r에서 흑백 반전을 최대로 적용하려면 8비트 최댓값인 256(L)을 사용</span>
            <span class="n">result</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="mi">255</span> <span class="o">-</span> <span class="n">r</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">src</span> <span class="o">=</span> <span class="n">imread</span><span class="p">(</span><span class="s">"Lena.png"</span><span class="p">,</span> <span class="n">IMREAD_GRAYSCALE</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"이미지를 불러올 수 없습니다."</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Mat</span> <span class="n">Lena_negative</span> <span class="o">=</span> <span class="n">negativeTransform</span><span class="p">(</span><span class="n">src</span><span class="p">);</span>

    <span class="n">imshow</span><span class="p">(</span><span class="s">"source"</span><span class="p">,</span> <span class="n">src</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"negative"</span><span class="p">,</span> <span class="n">Lena_negative</span><span class="p">);</span>

    <span class="n">waitKey</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="컴퓨터 비전" /><category term="컴퓨터 비전" /><category term="opencv" /><category term="Visual Studio" /><category term="Intensity Transformation" /><category term="Negative Transformation" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">[컴퓨터 비전] Intensity Transformation - Gamma Transformation 구현하기</title><link href="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/gamma-transformation/" rel="alternate" type="text/html" title="[컴퓨터 비전] Intensity Transformation - Gamma Transformation 구현하기" /><published>2025-12-29T12:50:00+00:00</published><updated>2025-12-29T12:50:00+00:00</updated><id>https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/gamma-transformation</id><content type="html" xml:base="https://dosp74.github.io/%EC%BB%B4%ED%93%A8%ED%84%B0%20%EB%B9%84%EC%A0%84/gamma-transformation/"><![CDATA[<p><img src="/assets/images/2025-12-29/2025-12-29-gamma-transformation.png" alt="Image" /></p>

<p>영상 처리에서 <strong>밝기(Intensity)</strong> 를 직접 변환하는 기법을 <strong>Intensity Transformation</strong> 이라고 한다.</p>

<p>이 기법은 공간적인 위치 관계를 고려하지 않고, <strong>각 픽셀의 밝기 값 자체를 함수로 변환</strong> 하는 방식이다.</p>

<p>이번 포스팅에서는 여러 Intensity Transformation 중 가장 자주 사용되는 <strong>Gamma Transformation(감마 변환)</strong> 에 대해 다루려고 한다.</p>

<h2 id="intensity-transformation">Intensity Transformation</h2>

<p>Intensity Transformation은 다음과 같은 형태로 표현할 수 있다.</p>

<p><strong>g(x, y) = T[f(x, y)]</strong></p>

<ul>
  <li>f(x, y): 입력 영상(input image)</li>
  <li>g(x, y): 출력 영상(output image)</li>
  <li>T: 밝기 변환 함수(intensity transformation function)</li>
</ul>

<p>즉, <strong>각 픽셀의 밝기 값 r을 변환 함수 T에 넣어 새로운 밝기 값 s를 만드는 과정</strong> 이다.</p>

<p>공간 좌표 <code class="language-plaintext highlighter-rouge">(x, y)</code> 주변 픽셀을 고려하지 않기 때문에 <strong>픽셀 단위</strong> 로 분류된다.</p>

<h2 id="power-lawgamma-transformation">Power-Law(Gamma) Transformation</h2>

<p>Gamma Transformation은 <strong>Power-Law Transformation</strong> 이라고도 불리며, 다음과 같은 수식으로 정의된다.</p>

<p><img src="/assets/images/2025-12-29/2025-12-29-gamma-transformation-formula.png" alt="Image" /></p>

<p><strong>s = cr^γ</strong></p>

<ul>
  <li>r: 입력 밝기 값 (0 ~ 1로 정규화)</li>
  <li>s: 출력 밝기 값</li>
  <li>γ: 감마 값</li>
  <li>c: 상수 (보통 1로 둠)</li>
</ul>

<p>예를 들어, 어두운 톤의 영상에 감마 값 0.2를 채택한다면 어떻게 될까?</p>

<p>어두운 곳의 변화를 증폭시켜 정상화할 수 있을 것이다.</p>

<p>영상 밝기 값이 8비트라면 0 ~ 255 범위지만, 수식을 다루기 편하게 하기 위해 정규화하여 계산한다. 어떤 픽셀 값이 128이라면 계산할 때는 128 / 255 = 0.5로 바꾸는 것이다. 변환 s = cr^γ는 이러한 정규화된 값 r(0 ~ 1)에 대해 적용한다. 연산이 끝난 뒤에는 다시 0 ~ 255 범위로 되돌려 저장하거나 화면에 표시한다.</p>

<p>따라서 γ &gt; 1이면 r^γ &lt; r이므로 밝은 쪽이 눌리면서 전체가 더 어두워지고, γ &lt; 1이면 r^γ &gt; r이므로 어두운 입력도 커져 전체가 밝아진다. 지수 함수의 개념을 떠올리면 이해가 쉬울 것이다.</p>

<h3 id="정리">정리</h3>

<p><strong>γ 값에 따른 특징</strong></p>

<p>γ &lt; 1이면 어두운 영역이 강조된다. 즉 전체적으로 밝아진다.</p>

<p>γ = 1이면 입력과 출력이 동일하다.</p>

<p>γ &gt; 1이면 밝은 영역이 억제된다. 즉 전체적으로 어두워진다.</p>

<p>이러한 특성 때문에 감마 변환은 디스플레이 보정이나 의료, 위성, 야간 영상 처리 등에서 매우 자주 사용된다.</p>

<p>하지만 감마 변환은 모든 픽셀의 밝기 값을 동일한 함수로 변환하기 때문에, 픽셀 값과 함께 <strong>노이즈 역시 같이 증폭되거나 감소하는 문제</strong> 가 있다.</p>

<p>특히 γ &lt; 1을 적용하여 어두운 영역을 강조할 경우, 어두운 영역에 존재하던 노이즈까지 함께 커지면서 영상의 품질이 오히려 저하될 수도 있다.</p>

<p>이러한 문제를 해결하기 위해서는 선명하게 하고자 하는 영역만 선택적으로 강조하고, 노이즈가 많이 포함된 영역은 변환의 영향을 최소화하는 보다 정교한 영상 처리 알고리즘이 필요하다.</p>

<hr />

<p>다음은 γ = 0.5, γ = 1.5를 각각 적용한 결과이다.</p>

<p><img src="/assets/images/2025-12-29/2025-12-29-gamma-transformation.png" alt="Image" /></p>

<p>[소스 코드]</p>

<p>사용 언어: C++</p>

<p>환경: Microsoft Windows 10 Home, Visual Studio 2022, C:\opencv...</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;opencv2/opencv.hpp&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;cmath&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">cv</span><span class="p">;</span>
<span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="n">Mat</span> <span class="nf">gammaTransform</span><span class="p">(</span><span class="k">const</span> <span class="n">Mat</span><span class="o">&amp;</span> <span class="n">src</span><span class="p">,</span> <span class="kt">double</span> <span class="n">gamma</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">result</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">size</span><span class="p">(),</span> <span class="n">CV_8UC1</span><span class="p">);</span> <span class="c1">// CV_8UC1 = 0 ~ 255 값의 1채널 이미지(매크로/타입/채널)</span>

    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">y</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">rows</span><span class="p">;</span> <span class="n">y</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">x</span> <span class="o">&lt;</span> <span class="n">src</span><span class="p">.</span><span class="n">cols</span><span class="p">;</span> <span class="n">x</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// 0 ~ 255 -&gt; 0 ~ 1로 정규화</span>
            <span class="kt">double</span> <span class="n">r</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="kt">double</span><span class="o">&gt;</span><span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">))</span> <span class="o">/</span> <span class="mf">255.0</span><span class="p">;</span>

            <span class="c1">// s = cr^gamma (c = 1)</span>
            <span class="kt">double</span> <span class="n">s</span> <span class="o">=</span> <span class="n">pow</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">gamma</span><span class="p">);</span>

            <span class="c1">// 다시 0 ~ 255 범위로 변환</span>
            <span class="n">result</span><span class="p">.</span><span class="n">at</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">x</span><span class="p">)</span> <span class="o">=</span> <span class="k">static_cast</span><span class="o">&lt;</span><span class="n">uchar</span><span class="o">&gt;</span><span class="p">(</span><span class="n">s</span> <span class="o">*</span> <span class="mf">255.0</span><span class="p">);</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">Mat</span> <span class="n">src</span> <span class="o">=</span> <span class="n">imread</span><span class="p">(</span><span class="s">"Lena.png"</span><span class="p">,</span> <span class="n">IMREAD_GRAYSCALE</span><span class="p">);</span>

    <span class="k">if</span> <span class="p">(</span><span class="n">src</span><span class="p">.</span><span class="n">empty</span><span class="p">())</span> <span class="p">{</span>
        <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">"이미지를 열 수 없습니다."</span> <span class="o">&lt;&lt;</span> <span class="n">endl</span><span class="p">;</span>

        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="n">Mat</span> <span class="n">gamma05</span> <span class="o">=</span> <span class="n">gammaTransform</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">);</span>
    <span class="n">Mat</span> <span class="n">gamma15</span> <span class="o">=</span> <span class="n">gammaTransform</span><span class="p">(</span><span class="n">src</span><span class="p">,</span> <span class="mf">1.5</span><span class="p">);</span>

    <span class="n">imshow</span><span class="p">(</span><span class="s">"source"</span><span class="p">,</span> <span class="n">src</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"Gamma: 0.5"</span><span class="p">,</span> <span class="n">gamma05</span><span class="p">);</span>
    <span class="n">imshow</span><span class="p">(</span><span class="s">"Gamma: 1.5"</span><span class="p">,</span> <span class="n">gamma15</span><span class="p">);</span>

    <span class="n">waitKey</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>8비트 그레이스케일 영상에서 픽셀 값의 범위는 0 ~ 255이다. 하지만 감마 변환 공식은 0 ~ 1 범위의 실수 값을 기준으로 정의되므로 다음과 같은 과정을 거친다.</p>

<ol>
  <li>입력 픽셀 값을 0 ~ 1로 정규화</li>
  <li>감마 변환 공식 적용</li>
  <li>다시 0 ~ 255 범위로 복원</li>
</ol>

<p>γ = 0.5일 때는 어두운 영역의 픽셀 값이 크게 증가하는 것을 볼 수 있다.
전체적으로 밝아졌으며 디테일이 잘 드러난다.</p>

<p>반면 γ = 1.5일 때는 밝은 영역이 억제되는 것을 볼 수 있다.
전체적으로 어두워졌으며 밝기가 감소되었다.</p>

<p>같은 영상이라도 γ 값에 따라 완전히 다른 인상을 주는 결과가 나온다는 점이 인상적이다.</p>

<h3 id="마무리">마무리</h3>

<p>Gamma Transformation은 픽셀 단위 밝기 변환 기법이다.</p>

<p>γ 값에 따라 밝기 분포를 비선형적으로 조절할 수 있다.</p>

<p>영상의 디테일을 강조하거나, 디스플레이를 보정하는 등에 쓰이는 기법이다.</p>

<p>이미지 출처</p>

<p>광운대학교 이윤구 교수님 강의자료<br />
Rafael C. Gonzalez &amp; Richard E. Woods, Digital Image Processing (2nd Edition, Prentice Hall)</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="컴퓨터 비전" /><category term="컴퓨터 비전" /><category term="opencv" /><category term="Visual Studio" /><category term="Intensity Transformation" /><category term="Gamma Transformation" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">[회고] 채팅 애플리케이션 개발 회고 - RDBMS에서 NoSQL(DynamoDB)로 전환하며 배운 것들</title><link href="https://dosp74.github.io/%ED%9A%8C%EA%B3%A0/from-rdbms-to-nosql/" rel="alternate" type="text/html" title="[회고] 채팅 애플리케이션 개발 회고 - RDBMS에서 NoSQL(DynamoDB)로 전환하며 배운 것들" /><published>2025-12-24T12:35:00+00:00</published><updated>2025-12-24T12:35:00+00:00</updated><id>https://dosp74.github.io/%ED%9A%8C%EA%B3%A0/from-rdbms-to-nosql</id><content type="html" xml:base="https://dosp74.github.io/%ED%9A%8C%EA%B3%A0/from-rdbms-to-nosql/"><![CDATA[<p><img src="/assets/images/2025-12-24/2025-12-24-architecture.png" alt="Image" /></p>

<p>3-2학기를 마치면서 광운대학교 AWS Cloud Club 소모임 활동의 일환으로 진행한 <strong>채팅 애플리케이션 개발 프로젝트</strong> 에 대한 회고록을 작성하고자 한다.</p>

<p>Spring Boot 기반 백엔드와 AWS 인프라를 활용해, 실제 서비스에 가까운 채팅 시스템을 설계하고 구현하는 것을 목표로 했다.</p>

<p><a href="https://github.com/orgs/acckwuchat-2/repositories">프로젝트 레포지토리</a></p>

<blockquote>
  <p>프론트엔드는 소모임장님이 미리 만들어두셨다.</p>
</blockquote>

<p>프로젝트를 진행하면서 단순히 <code class="language-plaintext highlighter-rouge">기능을 구현한다</code>를 넘어서, 도메인 특성에 맞는 데이터베이스 선택과 설계가 얼마나 중요한지를 깊이 고민하게 되었다.</p>

<p>프로젝트의 개발 흐름은 크게 세 가지로 나눌 수 있었는데, 인증과 사용자/채팅 데이터를 처리하는 백엔드 API 영역, 실시간 채팅을 담당하는 WebSocket 통신 영역, 그리고 AWS 인프라 및 배포 환경을 구성하는 아키텍처 영역이다.</p>

<p>이 중 나는 백엔드 API 영역을 담당하여, JWT 기반 인증/인가 로직과 DynamoDB 설계 및 연동을 포함한 auth, user, chat 도메인의 REST API를 구현했다.</p>

<h2 id="처음의-설계-rdbms-기반-erd">처음의 설계: RDBMS 기반 ERD</h2>

<p><img src="/assets/images/2025-12-24/2025-12-24-rdbms-erd.png" alt="Image" /></p>

<p>프로젝트 초반에는 자연스럽게 관계형 데이터베이스(RDBMS)를 기준으로 ERD를 설계했다.</p>

<ul>
  <li>User</li>
  <li>ChatRoom</li>
  <li>ChatMessage</li>
  <li>ChatRoomMember</li>
</ul>

<p>각 엔티티 간의 관계를 정규화하고, 외래 키와 JOIN을 통해 데이터를 조회하는 구조였다.</p>

<p>이 설계는 정합성과 구조적인 측면에서는 깔끔했지만, 채팅 도메인의 특성을 깊이 고려한 결과 몇 가지 의문이 생기기 시작했다.</p>

<h2 id="채팅-도메인의-특성과-문제-인식">채팅 도메인의 특성과 문제 인식</h2>

<p>채팅 애플리케이션은 일반적인 CRUD 중심 서비스와 성격이 다르다.</p>

<ul>
  <li>유저가 어느 정도 있다는 가정 하에, 메시지는 초당 매우 많이 생성된다.</li>
  <li>대부분의 메시지는 생성 이후 수정되지 않는 데이터이다.</li>
  <li>메시지 간 관계(JOIN)보다 쓰기 성능이 훨씬 중요하다.</li>
  <li>채팅방에는 매우 많은 사용자가 동시에 접속 가능하며, 접속하여 채팅할 수 있다.</li>
</ul>

<p>이러한 특성은 정규화 + JOIN 기반의 RDBMS 설계와는 맞지 않는 부분이 많았다.</p>

<p>강한 트랜잭션 일관성보다는 지연되지 않는 것과 쓰기 성능이 중요했다.</p>

<h2 id="rdbms---nosql-전환-결정">RDBMS -&gt; NoSQL 전환 결정</h2>

<p>결국 우리는 데이터베이스 선택을 <strong>데이터의 형태</strong> 가 아니라 <strong>도메인의 접근 패턴과 데이터의 특성</strong> 을 기준으로 다시 판단했다.</p>

<p>그 결과, 채팅 애플리케이션의 핵심 특징 및 요구사항은 다음과 같았다.</p>

<ul>
  <li>쓰기 성능이 좋아야 한다.</li>
  <li>수평으로 확장하기 좋아야 한다.</li>
  <li>조회 패턴이 비교적 단순하다. (최신순 정렬 등)</li>
  <li>JOIN이 중요하지 않은 데이터 접근이 많다.</li>
</ul>

<p>이는 분산 Key-Value 저장소가 잘하는 영역이었고, 이에 따라 NoSQL 기반 DynamoDB로의 전환을 결정하게 되었다.</p>

<h2 id="dynamodb-기반-데이터-모델링">DynamoDB 기반 데이터 모델링</h2>

<p><img src="/assets/images/2025-12-24/2025-12-24-nosql-data-modeling.png" alt="Image" /></p>

<p>NoSQL로 전환하면서 가장 크게 달라진 점은 ERD를 <strong>관계</strong> 가 아닌 <strong>조회 패턴</strong> 중심으로 다시 설계했다는 것이다.</p>

<h3 id="주요-설계-포인트">주요 설계 포인트</h3>

<p><img src="/assets/images/2025-12-24/2025-12-24-chatmessages.png" alt="Image" /></p>

<p><strong>ChatMessages</strong></p>

<p>Partition Key로 chat_room_id를, Sort Key로 created_at을 두어 채팅방별 시간순 메시지 조회에 최적화하였다.</p>

<p><img src="/assets/images/2025-12-24/2025-12-24-chatroommembers.png" alt="Image" /></p>

<p><strong>ChatRoomMembers</strong></p>

<p>기본 Partition Key로 chat_room_id를, Sort Key로 user_id를 두었고, GSI(user-index)로 Partition Key: user_id, Sort Key: chat_room_id를 두어 <strong>사용자가 참여 중인 채팅방 목록 조회</strong> 를 지원하도록 한다.</p>

<p>여기서 GSI는 Global Secondary Index로, 테이블의 기본 키와는 다른 Partition Key/Sort Key 조합으로 데이터를 조회할 수 있도록 만든 보조 인덱스이다. 이를 통해 여러 조회 패턴을 하나의 테이블에서 지원할 수 있다.
내부적으로는 별도의 테이블처럼 데이터가 복제되어 저장되며, Global이라는 단어가 붙은 이유는 어떤 파티션에 있든 전역적으로 조회가 가능하기 때문이다.</p>

<p>또한 DynamoDB의 특성을 고려해 Partition Key/Sort Key를 string 타입으로 통일했다.
이는 AWS 공식 문서에서 권장하는 방식이며, 문자열 기반 정렬이 시간 순 조회와 해시 분산 측면에서 유리하기 때문이라고 한다.</p>

<h2 id="join이-없는-설계에-대한-이해">JOIN이 없는 설계에 대한 이해</h2>

<p>DynamoDB에서는 SQL처럼 JOIN 연산을 수행하지 않는다. 대신 조회 시점에 JOIN을 하는 것이 아니라, 설계 시점에 데이터를 비정규화하여 함께 조회되도록 저장한다.</p>

<p>즉, JOIN이 필요 없도록 저장한다.</p>

<p>이 관점의 전환은 NoSQL을 이해하는 데 있어 가장 중요한 학습 포인트였다.</p>

<blockquote>
  <p>최종 발표에서 받은 질문도 NoSQL의 JOIN과 관련된 질문이었는데, 잘 대답하지 못해서 아쉬웠다.</p>
</blockquote>

<h3 id="다시-정리하면">다시 정리하면</h3>

<p>DynamoDB는 JOIN을 런타임에 수행하지 않기 때문에 JOIN이 필요한 데이터는 미리 하나의 item이나 동일한 Partition Key로 묶어서 저장하는 방식으로 설계한다. 즉, 미리 함께 조회될 형태에 맞게 데이터를 비정규화하여 저장한다.</p>

<p>구체적으로는 Single Table Design을 통해 관련 데이터를 같은 Partition Key로 저장하여 한 번의 쿼리로 함께 조회되도록 설계한다. 이를 통해 JOIN 없이도 유사한 효과를 낸다.</p>

<h2 id="느낀-점">느낀 점</h2>

<p>이번 프로젝트를 통해 얻은 가장 큰 교훈은 <strong>데이터베이스의 선택은 기술 스택의 문제가 아니라 도메인 이해의 문제라는 점</strong> 이다.</p>

<p>RDBMS와 NoSQL 중 무엇이 더 좋은가가 아니라, 우리 서비스의 접근 패턴과 데이터 특성에 무엇이 더 적합한가를 설명할 수 있어야 한다는 점을 배웠다.</p>

<p>단순히 DynamoDB를 써봤다에서 끝나는 것이 아니라, 왜 이 선택이 합리적인지 설명할 수 있게 되었다는 점에서 의미 있는 프로젝트였다고 생각한다.</p>

<p>공부를 하면 할수록 느끼는 거지만, 정말 배울 게 많다. 그리고 머리로 이해하고 있는 것과 말로 설명할 수 있는 것은 분명히 다르다…</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="회고" /><category term="데이터베이스" /><category term="RDBMS" /><category term="NoSQL" /><category term="AWS" /><category term="DynamoDB" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">[알고리즘] 백트래킹(Backtracking)</title><link href="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/backtracking/" rel="alternate" type="text/html" title="[알고리즘] 백트래킹(Backtracking)" /><published>2025-12-15T16:08:00+00:00</published><updated>2025-12-15T16:08:00+00:00</updated><id>https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/backtracking</id><content type="html" xml:base="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/backtracking/"><![CDATA[<p>백트래킹(Backtracking)은 <strong>조건을 만족하는 해를 찾기 위해 모든 경우를 탐색</strong> 하되, 불필요한 경우는 조기에 <strong>가지치기(pruning)</strong> 하며 탐색을 중단하는 <strong>탐색 알고리즘</strong> 이다.</p>

<p>즉, 선택을 번복할 수 있다는 점에서 그리디 알고리즘과 차이가 있다.</p>

<blockquote>
  <p>그리디 알고리즘은 <strong>항상 최적 선택</strong> 을 기준으로 해를 구성하지만, 백트래킹은 <strong>조건을 만족하는 해가 있는 방향으로만</strong> 탐색한다.</p>
</blockquote>

<p>백트래킹은 상태 공간(State Space)을 트리로 나타낼 수 있을 때 적합한 방식이며, 일종의 트리 탐색 알고리즘이라고 봐도 된다.</p>

<p>기본적으로 DFS(Depth-First-Search) 알고리즘을 기반으로 많이 사용되며 <strong>더 볼 필요 없는 분기</strong> 를 판단하여 <strong>탐색 범위를 줄이는 조건문을 적절히 삽입함으로써</strong> 효율성을 높이는 방식이다.</p>

<p><br /></p>

<h2 id="대표적인-예시-n-queens-problem">대표적인 예시: N-Queens Problem</h2>

<p>N x N 체스판 위에 N개의 퀸을 서로 공격하지 않게 놓는 문제이다.</p>

<p><img src="/assets/images/2025-12-16/2025-12-16-baekjoon-9663.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/9663">https://www.acmicpc.net/problem/9663</a></p>

<p>보통 N = 8이 적당하며, 8을 넘어가면 시간이 오래 걸린다.</p>

<p>N개의 퀸의 위치를 순차적으로 결정하되, 배치될 수 있는 후보지 즉 N^2가지의 자리에서 서로 위협하지 않게 결정한다.</p>

<p>2차원 배열에서 퀸을 두는 모습을 상상해보자.</p>

<p>일단 N = 4인 경우를 생각해보자. 4 x 4 체스판에 퀸을 둘 수 있는 모든 경우의 수는 16C4로 1820가지나 된다.</p>

<p>하지만, 정말로 모든 경우의 수를 봐야 하는지 생각해보면 그렇지 않다. 퀸은 같은 행에 2개 이상 둘 수 없으므로 1행에 1개만 둘 수 있다. 즉 4C1 * 4C1 * 4C1 * 4C1 = 256가지 경우에서 생각해볼 수 있다.</p>

<p>퀸의 위치를 {i, j}라고 두면, {1, 1}, {1, 2}, {1, 3}, {1, 4}에 퀸을 둔 후 트리를 백트래킹으로 탐색할 수 있다.</p>

<p>여기서 더 진행할지 말지를 결정해야 하는데, 현재 상태가 모든 퀸이 서로 위협하지 않는 상태이면 더 진행한다. 만약 위협하도록 배치되었으면 진행하지 않는다.</p>

<p>즉, nonpromising node를 만나면 되돌아간다.</p>

<p>dfs로 state space tree를 탐색하되 promising한지 검사하며 탐색한다. 만약 nonpromising하다면 되돌아간다.</p>

<p><br /></p>

<p>이제 promising 조건을 정해보자. promising이란, 현재까지의 선택이 조건을 만족하고, 향후 해가 존재할 가능성이 있을 때를 의미한다.</p>

<p>여기서는 <strong>새로 추가된 퀸이 다른 퀸을 위협하는지</strong> 판단하면 된다.</p>

<p>1 ~ k까지 배치되었고, k + 1번째 퀸을 두는 상황을 가정한다.
퀸이 놓인 열 위치를 col 배열로 관리한다. 즉 col[i]는 i번째 행의 퀸이 놓인 열의 위치이다.</p>

<p>우리가 조기에 같은 행에는 퀸을 2개 이상 두지 않는다고 가정했으므로, 새로 놓는 퀸의 열에 다른 퀸이 배치되어 있지 않는지 검사한다. 즉 <strong>col[i] == col[k]</strong> 인지 검사한다.</p>

<p>이제 남은 것은 퀸이 대각선으로 위협할 수 있는 자리에 배치된 경우인데, 대각선에 있는지 체크하는 방법은 서로에 대한 가로, 세로의 길이가 일치한지를 보면 된다.</p>

<p>즉 <strong>행의 차이</strong> 와 <strong>열의 차이</strong> 가 같은지 체크한다. 만약 같다면 두 퀸은 대각선에 놓인 것이다.</p>

<p>여기까지가 N-Queens Problem을 해결하는 일반적인 사고의 흐름이다. 이제 이 내용을 정리해보자.</p>

<p><br /></p>

<ul>
  <li>퀸은 <strong>같은 행, 같은 열, 같은 대각선</strong> 에 있으면 안 된다.</li>
  <li>모든 경우를 보는 완전 탐색으로 풀 수 있으나, 경우의 수가 기하급수적으로 증가한다.</li>
</ul>

<p>머릿속으로 상태 공간 트리(State Space Tree)를 상상한다.</p>

<ul>
  <li>탐색 공간을 트리로 구성해 <strong>DFS 방식</strong> 으로 탐색한다.</li>
  <li>각 노드는 <strong>현재까지 퀸을 배치한 상태</strong> 이다.</li>
  <li>한 단계마다 퀸을 한 행에 배치해 나가며, 가능한 열만 탐색한다.</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>start
├── 1행의 각 열 시도
│ ├── 2행의 가능한 열 시도
│ │ └── ...
</code></pre></div></div>

<p>새로 놓은 퀸이 이전 퀸들과 <strong>같은 열</strong> 또는 <strong>같은 대각선</strong> 에 있는지 검사한다.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>col[i] == col[k]           // 같은 열 체크
|col[i] - col[k]| == i - k // 같은 대각선 체크
</code></pre></div></div>

<p><br /></p>

<p>[C++]</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="n">n</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">col</span><span class="p">[</span><span class="mi">15</span><span class="p">];</span> <span class="c1">// col[i]는 i번째 행의 퀸이 놓인 열 위치</span>
<span class="kt">int</span> <span class="n">cnt</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

<span class="kt">bool</span> <span class="nf">promising</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">i</span><span class="p">;</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">col</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="n">col</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">||</span> <span class="n">abs</span><span class="p">(</span><span class="n">col</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-</span> <span class="n">col</span><span class="p">[</span><span class="n">k</span><span class="p">])</span> <span class="o">==</span> <span class="n">i</span> <span class="o">-</span> <span class="n">k</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
<span class="p">}</span>

<span class="kt">void</span> <span class="n">queens</span><span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">promising</span><span class="p">(</span><span class="n">i</span><span class="p">))</span> <span class="p">{</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">i</span> <span class="o">==</span> <span class="n">n</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">cnt</span><span class="o">++</span><span class="p">;</span>
        <span class="p">}</span>
        <span class="k">else</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;=</span> <span class="n">n</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">col</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">j</span><span class="p">;</span>
                <span class="n">queens</span><span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="kt">int</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">n</span><span class="p">;</span>

    <span class="n">queens</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">cnt</span><span class="p">;</span>

    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="정리">정리</h2>

<p>백트래킹(Backtracking)은 모든 경우를 탐색하되, 조건을 만족하지 않는 경우는 탐색을 중단한다.</p>

<p>N-Queens Problem은 대표적인 백트래킹 예제이다.</p>

<p>promising 조건을 정교하게 설정할수록 가지치기를 효과적으로 할 수 있다. 즉, 완전 탐색보다 훨씬 적은 연산으로 해를 찾을 수 있다.</p>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="알고리즘" /><category term="백트래킹" /><category term="백준" /><category term="9663번" /><summary type="html"><![CDATA[백트래킹(Backtracking)은 조건을 만족하는 해를 찾기 위해 모든 경우를 탐색 하되, 불필요한 경우는 조기에 가지치기(pruning) 하며 탐색을 중단하는 탐색 알고리즘 이다.]]></summary></entry><entry><title type="html">[알고리즘] 브루트포스 알고리즘(Brute Force Algorithm)</title><link href="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/brute-force-algorithm/" rel="alternate" type="text/html" title="[알고리즘] 브루트포스 알고리즘(Brute Force Algorithm)" /><published>2025-11-18T07:55:00+00:00</published><updated>2025-11-18T07:55:00+00:00</updated><id>https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/brute-force-algorithm</id><content type="html" xml:base="https://dosp74.github.io/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98/brute-force-algorithm/"><![CDATA[<p>브루트포스 알고리즘(Brute Force Algorithm)은 가능한 모든 경우의 수를 전부 탐색하여 답을 찾는 방식이다.</p>

<p>brute(신체적인 힘[폭력]에만 의존하는) + force(힘)의 이름 그대로 <strong>무식하게 밀어붙인다</strong>는 의미이다.</p>

<p>4자리의 비밀번호가 있을 때 0000부터 9999까지 모든 경우를 시도해본다거나, 배열에서 특정 합을 만드는 두 수를 찾는 문제 등에서 사용하는 예를 들 수 있다.</p>

<p>구현이 매우 간단하고 모든 경우를 검사하므로 정확성을 보장한다.</p>

<p>하지만, 입력 크기가 커지면 계산량이 폭발적으로 증가하기 때문에 효율성은 낮다.</p>

<h2 id="실전">실전</h2>

<p><img src="/assets/images/2025-11-18/2025-11-18-baekjoon-2798.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/2798">https://www.acmicpc.net/problem/2798</a></p>

<p>모든 경우의 수를 보며 M을 넘지 않으면서 M에 최대한 가까운 카드 3장의 합을 구하면 된다.</p>

<p>3장의 카드로 이루어지는 조합을 찾아야 하므로 3중 for 문으로 구현할 생각을 했다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    
    <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="n">M</span><span class="p">;</span>
    <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">N</span> <span class="o">&gt;&gt;</span> <span class="n">M</span><span class="p">;</span>
    
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">card</span><span class="p">(</span><span class="n">N</span><span class="p">);</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">N</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">card</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
    
    <span class="kt">int</span> <span class="n">optimalSum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">card</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">card</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="n">j</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">card</span><span class="p">.</span><span class="n">size</span><span class="p">();</span> <span class="n">k</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">if</span> <span class="p">(</span><span class="n">card</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">k</span><span class="p">]</span> <span class="o">&lt;=</span> <span class="n">M</span> <span class="o">&amp;&amp;</span> <span class="n">optimalSum</span> <span class="o">&lt;</span> <span class="n">card</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">k</span><span class="p">])</span> <span class="p">{</span>
                    <span class="n">optimalSum</span> <span class="o">=</span> <span class="n">card</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+</span> <span class="n">card</span><span class="p">[</span><span class="n">k</span><span class="p">];</span>
                <span class="p">}</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">optimalSum</span><span class="p">;</span>
    
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>다른 문제를 하나 더 풀어보자.</p>

<p><img src="/assets/images/2025-11-18/2025-11-18-baekjoon-2309.png" alt="Image" /></p>

<p><a href="https://www.acmicpc.net/problem/2309">https://www.acmicpc.net/problem/2309</a></p>

<p>가능한 정답이 여러 가지인 경우에는 아무거나 출력해도 되기 때문에, 일곱 난쟁이의 키의 합이 100이므로 모든 난쟁이의 키의 합에서 100을 뺀 후 해당 값을 만족시키는 조합을 찾았다.</p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;bits/stdc++.h&gt;</span><span class="cp">
</span><span class="k">using</span> <span class="k">namespace</span> <span class="n">std</span><span class="p">;</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">ios</span><span class="o">::</span><span class="n">sync_with_stdio</span><span class="p">(</span><span class="nb">false</span><span class="p">);</span>
    <span class="n">cin</span><span class="p">.</span><span class="n">tie</span><span class="p">(</span><span class="nb">nullptr</span><span class="p">);</span>
    
    <span class="n">vector</span><span class="o">&lt;</span><span class="kt">int</span><span class="o">&gt;</span> <span class="n">input</span><span class="p">(</span><span class="mi">9</span><span class="p">);</span>
    <span class="kt">int</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">9</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">cin</span> <span class="o">&gt;&gt;</span> <span class="n">input</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
        <span class="n">sum</span> <span class="o">+=</span> <span class="n">input</span><span class="p">[</span><span class="n">i</span><span class="p">];</span>
    <span class="p">}</span>
    
    <span class="kt">int</span> <span class="n">tolerance</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">-</span> <span class="mi">100</span><span class="p">;</span>
    
    <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">9</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="mi">9</span><span class="p">;</span> <span class="n">j</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="k">if</span> <span class="p">(</span><span class="n">input</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+</span> <span class="n">input</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">==</span> <span class="n">tolerance</span><span class="p">)</span> <span class="p">{</span>
                <span class="n">input</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">begin</span><span class="p">()</span> <span class="o">+</span> <span class="n">i</span><span class="p">);</span>
                <span class="n">input</span><span class="p">.</span><span class="n">erase</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">begin</span><span class="p">()</span> <span class="o">+</span> <span class="n">j</span> <span class="o">-</span> <span class="mi">1</span><span class="p">);</span>
                
                <span class="n">sort</span><span class="p">(</span><span class="n">input</span><span class="p">.</span><span class="n">begin</span><span class="p">(),</span> <span class="n">input</span><span class="p">.</span><span class="n">end</span><span class="p">());</span>
                
                <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">:</span> <span class="n">input</span><span class="p">)</span> <span class="p">{</span>
                    <span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="n">i</span> <span class="o">&lt;&lt;</span> <span class="s">"</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
                <span class="p">}</span>
                
                <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">}</span>
    <span class="p">}</span>
    
    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>]]></content><author><name>dosp74</name><email>pantene74@kw.ac.kr</email></author><category term="알고리즘" /><category term="브루트포스 알고리즘" /><category term="백준" /><category term="2798번" /><category term="2309번" /><summary type="html"><![CDATA[브루트포스 알고리즘(Brute Force Algorithm)은 가능한 모든 경우의 수를 전부 탐색하여 답을 찾는 방식이다.]]></summary></entry></feed>