<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>PS on Jiho Kim</title><link>https://blog.wlgh7407.com/tags/ps/</link><description>Recent content in PS on Jiho Kim</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Thu, 02 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.wlgh7407.com/tags/ps/index.xml" rel="self" type="application/rss+xml"/><item><title>BOJ 25456 궁금한 시프트</title><link>https://blog.wlgh7407.com/posts/algorithm/ps/260402_algorithm_boj-25456-%EA%B6%81%EA%B8%88%ED%95%9C-%EC%8B%9C%ED%94%84%ED%8A%B8/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/ps/260402_algorithm_boj-25456-%EA%B6%81%EA%B8%88%ED%95%9C-%EC%8B%9C%ED%94%84%ED%8A%B8/</guid><description>&lt;h2 id="-문제-정보"&gt;&lt;a href="#-%eb%ac%b8%ec%a0%9c-%ec%a0%95%eb%b3%b4" class="header-anchor"&gt;&lt;/a&gt;📝 문제 정보
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;링크: &lt;a class="link" href="https://www.acmicpc.net/problem/25456" target="_blank" rel="noopener"
 &gt;https://www.acmicpc.net/problem/25456&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-관찰-및-접근"&gt;&lt;a href="#-%ea%b4%80%ec%b0%b0-%eb%b0%8f-%ec%a0%91%ea%b7%bc" class="header-anchor"&gt;&lt;/a&gt;🧐 관찰 및 접근
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;0과 1로 이루어진 두 문자열 $S, T$가 주어진다.&lt;/li&gt;
&lt;li&gt;두 문자열에 시프트 연산이 가능하다고 할때, 합성곱의 최댓값을 구하자.&lt;/li&gt;
&lt;li&gt;합성곱이므로 FFT를 의심해볼 수 있겠다.&lt;/li&gt;
&lt;li&gt;0과 1로 이루어진 문자열을 다항식으로 해석하면 된다.&lt;/li&gt;
&lt;li&gt;곱 결과가 같은 차수에 올 수 있도록 한 다항식의 계수들을 뒤집고, 시프트 연산을 위해 둘중 한 배열을 두배로 늘려서 계산하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="-풀이"&gt;&lt;a href="#-%ed%92%80%ec%9d%b4" class="header-anchor"&gt;&lt;/a&gt;💻 풀이
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;코드 (C++):&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="paywall-wrapper"&gt;
 &lt;div class="paywall-content paywall-locked"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;solve&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; string S, T;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cin &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; S &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; T;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; S.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; sz &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt;(sz &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;N) sz &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; A(sz), B(sz);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(S[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) A[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(S[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) A[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(T[i] &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;) B[N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(B);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, sz) A[i] &lt;span style="color:#f92672"&gt;*=&lt;/span&gt; B[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; FFT&lt;span style="color:#f92672"&gt;::&lt;/span&gt;fft(A, true);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) ans &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(ans, (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;)round(A[i].real()));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// rep(i, 0, 3*N) cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; &amp;#39; &amp;#39; &amp;lt;&amp;lt; A[i] &amp;lt;&amp;lt; &amp;#39;\n&amp;#39;;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cout &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; ans &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt; &lt;/div&gt;
 &lt;div class="paywall-overlay"&gt;
 &lt;div class="paywall-icon"&gt;🔒&lt;/div&gt;
 &lt;p class="paywall-title"&gt;구현 코드 잠금&lt;/p&gt;
 &lt;p class="paywall-desc"&gt;아래 쿠팡 링크를 방문하시면 코드가 공개됩니다.&lt;br&gt;광고 수익이 블로그 운영에 도움이 됩니다 🙏&lt;/p&gt;
 &lt;a
 href="https://link.coupang.com/a/dOJ25W"
 target="_blank"
 rel="sponsored noopener"
 class="paywall-btn"
 onclick="paywallUnlock()"
 &gt;
 🛒 쿠팡 방문하고 코드 보기
 &lt;/a&gt;
 &lt;p class="paywall-hint"&gt;방문 후 잠금이 자동으로 해제됩니다&lt;/p&gt;
 &lt;/div&gt;
&lt;/div&gt;</description></item><item><title>FFT - 고속 푸리에 변환</title><link>https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/</link><pubDate>Thu, 02 Apr 2026 00:00:00 +0000</pubDate><guid>https://blog.wlgh7407.com/posts/algorithm/topics/260402_til_fft---%EA%B3%A0%EC%86%8D-%ED%91%B8%EB%A6%AC%EC%97%90-%EB%B3%80%ED%99%98/</guid><description>&lt;h2 id="-상세-정리"&gt;&lt;a href="#-%ec%83%81%ec%84%b8-%ec%a0%95%eb%a6%ac" class="header-anchor"&gt;&lt;/a&gt;📝 상세 정리
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;이제 슬슬 FFT를 이해할 때가 된것 같다. 정리해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="푸리에-변환"&gt;&lt;a href="#%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98" class="header-anchor"&gt;&lt;/a&gt;푸리에 변환
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;여러 사인파 / 코사인파가 더해진 함수 $h(t)$가 있다.
&lt;ul&gt;
&lt;li&gt;일단 먼저 사인파만 있다고 가정해보자.&lt;/li&gt;
&lt;li&gt;우리는 이 함수 $h(t)$에서 어떤 주파수를 가진 성분들이 있는지 검사하고싶다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더 쉽게, 먼저 사인파 하나에 대해서 생각해보자.
&lt;ul&gt;
&lt;li&gt;함수 $f(x) = \sin{ax}$가 있다.
&lt;ul&gt;
&lt;li&gt;우리는 $a$를 모른다고 가정하자.&lt;/li&gt;
&lt;li&gt;어떻게 하면 이 $a$를 알아낼 수 있을까?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;임의의 사인파 $g(x) = \sin{bx}$를 곱해보자.
&lt;ul&gt;
&lt;li&gt;삼각함수의 곱셈정리에 의해&lt;/li&gt;
&lt;li&gt;$f(x)g(x) = \sin{ax}\sin{bx} = \frac{\cos{((a-b)x)} - \cos{((a+b)x)}}{2}$
&lt;ul&gt;
&lt;li&gt;위와 같이 나타낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이를 음의 무한대부터 양의 무한대까지 적분하면, 주기함수의 특성을 이용해서 $a, b$가 같은지 다른지 알아낼 수 있다.
&lt;ul&gt;
&lt;li&gt;$a, b$는 주파수기에 $a, b &gt; 0$이 보장된다.&lt;/li&gt;
&lt;li&gt;$a = b$일 때만 양의 무한대로 발산하고, 그 외의 경우 진동발산한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위의 특징은 여러 사인파의 합 $h(t)$에 대해서도 간단하게 성립한다.&lt;/li&gt;
&lt;li&gt;코사인 파가 섞여있다면 코사인파를 곱해서 같은 방법으로 수행해주면 되겠다.&lt;/li&gt;
&lt;li&gt;이 때, 오일러 공식 $e^{ix} = \cos{x} + i\sin{x}$을 이용해서 실수부 / 허수부로 나눠서 더 쉽게 계산할 수도 있다.&lt;/li&gt;
&lt;li&gt;이를 식으로 나타내면 다음과 같다.
&lt;ul&gt;
&lt;li&gt;
$$\hat{h}(x) = \int_{-\infty}^{\infty}{h(t)e^{-2\pi itx}dt}$$&lt;/li&gt;
&lt;li&gt;이는 신호 -&amp;gt; 주파수로의 변환으로 해석하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;반대로 주파수 -&amp;gt; 신호의 변환도 가능하다.
&lt;ul&gt;
&lt;li&gt;
$$h(t) = \int_{-\infty}^{\infty}{\hat{h}(x)e^{2\pi itx}dx}$$&lt;/li&gt;
&lt;li&gt;이를 푸리에 역변환이라고 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여기서 갖고 놀아보자. 이해할때 클로드가 만들어줬다.
&lt;div class="fv-wrap"&gt;
 &lt;style&gt;
 .fv-wrap {
 background: transparent;
 color: #e0e0e0;
 font-family: "Segoe UI", sans-serif;
 margin: 1.5rem 0;
 }
 .fv-wrap .fv-controls {
 display: flex;
 gap: 12px;
 flex-wrap: wrap;
 align-items: center;
 margin-bottom: 12px;
 padding: 10px 12px;
 background: #1a1d27;
 border-radius: 8px;
 }
 .fv-wrap .fv-controls label {
 font-size: 0.78rem;
 color: #aaa;
 }
 .fv-wrap .fv-controls select {
 background: #252836;
 color: #e0e0e0;
 border: 1px solid #333;
 border-radius: 4px;
 padding: 4px 8px;
 font-size: 0.8rem;
 }
 .fv-wrap .fv-section {
 margin-bottom: 16px;
 background: #1a1d27;
 border-radius: 8px;
 overflow: hidden;
 }
 .fv-wrap .fv-section-title {
 color: #7eb8f7;
 font-size: 0.85rem;
 font-weight: 600;
 padding: 8px 12px 2px;
 margin: 0;
 border: none;
 }
 .fv-wrap .fv-section-desc {
 color: #666;
 font-size: 0.75rem;
 padding: 0 12px 6px;
 margin: 0;
 }
 .fv-wrap canvas {
 width: 100%;
 height: 160px;
 display: block;
 }
 &lt;/style&gt;

 &lt;div class="fv-controls"&gt;
 &lt;label&gt;신호 종류: &lt;/label&gt;
 &lt;select id="fv-signalType"&gt;
 &lt;option value="finite_sine"&gt;유한 사인파 (현실적)&lt;/option&gt;
 &lt;option value="two_freq"&gt;두 주파수 합성&lt;/option&gt;
 &lt;option value="square"&gt;구형파&lt;/option&gt;
 &lt;option value="infinite_sine"&gt;무한 사인파 (이상적)&lt;/option&gt;
 &lt;/select&gt;
 &lt;/div&gt;

 &lt;div class="fv-section"&gt;
 &lt;p class="fv-section-title"&gt;① 시간 도메인 — h(t)&lt;/p&gt;
 &lt;p class="fv-section-desc"&gt;실제 데이터 (샘플링된 이산 값들)&lt;/p&gt;
 &lt;canvas id="fv-timeCanvas"&gt;&lt;/canvas&gt;
 &lt;/div&gt;

 &lt;div class="fv-section"&gt;
 &lt;p class="fv-section-title"&gt;② 주파수 도메인 — |X[k]| (DFT 결과)&lt;/p&gt;
 &lt;p class="fv-section-desc"&gt;
 각 주파수 성분의 크기. 스파이크가 있는 곳 = 해당 주파수 존재
 &lt;/p&gt;
 &lt;canvas id="fv-freqCanvas"&gt;&lt;/canvas&gt;
 &lt;/div&gt;

 &lt;script&gt;
 (function () {
 const N = 256;
 const wrap = document.currentScript.closest(".fv-wrap");
 const select = wrap.querySelector("#fv-signalType");

 function getSignal(type) {
 const x = new Array(N);
 for (let n = 0; n &lt; N; n++) {
 const t = n / N;
 if (type === "finite_sine") {
 const win = Math.sin(Math.PI * t);
 x[n] = win * Math.sin(2 * Math.PI * 5 * t);
 } else if (type === "two_freq") {
 const win = Math.sin(Math.PI * t);
 x[n] =
 win *
 (Math.sin(2 * Math.PI * 5 * t) +
 0.6 * Math.sin(2 * Math.PI * 13 * t));
 } else if (type === "square") {
 x[n] = Math.sin(2 * Math.PI * 5 * t) &gt; 0 ? 1 : -1;
 } else if (type === "infinite_sine") {
 x[n] = Math.sin(2 * Math.PI * 5 * t);
 }
 }
 return x;
 }

 function dft(x) {
 const re = new Array(N).fill(0);
 const im = new Array(N).fill(0);
 for (let k = 0; k &lt; N; k++) {
 for (let n = 0; n &lt; N; n++) {
 const angle = (-2 * Math.PI * k * n) / N;
 re[k] += x[n] * Math.cos(angle);
 im[k] += x[n] * Math.sin(angle);
 }
 }
 return { re, im };
 }

 function magnitude(re, im) {
 return re.map((r, i) =&gt; Math.sqrt(r * r + im[i] * im[i]));
 }

 function drawSignal(canvas, data, color) {
 const dpr = window.devicePixelRatio || 1;
 canvas.width = canvas.offsetWidth * dpr;
 canvas.height = 160 * dpr;
 const ctx = canvas.getContext("2d");
 ctx.scale(dpr, dpr);
 const W = canvas.offsetWidth,
 H = 160;

 ctx.fillStyle = "#1a1d27";
 ctx.fillRect(0, 0, W, H);

 ctx.strokeStyle = "#333";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(0, H / 2);
 ctx.lineTo(W, H / 2);
 ctx.stroke();

 const max = Math.max(...data.map(Math.abs)) || 1;
 const show = data.slice(0, Math.floor(data.length / 2));
 const step = W / show.length;

 ctx.strokeStyle = color;
 ctx.lineWidth = 1.5;
 ctx.beginPath();
 show.forEach((v, i) =&gt; {
 const x = i * step;
 const y = H / 2 - (v / max) * (H / 2 - 12);
 if (i === 0) ctx.moveTo(x, y);
 else ctx.lineTo(x, y);
 });
 ctx.stroke();
 }

 function drawFreq(canvas, mag) {
 const dpr = window.devicePixelRatio || 1;
 canvas.width = canvas.offsetWidth * dpr;
 canvas.height = 160 * dpr;
 const ctx = canvas.getContext("2d");
 ctx.scale(dpr, dpr);
 const W = canvas.offsetWidth,
 H = 160;

 ctx.fillStyle = "#1a1d27";
 ctx.fillRect(0, 0, W, H);

 const half = mag.slice(0, Math.floor(N / 2));
 const max = Math.max(...half) || 1;
 const barW = W / half.length;

 half.forEach((v, k) =&gt; {
 const h = (v / max) * (H - 20);
 const x = k * barW;
 const intensity = v / max;
 const r = Math.floor(intensity * 255);
 const g = Math.floor(intensity * 140);
 const b = Math.floor((1 - intensity) * 220 + 35);
 ctx.fillStyle = `rgb(${r},${g},${b})`;
 ctx.fillRect(x, H - h, barW - 1, h);
 });

 ctx.fillStyle = "#666";
 ctx.font = "10px sans-serif";
 [0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120].forEach((k) =&gt; {
 const x = (k / (N / 2)) * W;
 ctx.fillText(k, x, H - 2);
 });
 ctx.fillStyle = "#555";
 ctx.fillText("주파수 k →", W - 70, H - 2);
 }

 function draw() {
 const type = select.value;
 const signal = getSignal(type);
 const { re, im } = dft(signal);
 const mag = magnitude(re, im);
 drawSignal(wrap.querySelector("#fv-timeCanvas"), signal, "#7eb8f7");
 drawFreq(wrap.querySelector("#fv-freqCanvas"), mag);
 }

 select.addEventListener("change", draw);
 window.addEventListener("resize", draw);
 draw();
 })();
 &lt;/script&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;복소평면에서의 이해도 가능하다. 주파수가 다르다면 상쇄되어 없어진다.
&lt;div class="fcv-wrap"&gt;
 &lt;style&gt;
 .fcv-wrap {
 background: transparent;
 color: #e0e0e0;
 font-family: "Segoe UI", sans-serif;
 margin: 1.5rem 0;
 }
 .fcv-wrap .fcv-header {
 margin-bottom: 12px;
 }
 .fcv-wrap .fcv-header h3 {
 color: #7eb8f7;
 font-size: 1rem;
 margin: 0 0 4px;
 border: none;
 }
 .fcv-wrap .fcv-header p {
 color: #888;
 font-size: 0.78rem;
 margin: 0;
 }
 .fcv-wrap .fcv-controls {
 display: flex;
 gap: 8px;
 flex-wrap: wrap;
 align-items: center;
 margin-bottom: 12px;
 padding: 10px 12px;
 background: #1a1d27;
 border-radius: 8px;
 }
 .fcv-wrap .fcv-ctrl-label {
 font-size: 0.75rem;
 color: #888;
 }
 .fcv-wrap .fcv-sep {
 width: 1px;
 height: 18px;
 background: #333;
 margin: 0 4px;
 }
 .fcv-wrap .fcv-freq-btn {
 background: #252836;
 color: #aaa;
 border: 1px solid #333;
 border-radius: 4px;
 padding: 3px 10px;
 font-size: 0.75rem;
 cursor: pointer;
 transition: all 0.15s;
 }
 .fcv-wrap .fcv-freq-btn:hover {
 background: #2e3347;
 color: #e0e0e0;
 }
 .fcv-wrap .fcv-freq-btn.active {
 background: #2a4a7f;
 color: #7eb8f7;
 border-color: #3a6abf;
 }
 .fcv-wrap .fcv-main {
 display: grid;
 grid-template-columns: 1fr 1fr;
 gap: 12px;
 }
 @media (max-width: 640px) {
 .fcv-wrap .fcv-main {
 grid-template-columns: 1fr;
 }
 }
 .fcv-wrap .fcv-panel {
 background: #1a1d27;
 border-radius: 8px;
 overflow: hidden;
 }
 .fcv-wrap .fcv-panel-title {
 font-size: 0.72rem;
 color: #7eb8f7;
 padding: 8px 12px 4px;
 display: flex;
 justify-content: space-between;
 align-items: center;
 gap: 4px;
 min-height: 2rem;
 }
 .fcv-wrap .fcv-val {
 font-size: 0.7rem;
 color: #f0a500;
 font-family: "JetBrains Mono", monospace;
 white-space: nowrap;
 flex-shrink: 0;
 min-width: 10rem;
 text-align: right;
 }
 .fcv-wrap canvas {
 width: 100%;
 height: 220px;
 display: block;
 }
 .fcv-wrap .fcv-info-bar {
 display: flex;
 gap: 8px;
 flex-wrap: nowrap;
 overflow: hidden;
 padding: 6px 12px;
 font-size: 0.68rem;
 color: #666;
 font-family: "JetBrains Mono", monospace;
 border-top: 1px solid #222;
 white-space: nowrap;
 }
 .fcv-wrap .fcv-info-bar span {
 flex-shrink: 1;
 overflow: hidden;
 text-overflow: ellipsis;
 }
 .fcv-wrap .fcv-info-bar b {
 color: #aaa;
 }
 .fcv-wrap .fcv-match-label {
 font-size: 0.7rem;
 }
 &lt;/style&gt;

 &lt;div class="fcv-header"&gt;
 &lt;h3&gt;푸리에 변환 — 복소평면 시각화&lt;/h3&gt;
 &lt;p&gt;
 h(t) = sin(2π·f_signal·t) &amp;nbsp;|&amp;nbsp; 검사 주파수 x 를 바꿔가며 적분값이
 어떻게 달라지는지 확인
 &lt;/p&gt;
 &lt;/div&gt;

 &lt;div class="fcv-controls"&gt;
 &lt;span class="fcv-ctrl-label"&gt;신호 주파수 f =&lt;/span&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-signal="3"&gt;3 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-signal="5"&gt;5 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-signal="7"&gt;7 Hz&lt;/button&gt;
 &lt;div class="fcv-sep"&gt;&lt;/div&gt;
 &lt;span class="fcv-ctrl-label"&gt;검사 주파수 x =&lt;/span&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="1"&gt;1 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="2"&gt;2 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-check="3"&gt;3 Hz ✓&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="5"&gt;5 Hz&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-check="7"&gt;7 Hz&lt;/button&gt;
 &lt;div class="fcv-sep"&gt;&lt;/div&gt;
 &lt;span class="fcv-ctrl-label"&gt;속도&lt;/span&gt;
 &lt;button class="fcv-freq-btn" data-fcv-speed="0.3"&gt;0.3×&lt;/button&gt;
 &lt;button class="fcv-freq-btn active" data-fcv-speed="1"&gt;1×&lt;/button&gt;
 &lt;button class="fcv-freq-btn" data-fcv-speed="2"&gt;2×&lt;/button&gt;
 &lt;/div&gt;

 &lt;div class="fcv-main"&gt;
 &lt;div class="fcv-panel"&gt;
 &lt;div class="fcv-panel-title"&gt;
 복소평면 — h(t)·e^{−2πitx} 궤적
 &lt;span class="fcv-val" id="fcv-sumVal"&gt;합: —&lt;/span&gt;
 &lt;/div&gt;
 &lt;canvas id="fcv-complexCanvas"&gt;&lt;/canvas&gt;
 &lt;div class="fcv-info-bar"&gt;
 &lt;span&gt;t = &lt;b id="fcv-tVal"&gt;0.000&lt;/b&gt;s&lt;/span&gt;
 &lt;span
 &gt;Re=&lt;b id="fcv-reVal"&gt;0.000&lt;/b&gt; Im=&lt;b id="fcv-imVal"&gt;0.000&lt;/b&gt;&lt;/span
 &gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;div class="fcv-panel"&gt;
 &lt;div class="fcv-panel-title"&gt;
 시간 도메인 — h(t) 와 검사파
 &lt;span class="fcv-match-label" id="fcv-matchLabel"&gt;&lt;/span&gt;
 &lt;/div&gt;
 &lt;canvas id="fcv-timeCanvas"&gt;&lt;/canvas&gt;
 &lt;div class="fcv-info-bar"&gt;
 &lt;span&gt;누적 Re = &lt;b id="fcv-cumRe"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;span&gt;누적 Im = &lt;b id="fcv-cumIm"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;span&gt;|합| = &lt;b id="fcv-cumMag"&gt;0.000&lt;/b&gt;&lt;/span&gt;
 &lt;/div&gt;
 &lt;/div&gt;
 &lt;/div&gt;

 &lt;script&gt;
 (function () {
 const state = {
 fSignal: 3,
 fCheck: 3,
 speed: 1,
 t: 0,
 trailRe: [],
 trailIm: [],
 cumRe: 0,
 cumIm: 0,
 dt: 0.005,
 maxTrail: 600,
 };

 const wrap = document.currentScript.closest(".fcv-wrap");

 wrap.querySelectorAll("[data-fcv-signal]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-signal]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.fSignal = parseFloat(btn.dataset.fcvSignal);
 reset();
 updateMatchLabel();
 });
 });
 wrap.querySelectorAll("[data-fcv-check]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-check]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.fCheck = parseFloat(btn.dataset.fcvCheck);
 reset();
 updateMatchLabel();
 });
 });
 wrap.querySelectorAll("[data-fcv-speed]").forEach((btn) =&gt; {
 btn.addEventListener("click", () =&gt; {
 wrap
 .querySelectorAll("[data-fcv-speed]")
 .forEach((b) =&gt; b.classList.remove("active"));
 btn.classList.add("active");
 state.speed = parseFloat(btn.dataset.fcvSpeed);
 });
 });

 function reset() {
 state.t = 0;
 state.trailRe = [];
 state.trailIm = [];
 state.cumRe = 0;
 state.cumIm = 0;
 }

 function updateMatchLabel() {
 const el = wrap.querySelector("#fcv-matchLabel");
 if (state.fSignal === state.fCheck) {
 el.textContent = "✓ 주파수 일치 → 발산";
 el.style.color = "#4aff8a";
 } else {
 el.textContent = "✗ 주파수 불일치 → 상쇄";
 el.style.color = "#ff6b6b";
 }
 }
 updateMatchLabel();

 const cC = wrap.querySelector("#fcv-complexCanvas");
 const cT = wrap.querySelector("#fcv-timeCanvas");

 function resize() {
 const dpr = window.devicePixelRatio || 1;
 [cC, cT].forEach((c) =&gt; {
 const rect = c.getBoundingClientRect();
 c.width = rect.width * dpr;
 c.height = 220 * dpr;
 });
 }
 window.addEventListener("resize", resize);
 resize();

 function drawComplex() {
 const dpr = window.devicePixelRatio || 1;
 const ctx = cC.getContext("2d");
 const W = cC.width / dpr,
 H = cC.height / dpr;
 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
 ctx.clearRect(0, 0, W, H);

 const cx = W / 2,
 cy = H / 2;
 const R = Math.min(W, H) * 0.38;

 ctx.beginPath();
 ctx.arc(cx, cy, R, 0, Math.PI * 2);
 ctx.strokeStyle = "rgba(30,60,100,0.4)";
 ctx.lineWidth = 1;
 ctx.stroke();

 ctx.strokeStyle = "rgba(255,255,255,0.07)";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(cx - R - 20, cy);
 ctx.lineTo(cx + R + 20, cy);
 ctx.stroke();
 ctx.beginPath();
 ctx.moveTo(cx, cy - R - 20);
 ctx.lineTo(cx, cy + R + 20);
 ctx.stroke();

 ctx.font = "9px JetBrains Mono, monospace";
 ctx.fillStyle = "rgba(74,158,255,0.35)";
 ctx.fillText("Re", cx + R + 6, cy + 4);
 ctx.fillText("Im", cx + 4, cy - R - 8);
 ctx.fillText("+1", cx + R - 14, cy - 6);
 ctx.fillText("-1", cx - R - 2, cy - 6);

 if (state.trailRe.length &gt; 1) {
 ctx.beginPath();
 for (let i = 0; i &lt; state.trailRe.length; i++) {
 const px = cx + state.trailRe[i] * R;
 const py = cy - state.trailIm[i] * R;
 if (i === 0) ctx.moveTo(px, py);
 else ctx.lineTo(px, py);
 }
 ctx.strokeStyle =
 state.fSignal === state.fCheck
 ? "rgba(74,255,140,0.5)"
 : "rgba(255,107,107,0.4)";
 ctx.lineWidth = 1.5;
 ctx.stroke();
 }

 const h = Math.sin(2 * Math.PI * state.fSignal * state.t);
 const angle = -2 * Math.PI * state.fCheck * state.t;
 const re = h * Math.cos(angle);
 const im = h * Math.sin(angle);
 const px = cx + re * R,
 py = cy - im * R;

 ctx.beginPath();
 ctx.moveTo(cx, cy);
 ctx.lineTo(px, py);
 ctx.strokeStyle = "rgba(255,255,255,0.15)";
 ctx.lineWidth = 1;
 ctx.stroke();

 ctx.beginPath();
 ctx.arc(px, py, 5, 0, Math.PI * 2);
 ctx.fillStyle = state.fSignal === state.fCheck ? "#4aff8a" : "#ff6b6b";
 ctx.fill();

 const cumScale = 1 / (state.trailRe.length + 1);
 const avgRe = state.cumRe * cumScale;
 const avgIm = state.cumIm * cumScale;
 const sumPx = cx + avgRe * R,
 sumPy = cy - avgIm * R;

 ctx.beginPath();
 ctx.moveTo(cx, cy);
 ctx.lineTo(sumPx, sumPy);
 ctx.strokeStyle = "#f0a500";
 ctx.lineWidth = 2;
 ctx.stroke();
 ctx.beginPath();
 ctx.arc(sumPx, sumPy, 4, 0, Math.PI * 2);
 ctx.fillStyle = "#f0a500";
 ctx.fill();

 const mag = Math.sqrt(avgRe * avgRe + avgIm * avgIm);
 wrap.querySelector("#fcv-sumVal").textContent =
 `|평균| = ${mag.toFixed(3)}`;
 }

 function drawTime() {
 const dpr = window.devicePixelRatio || 1;
 const ctx = cT.getContext("2d");
 const W = cT.width / dpr,
 H = cT.height / dpr;
 ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
 ctx.clearRect(0, 0, W, H);

 const midH = H / 2,
 amp = H * 0.22,
 duration = 2;

 ctx.strokeStyle = "rgba(255,255,255,0.07)";
 ctx.lineWidth = 1;
 ctx.beginPath();
 ctx.moveTo(0, midH);
 ctx.lineTo(W, midH);
 ctx.stroke();

 ctx.beginPath();
 for (let px = 0; px &lt; W; px++) {
 const t = (px / W) * duration;
 const y = midH - Math.sin(2 * Math.PI * state.fSignal * t) * amp;
 if (px === 0) ctx.moveTo(px, y);
 else ctx.lineTo(px, y);
 }
 ctx.strokeStyle = "rgba(74,158,255,0.7)";
 ctx.lineWidth = 1.5;
 ctx.stroke();

 ctx.beginPath();
 for (let px = 0; px &lt; W; px++) {
 const t = (px / W) * duration;
 const y = midH - Math.cos(2 * Math.PI * state.fCheck * t) * amp;
 if (px === 0) ctx.moveTo(px, y);
 else ctx.lineTo(px, y);
 }
 ctx.strokeStyle = "rgba(240,165,0,0.5)";
 ctx.lineWidth = 1.5;
 ctx.stroke();

 ctx.beginPath();
 const tStart = (state.t / duration) * W;
 for (let px = 0; px &lt; Math.min(tStart, W); px++) {
 const t = (px / W) * duration;
 const product =
 Math.sin(2 * Math.PI * state.fSignal * t) *
 Math.cos(2 * Math.PI * state.fCheck * t);
 const y = midH - product * amp;
 if (px === 0) ctx.moveTo(px, midH);
 ctx.lineTo(px, y);
 }
 ctx.lineTo(Math.min(tStart, W), midH);
 ctx.closePath();
 ctx.fillStyle =
 state.fSignal === state.fCheck
 ? "rgba(74,255,140,0.12)"
 : "rgba(255,107,107,0.1)";
 ctx.fill();

 const curX = (state.t / duration) * W;
 if (curX &lt;= W) {
 ctx.beginPath();
 ctx.moveTo(curX, 0);
 ctx.lineTo(curX, H);
 ctx.strokeStyle = "rgba(255,255,255,0.2)";
 ctx.lineWidth = 1;
 ctx.setLineDash([3, 4]);
 ctx.stroke();
 ctx.setLineDash([]);
 }

 ctx.font = "9px JetBrains Mono, monospace";
 ctx.fillStyle = "rgba(74,158,255,0.6)";
 ctx.fillText(`h(t) = sin(2π·${state.fSignal}·t)`, 8, 16);
 ctx.fillStyle = "rgba(240,165,0,0.6)";
 ctx.fillText(`검사파 = cos(2π·${state.fCheck}·t)`, 8, 28);
 }

 let last = null;
 function loop(ts) {
 if (!last) last = ts;
 const elapsed = (ts - last) / 1000;
 last = ts;

 state.t += elapsed * state.speed;
 if (state.t &gt; 2) {
 state.t = 0;
 state.trailRe = [];
 state.trailIm = [];
 state.cumRe = 0;
 state.cumIm = 0;
 }

 const h = Math.sin(2 * Math.PI * state.fSignal * state.t);
 const angle = -2 * Math.PI * state.fCheck * state.t;
 const re = h * Math.cos(angle);
 const im = h * Math.sin(angle);

 state.trailRe.push(re);
 state.trailIm.push(im);
 if (state.trailRe.length &gt; state.maxTrail) {
 state.trailRe.shift();
 state.trailIm.shift();
 }
 state.cumRe += re * state.dt;
 state.cumIm += im * state.dt;

 drawComplex();
 drawTime();

 wrap.querySelector("#fcv-tVal").textContent = state.t.toFixed(3);
 wrap.querySelector("#fcv-reVal").textContent = re.toFixed(3);
 wrap.querySelector("#fcv-imVal").textContent = im.toFixed(3);
 wrap.querySelector("#fcv-cumRe").textContent = state.cumRe.toFixed(3);
 wrap.querySelector("#fcv-cumIm").textContent = state.cumIm.toFixed(3);
 wrap.querySelector("#fcv-cumMag").textContent = Math.sqrt(
 state.cumRe ** 2 + state.cumIm ** 2,
 ).toFixed(3);

 requestAnimationFrame(loop);
 }

 requestAnimationFrame(loop);
 })();
 &lt;/script&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="이산-푸리에-변환-dft"&gt;&lt;a href="#%ec%9d%b4%ec%82%b0-%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98-dft" class="header-anchor"&gt;&lt;/a&gt;이산 푸리에 변환 (DFT)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;현실에서 얻을 수 있는 데이터는 이산적이다.
&lt;ul&gt;
&lt;li&gt;물론 라플라스 변환마냥 수학적으로 변환해서 계산하는 테크닉적으로 쓰려면 그냥 쓰면 된다.&lt;/li&gt;
&lt;li&gt;사인파를 많이 쓸거같은 음악 데이터로 생각해보면, 결국 소리를 받을때도, 저장할때도 이산적으로 저장할 수 밖에 없다.&lt;/li&gt;
&lt;li&gt;따라서 이산 데이터에 대해서도 푸리에 변환을 정의해보자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그러면 적분값 대신 시그마 값으로 표현할 수 있겠다.&lt;/li&gt;
&lt;li&gt;
$$X[k] = \sum_{n=0}^{N-1}x[n]\exp\left(-i\frac{2\pi kn}{N}\right) = \sum_{n = 0}^{N - 1}{x[n] \omega^{kn}} \,\,\,\,  (\omega = \exp\left(-i\dfrac{2\pi}{N}\right))$$&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;연속&lt;/th&gt;
 &lt;th&gt;이산&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;$t$&lt;/td&gt;
 &lt;td&gt;$n/N$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$h(t)$&lt;/td&gt;
 &lt;td&gt;$x[n]$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$x$ (주파수)&lt;/td&gt;
 &lt;td&gt;$k$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$dt$&lt;/td&gt;
 &lt;td&gt;$1/N$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;$e^{-2\pi i t x}$&lt;/td&gt;
 &lt;td&gt;$\omega^{kn}$&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;위와 같이 대응되게 생각하면 된다.
&lt;ul&gt;
&lt;li&gt;$\omega$는 단위근이라고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이때 $\omega^N = 1$이므로, $0 \leq k &lt; N$ 인 $k$에 대해서만 유의미한 결과를 얻어낼 수 있다.
&lt;ul&gt;
&lt;li&gt;따라서 DFT에서는 최대 $N$개의 주파수에 대해서만 검증할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;여기서도 물론 역변환을 정의할 수 있다.

$$x[n] = \frac{1}{N}\sum_{k=0}^{N-1}X[k]\omega^{-kn}$$&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="고속-푸리에-변환"&gt;&lt;a href="#%ea%b3%a0%ec%86%8d-%ed%91%b8%eb%a6%ac%ec%97%90-%eb%b3%80%ed%99%98" class="header-anchor"&gt;&lt;/a&gt;고속 푸리에 변환
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;위의 푸리에 변환을 계산하는데, 총 $N$개의 주파수에 대해 $N$개 신호의합을 구하니까 $O(N^2)$의 시간복잡도가 걸린다.&lt;/li&gt;
&lt;li&gt;여기서 관찰을 조금 해보자.
&lt;ul&gt;
&lt;li&gt;8개의 데이터 $x_0, x_1, x_2, \cdots, x_7$이 있다고 해보자.&lt;/li&gt;
&lt;li&gt;이때 $x_n, x_{n+4}$에 곱해지는 계수를 비교하면 $\exp{(-i\pi k)}$ 만큼의 차이가 난다.&lt;/li&gt;
&lt;li&gt;$k$가 홀수일때는 $-1$, 짝수일때는 $1$이다.&lt;/li&gt;
&lt;li&gt;이를 이용해서 DFT의 식을 짝수항과 홀수항으로 나눠서 다음과 같이 쓸 수 있다.
 
$$\begin{align}
 X[k] &amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi kn}{N}\right) \\
 &amp;= \sum_{n=0}^{N/2-1} x[2n]\exp\left(-i\frac{2\pi k(2n)}{N}\right) + \sum_{n=0}^{N/2-1} x[2n+1]\exp\left(-i\frac{2\pi k(2n+1)}{N}\right) \\
 &amp;= \sum_{n=0}^{N/2-1} x[2n]\exp\left(-i\frac{2\pi kn}{N/2}\right) + \exp\left(-i\frac{2\pi k}{N}\right)\sum_{n=0}^{N/2-1} x[2n+1]\exp\left(-i\frac{2\pi kn}{N/2}\right) \\
 &amp;= E[k] + \exp\left(-i\frac{2\pi k}{N}\right)O[k] \\
 &amp;= E[k] + \omega^kO[k]
 \end{align}$$
 - 관찰
 - 위 식의 짝수항 / 홀수항 부분은 $N/2$ 크기의 DFT와 같다.
 - 이 식은 $k &lt; N/2$ 에 대해서만 정의되지만, $k \geq N/2$에 대해서 처리하는 방법은 위에서 찾았다.

$$\begin{align}
X[k+N/2] &amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi (k+N/2)n}{N}\right) \\
&amp;= \sum_{n=0}^{N-1} x[n]\exp\left(-i\frac{2\pi kn}{N}\right)\exp(-i\pi n) \\
&amp;= E[k] - \omega^k O[k]
\end{align}$$
 - 따라서 분할정복을 이용해서 $O(N\log{N})$ 시간에 연산을 수행할 수 있겠다.
 - 이를 고속 푸리에 변환, FFT라 하자.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="합성곱"&gt;&lt;a href="#%ed%95%a9%ec%84%b1%ea%b3%b1" class="header-anchor"&gt;&lt;/a&gt;합성곱
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;근데 저 수학 씹덕같은걸 왜배웠냐?
&lt;ul&gt;
&lt;li&gt;DFT를 통해 합성곱을, 합성곱으로 다항식의 곱셈을, 다항식의 곱셈으로 두 수의 곱셈을 할 수 있다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;다음과 같은 함수가 있다고 하자.
&lt;ul&gt;
&lt;li&gt;$f(x) = a_{N-1}x^{N-1} + a_{N-2}x^{N-2} + \cdots + a_1x + a_0$&lt;/li&gt;
&lt;li&gt;여기서, $x = \omega^k$를 대입하면 그 결과는 DFT와 같다!&lt;/li&gt;
&lt;li&gt;이를 이용해서 두 다항식 $f, g$에 대한 DFT로 새로운 함수 $h$에 대한 DFT 결과값을 구하고, 이를 역변환해서 $h$를 찾아낼 수 있겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이걸 코드로 옮기면 다음과 같다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;using&lt;/span&gt; cd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; complex&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;double&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; PI &lt;span style="color:#f92672"&gt;=&lt;/span&gt; acos(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; fft(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(N &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; a;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; even(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;), odd(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fft(even);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; fft(odd);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ret(N);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(k, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd t &lt;span style="color:#f92672"&gt;=&lt;/span&gt; polar(&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;, &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;PI&lt;span style="color:#f92672"&gt;*&lt;/span&gt;k&lt;span style="color:#f92672"&gt;/&lt;/span&gt;N) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; odd[k];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;-&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ifft(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; a){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(N &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; a;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; even(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;), odd(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ifft(even);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ifft(odd);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; ret(N);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(k, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd t &lt;span style="color:#f92672"&gt;=&lt;/span&gt; polar(&lt;span style="color:#ae81ff"&gt;1.0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;*&lt;/span&gt;PI&lt;span style="color:#f92672"&gt;*&lt;/span&gt;k&lt;span style="color:#f92672"&gt;/&lt;/span&gt;N) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; odd[k];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;+&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ret[k&lt;span style="color:#f92672"&gt;+&lt;/span&gt;N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even[k] &lt;span style="color:#f92672"&gt;-&lt;/span&gt; t;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ret;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;ifft를 사용할때 마지막에 a의 길이로 나눠줘야 함에 주의하자.&lt;/li&gt;
&lt;li&gt;사실상 구조가 똑같아서, bool inv같은걸로 최적화하는게 더 좋은거같다&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="최적화"&gt;&lt;a href="#%ec%b5%9c%ec%a0%81%ed%99%94" class="header-anchor"&gt;&lt;/a&gt;최적화
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;하지만 이렇게 재귀함수로 새로 배열을 만들면서 진행하면, 느리고 메모리도 많이 먹는다.&lt;/li&gt;
&lt;li&gt;인접한 원소들 끼리만 연산할 수 있게, 배열을 재배치하자.&lt;/li&gt;
&lt;li&gt;$[0, 4, 2, 6, 1, 5, 3, 7]$과 같은 모양이면 좋겠다.&lt;/li&gt;
&lt;li&gt;이는 비트를 좌우로 뒤집는 것으로 수행 가능하다.&lt;/li&gt;
&lt;li&gt;이렇게 하면 반복문으로 계산할 수 있고, polar 극좌표점 자체도 미리 계산해둬서 쓰면 $NlogN$번에서 $N/2$번으로 줄일 수 있겠다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;namespace&lt;/span&gt; FFT{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fft&lt;/span&gt;(vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;a, &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; inv &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; N &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a.size();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assert(N &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; (N &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; (N&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// N은 2의 거듭제곱이어야 함
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 비트 뒤집기
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, j &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; bit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(; j &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; bit; bit &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; j &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; bit;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; j) swap(a[i], a[j]);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 단위근 미리 계산
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;double&lt;/span&gt; ang &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; acos(&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;/&lt;/span&gt; N &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (inv &lt;span style="color:#f92672"&gt;?&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; vector&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;cd&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; w(N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;); &lt;span style="color:#75715e"&gt;// 단위근
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) w[i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; cd(cos(ang&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i), sin(ang&lt;span style="color:#f92672"&gt;*&lt;/span&gt;i));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Cooley-Tukey FFT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; len &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;; len &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; N; len &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; step &lt;span style="color:#f92672"&gt;=&lt;/span&gt; N &lt;span style="color:#f92672"&gt;/&lt;/span&gt; len;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; N; i &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; len){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; rep(j, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cd even &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j], odd &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;*&lt;/span&gt; w[j&lt;span style="color:#f92672"&gt;*&lt;/span&gt;step];
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;+&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a[i&lt;span style="color:#f92672"&gt;+&lt;/span&gt;j&lt;span style="color:#f92672"&gt;+&lt;/span&gt;(len&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; even &lt;span style="color:#f92672"&gt;-&lt;/span&gt; odd;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 역 FFT인 경우 결과를 N으로 나눔
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(inv) rep(i, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, N) a[i] &lt;span style="color:#f92672"&gt;/=&lt;/span&gt; N;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;1067번 문제 기준 608ms -&amp;gt; 92ms로, 6.6배 가까이 줄어들었다!&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="질문-사항"&gt;&lt;a href="#%ec%a7%88%eb%ac%b8-%ec%82%ac%ed%95%ad" class="header-anchor"&gt;&lt;/a&gt;❔질문 사항
&lt;/h2&gt;&lt;blockquote class="alert alert-question"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;&lt;/span&gt;
 &lt;span class="alert-title"&gt;주파수에 $2\pi$를 넣는 이유&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;주파수 $f$ -&amp;gt; 1초에 몇번 진동하는가
그냥 $\sin{ft}$라고 쓰면 $t = 1$일때 까지 $f$번 진동하지 않는다 (주기가 $2\pi$니까)
따라서 $2\pi$를 곱해줘서 각주파수로 변환해준다.&lt;/p&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;blockquote class="alert alert-question"&gt;
 &lt;div class="alert-header"&gt;
 &lt;span class="alert-icon"&gt;&lt;/span&gt;
 &lt;span class="alert-title"&gt;$h(x)$의 모든 값들은 발산하는데, 어떻게 그리지?&lt;/span&gt;
 &lt;/div&gt;
 &lt;div class="alert-body"&gt;
 &lt;p&gt;그래서 수학적으로는 디렉 델타 함수를 이용한다.
하지만 현실 데이터에서는 신호가 유한하거나 / 감쇠하기때문에, 변환 결과 발산하지 않고 자연스럽게 수렴한다.&lt;/p&gt;
 &lt;/div&gt;
 &lt;/blockquote&gt;
&lt;h2 id="-참고-자료"&gt;&lt;a href="#-%ec%b0%b8%ea%b3%a0-%ec%9e%90%eb%a3%8c" class="header-anchor"&gt;&lt;/a&gt;🔗 참고 자료
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.acmicpc.net/blog/view/141" target="_blank" rel="noopener"
 &gt;DFT, FFT 이해하기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>