<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>LimeCoding</title>
    <link>https://limecoding.tistory.com/</link>
    <description>주로 일기를 쓰지만 공부한 내용도 일기로 쓰는 본격 일기형 블로그</description>
    <language>ko</language>
    <pubDate>Sun, 9 Aug 2026 03:53:37 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>LimeCoding</managingEditor>
    <item>
      <title>[ASMR 스트리밍 서비스] 개발일지 - 1 디스코드로 알림 구현하기</title>
      <link>https://limecoding.tistory.com/256</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;개요&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;실시간 서비스를 제공하다보면 서버가 죽는다던가 특정 동작에서 오류가 발생하는 등 많은 문제 상황이 발생한다. 실시간 서비스에서 특히 사용자가 많이 이용하는 기능에 문제가 발생하면 사용자의 불편 뿐만 아니라 매출 하락으로도 이어질 수 있다. 이런 문제를 사전에 탐지할 수 있다면(테스트 코드와 같은 미연에 방지할 수 있는 방법들은 있다!) 좋겠지만 그렇지 않은 경우에는 개발자가 빠르게 대응을 할 수 있어야 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;개발자가 대응을 하기 위해선 문제 사항이 개발자에게 전달이 되어야 한다. 가장 원초적인 방법으로는 시시때때로 로그를 확인하는 방법이다. 그러나 이 방법에는 다음과 같은 제약 사항을 가진다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1. 개발자가 직접 서버로 접속할 수 있어야 한다.(컴퓨터가 없다면 접근이 힘들다!)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2. 언제 어디서 버그가 날지 모른다.(만약 내가 확인하고 난 뒤 바로 오류가 생긴다면?)&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;어떻게 해결하지?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위와같은 문제가 있을 때 우리의 핵심 문제는 다음과 같이 정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;b&gt;컴퓨터가 없어도 어디서 버그가 났는지 쉽게 확인할 수 있어야 한다!&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;첫 번째로 컴퓨터가 없어도 어디서나 접근할 수 있어야 한다는 문제를 풀어보자. 우리는 데스크탑과 같은 컴퓨터가 없어도 손에 작은 컴퓨터를 가지고 있다. 바로 스마트폰이다. 스마트폰을 이용해서 카카오톡이나 문자 메세지와 같은 통신 수단을 이용해 다른 사용자와 소통한다. 그러면 이걸 사람과 사람끼리의 소통이 아닌 컴퓨터와 사람과의 소통으로 치환한다면 우리는 컴퓨터가 하는 말을 들을 수 있다.(아주 미친 소리같아보인다) 여기서 우리는 다양한 메신저 프로그램을 이용할 수 있는데 그중 디스코드를 선택했다. 디스코드를 선택한 이유는 현재 컴퓨터 환경에서 추가적인 설정없이 디스코드로 바로 접근이 가능하기에 디스코드를 사용했다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;두 번째로 어디서 버그가 났는지 확인할 수 있어야 한다는 문제인데 이건 정말로 간단하다. 간단하게 우리가 특정 부분에서 로그를 찍듯이 에러가 나면 디스코드를 통해 에러 메세지를 전송하면 된다. 구조는 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Project (1).png&quot; data-origin-width=&quot;3616&quot; data-origin-height=&quot;1248&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kqw8P/btsNOkhW1Wn/QOqiVo1esxXcs8XkPZQ6Fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kqw8P/btsNOkhW1Wn/QOqiVo1esxXcs8XkPZQ6Fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kqw8P/btsNOkhW1Wn/QOqiVo1esxXcs8XkPZQ6Fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqw8P%2FbtsNOkhW1Wn%2FQOqiVo1esxXcs8XkPZQ6Fk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3616&quot; height=&quot;1248&quot; data-filename=&quot;Project (1).png&quot; data-origin-width=&quot;3616&quot; data-origin-height=&quot;1248&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;구현 방법&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구현 방법은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 디스코드에 사용되는 알림 기능을 인터페이스로 정의한다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;package limecoding.asmrstreamingservice.service.alert;

public interface AlertService {
    void sendAlert(String message);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메세지 DTO&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;groovy&quot;&gt;&lt;code&gt;package limecoding.asmrstreamingservice.dto.alert;

import lombok.*;

@Setter
@Getter
@ToString
@AllArgsConstructor
public class DiscordWebHookMessage {
    private String content;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음으로 구현체를 2개를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. discord 알림 서비스&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;package limecoding.asmrstreamingservice.service.alert.impl;

import limecoding.asmrstreamingservice.dto.alert.DiscordWebHookMessage;
import limecoding.asmrstreamingservice.service.alert.AlertService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;


@RequiredArgsConstructor
@Slf4j
@Service
@ConditionalOnProperty(name = &quot;alert.discord.enabled&quot;, havingValue = &quot;true&quot;)
public class DiscordAlertService implements AlertService {

    @Value(&quot;${alert.discord.url}&quot;)
    private String discordUrl;
    private final RestTemplate restTemplate;

    @Override
    public void sendAlert(String message) {

        DiscordWebHookMessage payload = new DiscordWebHookMessage(message);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity&amp;lt;DiscordWebHookMessage&amp;gt; request = new HttpEntity&amp;lt;&amp;gt;(payload, headers);

        try {
            restTemplate.postForEntity(discordUrl, request, String.class);
        } catch (Exception e) {
            log.error(&quot;디스코드 전송 실패 : {}&quot;, e.getMessage());
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 가짜 알림 서비스&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot;&gt;&lt;code&gt;package limecoding.asmrstreamingservice.service.alert.impl;

import limecoding.asmrstreamingservice.service.alert.AlertService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

@Service
@ConditionalOnProperty(name = &quot;alert.discord.enabled&quot;, havingValue = &quot;false&quot;)
public class NoAlertService implements AlertService {
    @Override
    public void sendAlert(String message) {
        // Do not Send Message
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이렇게 2개를 만든 이유는 알림 서비스를 on/off할 수 있도록 하기 위해서이다. application.properties에서 true, false를 놓고 if문을 이용해서 할 수도 있겠지만 보기도 지저분해지고 이로 인해 유지보수하기가 힘들다.(if문으로 조건보고 {}안에 코드 짜고 있으면 무슨 말인지 알 수 있다.) 이를 방지하고 위해 &lt;a href=&quot;https://www.baeldung.com/spring-conditionalonproperty&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;@ConditionalOnProperty&lt;/a&gt;를 사용했는데 이는 선택적으로 빈을 만들 때 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들면 간단하게 디스코드 알림을 on/off할 수 있다. 다음 application.properties에 다음과 같이 설정한다.&lt;/p&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;#discord alert setting
alert.discord.url=&amp;lt;discord url&amp;gt;
#true/false
alert.discord.enabled=false&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #1e1f22; color: #bcbec4;&quot;&gt;
&lt;pre class=&quot;java&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;private final AlertService alertService;

public void test(){
	alertService.sendAlert(&quot;알림 보내기&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 이런 식으로 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스코드 webhook 문서&lt;/p&gt;
&lt;figure id=&quot;og_1746621717023&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Discord Developer Portal &amp;mdash; API Docs for Bots and Developers&quot; data-og-description=&quot;Integrate your service with Discord &amp;mdash; whether it's a bot or a game or whatever your wildest imagination can come up with.&quot; data-og-host=&quot;discord.com&quot; data-og-source-url=&quot;https://discord.com/developers/docs/resources/webhook&quot; data-og-url=&quot;https://discord.com/developers/docs/resources/webhook&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cBMocT/hyYMZPsRtr/K2pBq0zkKeKNsd8Neh0vC1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/cBQCbg/hyYRrDv1kc/2evuCCltKwogKP0Om3cUk1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256&quot;&gt;&lt;a href=&quot;https://discord.com/developers/docs/resources/webhook&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://discord.com/developers/docs/resources/webhook&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cBMocT/hyYMZPsRtr/K2pBq0zkKeKNsd8Neh0vC1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256,https://scrap.kakaocdn.net/dn/cBQCbg/hyYRrDv1kc/2evuCCltKwogKP0Om3cUk1/img.png?width=2400&amp;amp;height=1256&amp;amp;face=0_0_2400_1256');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Discord Developer Portal &amp;mdash; API Docs for Bots and Developers&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Integrate your service with Discord &amp;mdash; whether it's a bot or a game or whatever your wildest imagination can come up with.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;discord.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>개인 프로젝트/ASMR 스트리밍 서비스</category>
      <category>디스코드</category>
      <category>스프링 알림</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/256</guid>
      <comments>https://limecoding.tistory.com/256#entry256comment</comments>
      <pubDate>Wed, 7 May 2025 21:42:38 +0900</pubDate>
    </item>
    <item>
      <title>[Google MLB] Gemma sprint 후기</title>
      <link>https://limecoding.tistory.com/254</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;이번에 구글 머신러닝 부트 캠프를 진행하면서 Gemma2를 파인튜닝하는 프로젝트를 진행했다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아이디어 선정&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;아이디어를 선정할 때 처음에는 크게 떠오르는 것들이 없었는데 다른 분들 하신 것을 보다보니 옛날에 해보고 싶었던 것들이나 재미있는 것들이 떠올랐지만 주제가 겹치거나 너무 간단한 것들이 떠올라서 팀원분과 의논을 했다. 그러다 마침 팀원분이 특허 관련해서 공부하신 분이라 특허와 LLM을 합쳐 특허 명세서 작성을 도와주는 모델을 만들면 좋겠다는 생각이 들었다. 나도 예전에 특허 명세서를 작성하면서 어려움을 겪은 적이 있어서 조금 더 마음이 갔다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;데이터 셋&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;특허 명세서를 작성하기 위해선 특허 명세서에 대한 데이터 셋이 필요했다. 해당 데이터 셋은 AI-Hub에서 가져오기로 했다. 특허 명세서를 작성하는데 있어서 중요한 항목들이 있는데 그중 하나인 청구항 내용이 우리가 구한 데이터 셋에는 없었다. 그래서 키프리스 크롤링을 통해 데이터 셋을 크롤링하고 있었는데 팀원분께서 robot.txt에서 네이버, 구글과 같은 봇만 허용되고 다른 크롤링은 막아놓았다는 얘기를 듣고 아차! 싶었다. 그래서 다시 문서에 대한 정보를 찾아보고 며엣서 문서가 오픈된 문서라서 손으로 직접 가져가는 것에는 문제가 없을 것이라고 판단되어 팀원분과 같이 데이터를 모아서 약 1000개 정도 데이터를 모았다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;모델 학습&lt;/b&gt;&lt;/h1&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;파인 튜닝이 처음이라 어떻게 하는지 잘 몰라서 처음에는 인터넷을 찾아다니며 어떻게 파인 튜닝을 하는지 튜토리얼들을 찾아다녔다. 대강 감을 잡은 뒤 gemma2-2b-it를 기본 모델로 사용해 파인튜닝을 진행했다. 처음에서 huggingface의 Transformer를 이용해서 했으나 특허 명세서 작성을 위해 필요한 학습 문장 길이가 너무 길어 입력 길이를 늘리니 메모리가 터지고 메모리가 터지지 않게 길이를 제한하려고 보니 중간에 짤리는 현상이 발생했다. 이걸 어떻게 해결할까 생각하다가 unsloth라는 것을 찾게 되었고 unsloth가 효율적으로 메모리를 관리하여 한정된 자원에서 더 큰 모델을 학습시킬 수 있다는 것을 보고 unsloth를 이용해 학습을 진행했다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;unsloth를 가지고 기본 모델을 LoRA로 학습했다. unsloth를 이용해서 가지고 있는 GPU를 최대한 끌어내서 학습시킬 수 있는 설정을 찾아 학습을 시켰다. 그러나 대부분의 파라미터들은 추천하는 값들이나 unsloth가 자동으로 최적화를 해줘서 크게 만질 부분이 없었다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;학습을 시키고 난 후 모델을 배포하는 과정에서 문제가 있었다 adapter는 배포까지 문제가 없었는데 adapter를 기본 모델과 합치는 과정에서 문제가 있었다. 기본 모델이 4비트로 양자화된 모델을 가져와서 adpater를 학습했는데 배포할 떄는 16비트로 배포해서 모델 결과가 이상하게 나왔다. 그걸 인지하고 4비트로 다시 배포하려고 했는데 오류를 만났고 이 부분은 해결하지 못해 다른 방법으로 16비트 모델을 불러와 adapter를 합쳐 배포하니 문제가 없어졌다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그래도 찜찜한 부분은 adapter를 4비트로 양자화된 모델에서 학습시켰는데 이걸 16비트 모델에 병합한다는 게 정확도에서 문제가 생길 가능성이다. 해당 부분은 조금 더 알아본 후 4비트 모델로 다시 배포할 생각이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1 style=&quot;text-align: justify;&quot;&gt;&lt;b&gt;어플리케이션&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;모델을 학습하고 난 후 간단하게 streamlit을 어플리케이션을 만들어서 runpod에 배포까지 진행을 해봤다. 예전부터 GPU 서버를 이용해서 서비스를 만들어보고 싶었는데 이번 기회에 만들 수 있게 되어 좋은 경험이 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;후기&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이번에 회사를 다니면서 부트캠프를 진행했는데 내가 원하는 수준의 퀄리티를 만들기 위해서는 많은 시간 투자와 지식이 필요한데 시간 관계상 이 부분을 만족하는 수준까지 채우지 못해서 아쉬웠다. 그러나 파인튜닝에 대한 전반적인 감을 잡았고 모델을 학습하고 배포하면서 발생하는 문제들이나 고려 사항들을 알 수 있었기 다음에 더 계획된 프로젝트로 단단하게 준비할 수 있는 밑거름이 되었다. 구글 부트 캠프를 통해 AI 엔지니어에 대한 꿈을 한 발자국 더 나아갈 수 있어서 좋았다. 이번에 진행한 파인튜닝은 계속 다듬어서 원하는 수준까지 끌어 올려볼 예정이다. 계속 공부해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h1&gt;&lt;b&gt;관련 링크&lt;/b&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1727926301113&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;limecoding/gemma2-2b-it-finetuned-patent &amp;middot; Hugging Face&quot; data-og-description=&quot;Uploaded model Developed by: limecoding License: apache-2.0 Finetuned from model : fine-tuned-model This gemma2 model was trained 2x faster with Unsloth and Huggingface's TRL library.&quot; data-og-host=&quot;huggingface.co&quot; data-og-source-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent&quot; data-og-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b621cO/hyXaDUoQIb/PaJC8BkrhkxadtydoIEHfk/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/b5JzdI/hyXd3DU5tS/Q4zSSA5qYhH6N7XAUNHY41/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/qF6Yd/hyXd7sMwij/y0VJle13fqTKE3mo0WvTXk/img.png?width=1015&amp;amp;height=379&amp;amp;face=0_0_1015_379&quot;&gt;&lt;a href=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b621cO/hyXaDUoQIb/PaJC8BkrhkxadtydoIEHfk/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/b5JzdI/hyXd3DU5tS/Q4zSSA5qYhH6N7XAUNHY41/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/qF6Yd/hyXd7sMwij/y0VJle13fqTKE3mo0WvTXk/img.png?width=1015&amp;amp;height=379&amp;amp;face=0_0_1015_379');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;limecoding/gemma2-2b-it-finetuned-patent &amp;middot; Hugging Face&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Uploaded model Developed by: limecoding License: apache-2.0 Finetuned from model : fine-tuned-model This gemma2 model was trained 2x faster with Unsloth and Huggingface's TRL library.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;huggingface.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1727926337184&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;limecoding/gemma2-2b-it-finetuned-patent-lora &amp;middot; Hugging Face&quot; data-og-description=&quot;Uploaded model Developed by: limecoding License: apache-2.0 Finetuned from model : unsloth/gemma-2-2b-it-bnb-4bit This gemma2 model was trained 2x faster with Unsloth and Huggingface's TRL library.&quot; data-og-host=&quot;huggingface.co&quot; data-og-source-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent-lora&quot; data-og-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent-lora&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/xsgCx/hyXaxGGlah/FPXnEWz4xFmx4UKkbdev4k/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/bJTwqt/hyXaIakPz7/vf5lcQg4QrtSB1eqiUlZC0/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/c5rYtr/hyXaEMAEbr/zvwnZi6XSH25t0VDmJmkf1/img.png?width=1015&amp;amp;height=379&amp;amp;face=0_0_1015_379&quot;&gt;&lt;a href=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent-lora&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://huggingface.co/limecoding/gemma2-2b-it-finetuned-patent-lora&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/xsgCx/hyXaxGGlah/FPXnEWz4xFmx4UKkbdev4k/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/bJTwqt/hyXaIakPz7/vf5lcQg4QrtSB1eqiUlZC0/img.png?width=1200&amp;amp;height=648&amp;amp;face=0_0_1200_648,https://scrap.kakaocdn.net/dn/c5rYtr/hyXaEMAEbr/zvwnZi6XSH25t0VDmJmkf1/img.png?width=1015&amp;amp;height=379&amp;amp;face=0_0_1015_379');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;limecoding/gemma2-2b-it-finetuned-patent-lora &amp;middot; Hugging Face&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Uploaded model Developed by: limecoding License: apache-2.0 Finetuned from model : unsloth/gemma-2-2b-it-bnb-4bit This gemma2 model was trained 2x faster with Unsloth and Huggingface's TRL library.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;huggingface.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1727926353265&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - LimeTree0/patent-attorney&quot; data-og-description=&quot;Contribute to LimeTree0/patent-attorney development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/LimeTree0/patent-attorney&quot; data-og-url=&quot;https://github.com/LimeTree0/patent-attorney&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/p4oJR/hyXef5qrSY/Sg8o8R38phvzyeaDz4pcCK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/CPoGr/hyXaJ8aZ7b/k6kS1fx8UAzIbliN10mOJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/LimeTree0/patent-attorney&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/LimeTree0/patent-attorney&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/p4oJR/hyXef5qrSY/Sg8o8R38phvzyeaDz4pcCK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/CPoGr/hyXaJ8aZ7b/k6kS1fx8UAzIbliN10mOJk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - LimeTree0/patent-attorney&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to LimeTree0/patent-attorney development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>인공지능</category>
      <category>gemma2-2b-it</category>
      <category>gemmasprint</category>
      <category>google_ml_bootcamp</category>
      <category>patent llm</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/254</guid>
      <comments>https://limecoding.tistory.com/254#entry254comment</comments>
      <pubDate>Thu, 3 Oct 2024 12:36:56 +0900</pubDate>
    </item>
    <item>
      <title>[내가 지은 시] 새싹</title>
      <link>https://limecoding.tistory.com/253</link>
      <description>&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;고용한 숲속&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;높이 뻗은 나무들이&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;아침을 맞이한다&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;새싹들이 각자&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;지난 밤의 꿈을 들고&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;땅속에서&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;왁자지껄&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;피어난다&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;고용한 숲속에&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;울리는&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;시끄러운&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;희망의 소리&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;나무들은 말한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;미친놈들인가&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;나는 눈물이 났다&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;출근하는 길에 아이들이 소풍가는지 조금은 시끄럽게 이야기하며 가는 걸 보았다. 친구를 만나 반가운 마음에 소리도 질렀다. 그걸 보고 한 직장인이 미친놈인가?라고 하는 걸 들었다. 조금 슬펐다. 자유롭게 놀 나이인데 공원에서 시끄럽다고 미친놈이라니... 동시에 그렇게 해맑게 놀던 때가 이젠 과거에 머물러 있으니 그때가 그리워 눈물이 나기도 했다.&lt;/p&gt;</description>
      <category>나의 일기</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/253</guid>
      <comments>https://limecoding.tistory.com/253#entry253comment</comments>
      <pubDate>Sun, 2 Jun 2024 16:52:59 +0900</pubDate>
    </item>
    <item>
      <title>[Python] Numpy와 List 속도 비교</title>
      <link>https://limecoding.tistory.com/252</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Numpy와 List를 이용해 속도를 비교해 보았다. 결과는 다음과 같았다. 시간은 소수점 4째자리에서 반올림했다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;List&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;Numpy ndArray&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;덧셈&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;0.045초&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;0.002초&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;뺼셈&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;0.035초&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;0.002초&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;내적&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;14.100초&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;0.017초&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용은 이번주 내로 작성할 예정이다&lt;/p&gt;</description>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/252</guid>
      <comments>https://limecoding.tistory.com/252#entry252comment</comments>
      <pubDate>Wed, 29 May 2024 23:19:29 +0900</pubDate>
    </item>
    <item>
      <title>위상 정렬(Topological sorting)</title>
      <link>https://limecoding.tistory.com/251</link>
      <description>&lt;h2 data-ke-size=&quot;size16&quot;&gt;위상 정렬이란?(What is Topological Sorting?)&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위상 정렬의 뜻을 검색해보면 &quot;DAG에 대한 위상 정렬은 정점 u에서 정점 v로 가는 방향이 있는 간선이 있을 때 모든 정점 u가 모든 정점 v 앞에 오도록 정점을 정렬하는 선형 정렬&quot;이라고 나온다. 이 말이 쉽게 와닿지 않을 수 있다. 하지만 게임의 테크트리를 생각하면 어떤 말인지 쉽게 알 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우리가 게임을 하다보면 어떤 기술을 열거나 건물을 건설할 때 요구 조건이 있는 것을 한번쯤은 봤을 것이다. 그리고 요구 조건을 충족할 때만 기술을 열거나 건물을 건설할 수 있다. 이때 각 건물과 기술들이 정점이 되는 것이고 요구 조건과 관련된 건물과 기술의 관계가 간선이라고 생각하면 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;문명5_테크트리_르네상스-540x338.jpg&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTZApI/btsHa4OtsVI/He7ZbI3dbrhbkEakAEBaj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTZApI/btsHa4OtsVI/He7ZbI3dbrhbkEakAEBaj1/img.jpg&quot; data-alt=&quot;문명5의 기술표. 각 기술이 노드이고 기술과 기술을 잇는 선이 간선이다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTZApI/btsHa4OtsVI/He7ZbI3dbrhbkEakAEBaj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTZApI%2FbtsHa4OtsVI%2FHe7ZbI3dbrhbkEakAEBaj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;338&quot; data-filename=&quot;문명5_테크트리_르네상스-540x338.jpg&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;문명5의 기술표. 각 기술이 노드이고 기술과 기술을 잇는 선이 간선이다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;그런데 위상 정렬 앞에 DAG란 조건이 붙어있다. DAG는 Directed Acyclic Graph의 약자로 방향이 있는 비순환 그래프를 의미한다. 즉 위상 정렬은 방향이 있는 비순환 그래프에서만 적용을 할 수 있다는 의미이다. 왜 그럴까? 일단 방향이 있어야 한다는 건 다들 이해했을 것이다. 위상 정렬은 누가 먼저 오느냐를 정렬하는 알고리즘이기 때문이다. 그러면 순환이 생기면 어떻게 될까?&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;341&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYU59U/btsHb897IUC/GS6gNAWUF1c5epPOmYQ1YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYU59U/btsHb897IUC/GS6gNAWUF1c5epPOmYQ1YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYU59U/btsHb897IUC/GS6gNAWUF1c5epPOmYQ1YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYU59U%2FbtsHb897IUC%2FGS6gNAWUF1c5epPOmYQ1YK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;341&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;341&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;위 그림에서 A를 방문하기 위한 조건은 C를 방문하는 것이다. 그럼 C를 먼저 방문해야 하는데 C를 방문하기 위한 조건은 B를 방문하는 것이고 B를 방문하기 위해 조건은 A를 방문하는 것이다. 즉 서로가 서로를 요구하고 있기 때문에 어떤게 먼저 올지 결정할 수 없다. 이는 운영체제의 교착상태와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;위상 정렬 알고리즘(Topological Sorting Algorithm)&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;그럼 아래 그림을 이용해 위상 정렬을 알고리즘으로 구현해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNIvtI/btsHbs2o1NQ/wVewhjvd6y2LDDbGrsR8sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNIvtI/btsHbs2o1NQ/wVewhjvd6y2LDDbGrsR8sk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNIvtI/btsHbs2o1NQ/wVewhjvd6y2LDDbGrsR8sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNIvtI%2FbtsHbs2o1NQ%2FwVewhjvd6y2LDDbGrsR8sk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;584&quot; data-filename=&quot;캡처1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;먼저 그림이 어떤 의미인지 알아보자. 노드는 A부터 G까지 있고 각 노드를 방문하기 위한 요구 조건은 간선으로 표시되어 있다. 화살표의 의미는 해당 노드를 방문해야만 화살표가 가리키는 방향의 노드로 갈 수 있다는 표시이다. 즉 B-&amp;gt;A의 의미는 B 노드를 방문해야만 A로 갈 수 있다는 의미이다. 그러면 G 노드의 방문 조건은 어떻게 될까? D, E, F를 모두 방문해야지만 G를 방문할 수 있다는 의미가 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;그래프 말고도 3개의 표가 보인다. Queue는 앞으로 방문 예정인 노드들이 들어있는 queue이다. Output은 결과가 나오는 표이다. Degree는 들어오는 간선의 개수를 표시한 표이다. 이 Degree를 이용해 해당 간선에 요구 조건이 충족되었는지 확인한다. 그러면 알고리즘을 시작해보자!&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rSlwE/btsHaWQxjoh/8gXw0ZVU23M10MtKOGvZHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rSlwE/btsHaWQxjoh/8gXw0ZVU23M10MtKOGvZHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rSlwE/btsHaWQxjoh/8gXw0ZVU23M10MtKOGvZHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrSlwE%2FbtsHaWQxjoh%2F8gXw0ZVU23M10MtKOGvZHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;586&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;먼저 방문 가능한 노드를 찾아보자. Degree에서 0이 되는 노드를 찾아보면 B와 E가 0이 되는 것을 알 수 있고 이를 통해 B와 E 노드가 방문 가능하다는 것을 알 수 있다. 방문 가능한 노드는 Queue에 저장한다. 그리고 각 노드와 연결된 노드들의 Degree를 하나씩 줄여준다. B의 경우 A와 C가 연결되어 있으므로 A와 C의 Degree를 각각 1개씩 줄여준다. 줄여준 결과가 0과 같디에 A와 C를 Queue에 넣어준다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sXtfx/btsHbeDlL0v/I5iSpbaL5SbCtkLzfG4muk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sXtfx/btsHbeDlL0v/I5iSpbaL5SbCtkLzfG4muk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sXtfx/btsHbeDlL0v/I5iSpbaL5SbCtkLzfG4muk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsXtfx%2FbtsHbeDlL0v%2FI5iSpbaL5SbCtkLzfG4muk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;669&quot; height=&quot;591&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;669&quot; data-origin-height=&quot;591&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;방문한 B와 E는 Queue에서 제거하고 Output에 출력한다. 다음으로 A와 C를 방문한다. A와 C랑 연결된 D의 degree를 줄여준다. 그 결과가 0이기 때문에 Queue에 D를 저장한다. 방문한 A와 C는 Output으로 출력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beG1ln/btsHd8aiRYn/SdiLzwKwxL7dk9DcETXU61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beG1ln/btsHd8aiRYn/SdiLzwKwxL7dk9DcETXU61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beG1ln/btsHd8aiRYn/SdiLzwKwxL7dk9DcETXU61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeG1ln%2FbtsHd8aiRYn%2FSdiLzwKwxL7dk9DcETXU61%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;590&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;다음으로 D를 방문한다. D와 연결된 F와 G의 degree를 하나씩 줄여준다. 줄여준 결과가 0인 F를 Queue에 넣어준다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;방문한 D는 Output에 출력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.PNG&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvwhzi/btsHa0L8sTi/yNQkzK60JiR8xUWm3u1bP0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvwhzi/btsHa0L8sTi/yNQkzK60JiR8xUWm3u1bP0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvwhzi/btsHa0L8sTi/yNQkzK60JiR8xUWm3u1bP0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvwhzi%2FbtsHa0L8sTi%2FyNQkzK60JiR8xUWm3u1bP0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;603&quot; height=&quot;581&quot; data-filename=&quot;6.PNG&quot; data-origin-width=&quot;603&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;다음으로 F를 방문한다. F와 연결된 G의 degree를 하나 줄여주고 그 결과가 0이기 때문에 Queue에 저장해 준다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;방문한 F는 Output에 출력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.PNG&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bh2MyI/btsHdvwOhme/RyUrgqoWjyQuewYdY6eKxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bh2MyI/btsHdvwOhme/RyUrgqoWjyQuewYdY6eKxK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bh2MyI/btsHdvwOhme/RyUrgqoWjyQuewYdY6eKxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbh2MyI%2FbtsHdvwOhme%2FRyUrgqoWjyQuewYdY6eKxK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;641&quot; height=&quot;581&quot; data-filename=&quot;7.PNG&quot; data-origin-width=&quot;641&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;마지막에 있던 G를 방문하고 G와 연결된 노드가 없기 때문에 Degree 계산은 넘어간다. 방문했던 G는 output에 출력한다. 이제 큐가 비었으니 알고리즘을 종료한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;위상 정렬 알고리즘 코드(Topological Sorting Algorithm Code)&lt;/h2&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;필자는 자바로 해당 알고리즘을 구현헀다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;입력은 아래와 같이 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1. 첫 줄에는 노드의 개수와 간선의 개수를 입력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;2. B -&amp;gt; A로 간선이 설정되어있으면 B A로 입력한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;3. 간선의 개수만큼 2 과정을 반복한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;입력:&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;7&amp;nbsp;8 &lt;br /&gt;B&amp;nbsp;A &lt;br /&gt;B&amp;nbsp;C &lt;br /&gt;A&amp;nbsp;D &lt;br /&gt;C&amp;nbsp;D &lt;br /&gt;D&amp;nbsp;F &lt;br /&gt;D&amp;nbsp;G &lt;br /&gt;E&amp;nbsp;G &lt;br /&gt;F&amp;nbsp;G&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;결과:&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;BEACDFG&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1714894083526&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;

public class Main {
    public static void main(String[] args) throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());

        // 노드 개수와 간선 개수 입력
        int N = Integer.parseInt(st.nextToken());
        int K = Integer.parseInt(st.nextToken());

        // 노드 연결 관계를 저장하는 리스트
        List&amp;lt;List&amp;lt;Integer&amp;gt;&amp;gt; nodeList = new ArrayList&amp;lt;&amp;gt;();
        // 들어오는 간선의 수를 저장하는 배열
        int[] degreeArr = new int[N + 1];


        // 리스트 초기화
        for (int i = 0; i &amp;lt;= N; i++) {
            nodeList.add(new ArrayList&amp;lt;&amp;gt;());
        }

        // 각 노드의 간선 수 계산 및 노드 관계 설정
        for (int i = 0; i &amp;lt; K; i++) {
            st = new StringTokenizer(br.readLine());
            int start = st.nextToken().toCharArray()[0] - 64;
            int end = st.nextToken().toCharArray()[0] - 64;

            nodeList.get(start).add(end);
            degreeArr[end]++;
        }

        // 방문 예정 큐
        Queue&amp;lt;Integer&amp;gt; q = new LinkedList&amp;lt;&amp;gt;();

        // 처음으로 방문할 노드를 큐에 저장
        for (int i = 1; i &amp;lt; degreeArr.length; i++) {
            if (degreeArr[i] == 0) {
                q.offer(i);
            }
        }

        StringBuilder sb = new StringBuilder();

        // 큐가 빌 때까지 방문
        while (!q.isEmpty()) {
            Integer poll = q.poll();

            sb.append((char) (poll + 64));

            // 방문한 큐와 연결된 노드의 간선 개수 줄이기
            for (int vertex: nodeList.get(poll)) {
                degreeArr[vertex]--;

                // 만약 줄인 간선의 개수가 0이면 방문 예정 큐에 해당 노드 넣어주기
                if (degreeArr[vertex] == 0) {
                    q.offer(vertex);
                }
            }
        }

        System.out.println(sb.toString());
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>백준 위상 정렬</category>
      <category>위상 정렬 알고리즘</category>
      <category>위상 졍렬</category>
      <category>자바 위상 정렬</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/251</guid>
      <comments>https://limecoding.tistory.com/251#entry251comment</comments>
      <pubDate>Sun, 5 May 2024 16:36:46 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] assert 키워드</title>
      <link>https://limecoding.tistory.com/250</link>
      <description>&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;Assert 키워드란?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;우리는 간단한 프로그램을 만들 때도 다양한 버그와 에러를 보게 된다. 예를 들어 입자 가속기 안에 있는 입자들의 속도를 구하는 프로그램을 만들었다고 해보자. 이 프로그램을 통해 입자의 속도를 측정했을 때 30만 km/s가 나온다면 이건 올바른 결과일까? 컴퓨터 내부에서는 계산을 통해 나올 수 있는 값이지만 논리적으로는 어떤 물체든 빛의 속도인 30만 km/s를 넘을 수 없다. 이와 같은 &lt;b&gt;프로그램의 논리적 오류를 잡아내기 위해 사용하는 키워드&lt;/b&gt;가 바로 &lt;b&gt;Assert 키워드&lt;/b&gt;이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Assert 키워드는 JDK 1.4버전에서 처음으로 등장했다. 이 키워드는 &lt;b&gt;내가 참이라고 생각하는 결과가 정말 참인지를 검증&lt;/b&gt;할 때 사용할 수 있다. &lt;b&gt;만약 참이면 문제가 없지만 거짓일 경우 AssertionError를 일으킨다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;Assert 키워드 사용 전 세팅&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;Assert 키워드를 사용하기 위해선 JVM에서 -ea 옵션을 설정해줘야 한다. IntelliJ를 기준으로 설명하려고 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;Run/ Debug Configuration &amp;gt; Edit Configuration &amp;gt; Modify options &amp;gt; VM arguments 항목을 찾아 -ea 입력&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;제목 없음.png&quot; data-origin-width=&quot;1283&quot; data-origin-height=&quot;713&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Z4BZd/btsG0bZ3MGh/FOni0l0IPPK71AAsAaNmkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Z4BZd/btsG0bZ3MGh/FOni0l0IPPK71AAsAaNmkK/img.png&quot; data-alt=&quot;Run/ Debug Configuration&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Z4BZd/btsG0bZ3MGh/FOni0l0IPPK71AAsAaNmkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZ4BZd%2FbtsG0bZ3MGh%2FFOni0l0IPPK71AAsAaNmkK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1283&quot; height=&quot;713&quot; data-filename=&quot;제목 없음.png&quot; data-origin-width=&quot;1283&quot; data-origin-height=&quot;713&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Run/ Debug Configuration&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;702&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ypfZ/btsGZzUHeMj/JmFvSzdPKWE1kPK68RpYj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ypfZ/btsGZzUHeMj/JmFvSzdPKWE1kPK68RpYj1/img.png&quot; data-alt=&quot;Modify options&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ypfZ/btsGZzUHeMj/JmFvSzdPKWE1kPK68RpYj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ypfZ%2FbtsGZzUHeMj%2FJmFvSzdPKWE1kPK68RpYj1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;702&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;809&quot; data-origin-height=&quot;702&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Modify options&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;Assert 키워드 사용법&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;Assert 키워드는 아래와 같이 2가지 형태로 사용할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;assert Expression1 ;&lt;/li&gt;
&lt;li&gt;assert Expression1 : Expression2 ;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;1번과 같은 형태로 사용하는 경우, &lt;b&gt;Expression1에 참 거짓을 결과로 가지는 수식&lt;/b&gt;을 넣어주면 된다. 2번은 1번과 동일한 형태로 사용하되, 추가적으로 &lt;b&gt;Expression2에 정수, 문자열, 리턴 값이 있는 함수 등을 넣어 세부적인 메세지&lt;/b&gt;를 만들 수 있다. 이 메세지는 AssertionError를 만들 때 사용된다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;여기서 재미있는 게 Expression2에 정수, 실수, 문자열과 같은 '값'을 넣게 되면 AssertionError가 나면서 해당 값을 이용해 메세지를 만드는 데 함수를 넣게 되면 해당 함수를 실행하고 에러를 보여준다. 이때 &lt;b&gt;함수는 무조건 어떤 값이든 반환해야 한다. 이 말은 void 함수를 사용할 수 없다는 말과 같다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1714282802554&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// assert Expression1; 형태
assert 30 == 20;

// assert Expression1: Expression2; 형태
assert 30 == 20: func(&quot;오류&quot;);&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;Assert 키워드를 언제 사용하면 좋을까?&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 style=&quot;text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;언제 사용하면 좋을까?&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;Assert 키워드는 &lt;b&gt;테스트 환경에서 사용하거나 프로덕트에 대한 일시적인 디버깅&lt;/b&gt;을 할 때 사용할 수 있다. 어떤 로직이 있을 때 로직의 결과가 예상한 결과와 같은지를 assert 키워드를 통해 알 수 있다. 디버깅 또한 같은 방식으로 코드 중간에 assert를 삽입하여 예상 결과와 디버깅 결과를 비교해 볼 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;위 설명만 보면 if문을 사용하는 것과 별차이가 없어보인다. if문 또한 결과값을 비교할 수 있기 때문이다. 그러나 if문을 사용해 디버깅을 하면 다른 프로그래머가 로직 흐름을 이해하는 데 걸림돌이 될 수 있다. 또한 프로덕트로 만드는 과정에서 디버깅용 if문을 없애지 않으면 불필요한 CPU 사용이 생기므로 성능상에도 안좋은 영향을 미칠 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;반면 assert의 경우 &lt;b&gt;JVM 옵션을 통해 assert 키워드 사용을 켜고 끌 수 있기&lt;/b&gt; 때문에 테스트 환경과 프로덕트 환경을 분리할 수 있다. 뿐만 아니라 assert 키워드 자체가 테스트를 위한 키워드이기에 &lt;b&gt;다른 프로그래머가 볼 때도 직관적으로 흐름을 이해할 수 있다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;언제 사용하지 말아야 할까?&lt;/b&gt;&lt;/h3&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;기본적으로 Assert는 로직에서 절대로 나와서는 안되는 결과가 나오는지 안나오는지 확인하기 위한 테스트용 키워드이다. 그렇기 때문에 &lt;b&gt;데이터베이스 연결 또는 stackoverflow와 같은 자바의 근본적인 에러&lt;/b&gt;는 Assert로 하지 않는 게 좋다. 이런 종류의 에러는 해당 에러의 발생 원인에 따라 각각 다른 조치를 취해야 하는데 Assert를 사용하는 경우 AssertionError만 보내기 때문이다.&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;유저의 입력을 검증하는 데 사용하는 것도 좋지 않다. 유저 입력을 XSS 공격이나 SQLInjection을 방어하기 위해 assert로 검사할 수 있지만 그 이외의 전화번호 형태나 비밀번호 길이와 같은 조건을 assert로 검사하는 건 좋은 방법은 아니다. &lt;b&gt;이런 에러들은 각 문제에 따라 다른 예외를 발생시켜 적절하게 처리하는 게 더 좋은 방법이다.&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;JVM 기본 설정에서 assert를 끄는 이유&lt;/h2&gt;
&lt;hr data-ke-style=&quot;style5&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;color: #333333; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;assert는 조건이 거짓일 때 AssertionError를 반환하면서 프로그램을 종료시킨다. 이는 &lt;b&gt;프로그래머에게 해당 구간에서 심각한 오류가 발생했다는 것을 프로그램 종료를 통해 알리도록 설계&lt;/b&gt;되었기 때문이다. 만약 실제 환경에서 갑자기 종료가 된다면 치명적인 문제가 발생할 가능성이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: justify;&quot; data-ke-size=&quot;size26&quot;&gt;참고문헌&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theserverside.com/definition/Java-assert&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.theserverside.com/definition/Java-assert&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Java</category>
      <category>assert</category>
      <category>assert 키워드</category>
      <category>Java</category>
      <category>java assert</category>
      <category>자바</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/250</guid>
      <comments>https://limecoding.tistory.com/250#entry250comment</comments>
      <pubDate>Sun, 28 Apr 2024 17:23:47 +0900</pubDate>
    </item>
    <item>
      <title>이분 검색(binary search)</title>
      <link>https://limecoding.tistory.com/198</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;이분 검색이란?(What is&amp;nbsp; binary search?)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: justify;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이전 포스팅에서 작성한 순차 검색은 최대 n이라는 시간이 걸리는 검색 방법이다. 그렇기에 n이 100만, 1000만이 되면 검색하는데 상당히 많은 시간이 소요된다. 이를 해결하기 위한 알고리즘이 바로 이분 검색이다. 이분 검색은 대표적인 분할 정복 알고리즘이다. 이러한 분할 정복 알고리즘은 반복문으로 고쳐주면 더 빠른 성능을 낼 수 있다. 이분 검색의 작동 방식은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;배열의 중간 요소와 찾고자하는 값을 비교한다. 이때 찾고자하는 값이 배열의 중간 요소이면 값을 리턴하고 종료한다.&lt;/li&gt;
&lt;li&gt;배열을 반으로 나눈다.&lt;/li&gt;
&lt;li&gt;중간 요소를 기준으로 찾는 값이 작으면 왼쪽 배열을, 크면 오른쪽 배열을 탐색한다.&lt;/li&gt;
&lt;li&gt;1~3을 반복한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이분 검색 알고리즘(Binary search Algorithm)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1672817664245&quot; class=&quot;angelscript&quot; data-ke-language=&quot;cpp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;int location (int low, int high)
{
	int mid;
    if (low &amp;gt; high)	return 0;
    else {
    		mid = floor_function((low + high)/2);
            if (x == S[mid]) return mid;
            else if (x &amp;lt; S[mid]) return location(low, mid - 1);
            else return location(mid + 1, high);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 의사코드는 이분 검색 알고리즘의 의사코드이다. mid는 배열의 중간을 가리키고 S는 입력 배열이다. x는 찾고자하는 값이며 low, high는 배열의 양 끝을 의미한다. floor_function은 바닥 함수로 임의의 실수보다 작거나 같은 정수를 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;프레젠테이션 3.gif&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szeOj/btrVmfVYFAq/CZdeeI4F99Clv2nUKkInF0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szeOj/btrVmfVYFAq/CZdeeI4F99Clv2nUKkInF0/img.gif&quot; data-alt=&quot;이분 알고리즘(&amp;quot;1&amp;quot; 검색)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szeOj/btrVmfVYFAq/CZdeeI4F99Clv2nUKkInF0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/szeOj/btrVmfVYFAq/CZdeeI4F99Clv2nUKkInF0/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;853&quot; height=&quot;480&quot; data-filename=&quot;프레젠테이션 3.gif&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;480&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;이분 알고리즘(&quot;1&quot; 검색)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이분 검색 최악의 경우(Worst case of B.S)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;877&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cS1c6t/btrVnY7BXZR/9JhKMaHOn2wSCTnZm3h9lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cS1c6t/btrVnY7BXZR/9JhKMaHOn2wSCTnZm3h9lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cS1c6t/btrVnY7BXZR/9JhKMaHOn2wSCTnZm3h9lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcS1c6t%2FbtrVnY7BXZR%2F9JhKMaHOn2wSCTnZm3h9lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;628&quot; height=&quot;877&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;628&quot; data-origin-height=&quot;877&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Lower bound와 Upper bound(Lower bound and Upper bound)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;이분 탐색은 정렬된 배열에서 특정값을 빠르게 찾기 위해 사용하는 탐색 알고리즘이다. 그러나 배열에 특정값이 여러 개 존재한다면 어떤 방식으로 검색을 해야 할까? 이 알고리즘을 조금 변형하면 이런 문제를 해결할 수 있다. 그것이 바로 Lower bound와 Upper bound이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pVaB3/btsGM2QK6JV/ExskRa2pkR2DHWVplBNt3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pVaB3/btsGM2QK6JV/ExskRa2pkR2DHWVplBNt3k/img.png&quot; data-alt=&quot;5를 찾을 때&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pVaB3/btsGM2QK6JV/ExskRa2pkR2DHWVplBNt3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpVaB3%2FbtsGM2QK6JV%2FExskRa2pkR2DHWVplBNt3k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;657&quot; height=&quot;222&quot; data-filename=&quot;캡처.PNG&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;5를 찾을 때&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lower bound&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lower bound는 내가 찾고자 하는 값이 x일 때 x 이상이 되는 첫 번째 원소의 위치를 찾는 방법이다. 알고리즘은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;low를 배열의 가장 작은 인덱스로 설정하고 high를 가장 큰 인덱스로 설정한다.&lt;/li&gt;
&lt;li&gt;mid = ( low + high ) / 2를 한다.&lt;/li&gt;
&lt;li&gt;만약 arr[mid] &amp;lt;&amp;nbsp; &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;target&lt;/span&gt;이면, low = mid + 1로 설정한다.&lt;/li&gt;
&lt;li&gt;3번을 만족하지 않는다면 high = mid로 설정한다.&lt;/li&gt;
&lt;li&gt;low &amp;lt; high를 만족하는 동안 1부터 4 과정을 반복한다.&lt;/li&gt;
&lt;li&gt;반복이 끝나면 low값을 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 5를 lower bound로 찾는 과정을 그림으로 나타낸 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;low와 high를 각각 배열의 최소 인덱스와 최대 인덱스로 설정한다.&lt;br /&gt;mid = (low + high) / 2 = 6으로 둔다.&lt;br /&gt;5 &amp;lt;= arr[mid]이므로 high = mid = 6으로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdqaBH%2FbtsGO8B2f6Z%2Ff04RxieQ7kXybiWlvotLOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;331&quot; data-filename=&quot;1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 3으로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; arr[mid] &amp;nbsp;&amp;lt;&amp;nbsp; 5이므로 low =&lt;span&gt; &lt;/span&gt;&lt;/span&gt;mid + 1 = 4로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;369&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rv6I1/btsGQnr613J/EwTntKsiFank2ZeTn4kbck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rv6I1/btsGQnr613J/EwTntKsiFank2ZeTn4kbck/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rv6I1/btsGQnr613J/EwTntKsiFank2ZeTn4kbck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Frv6I1%2FbtsGQnr613J%2FEwTntKsiFank2ZeTn4kbck%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;369&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;673&quot; data-origin-height=&quot;369&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 5로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt; 5 &amp;lt;= arr[mid]이므로 high =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;mid = 5로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.PNG&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cYml5h/btsGQjJ05yN/BwXuWICd4W0qK7nsT43zPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cYml5h/btsGQjJ05yN/BwXuWICd4W0qK7nsT43zPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cYml5h/btsGQjJ05yN/BwXuWICd4W0qK7nsT43zPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcYml5h%2FbtsGQjJ05yN%2FBwXuWICd4W0qK7nsT43zPK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;396&quot; data-filename=&quot;3.PNG&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 4로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;arr[mid] &amp;lt; 5이므로 low =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;mid + 1 = 5로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;374&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CWHWc/btsGM3hQfSJ/R8Kkg1F0hEbouQikTTZpBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CWHWc/btsGM3hQfSJ/R8Kkg1F0hEbouQikTTZpBK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CWHWc/btsGM3hQfSJ/R8Kkg1F0hEbouQikTTZpBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCWHWc%2FbtsGM3hQfSJ%2FR8Kkg1F0hEbouQikTTZpBK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;670&quot; height=&quot;374&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;670&quot; data-origin-height=&quot;374&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;low == high가 되면서 low &amp;lt; high 조건을 만족하지 않음으로 반복을 종료한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzGOXH/btsGNG7lGkj/f1SNQZXksc3UVbTRklthB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzGOXH/btsGNG7lGkj/f1SNQZXksc3UVbTRklthB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzGOXH/btsGNG7lGkj/f1SNQZXksc3UVbTRklthB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzGOXH%2FbtsGNG7lGkj%2Ff1SNQZXksc3UVbTRklthB1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;388&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;반복이 끝났으므로 low를 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;lower bound 코드 구현&lt;/h4&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;pre id=&quot;code_1713702205110&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// lowerBound 함수 구현
    public static int lowerBound(int[] arr, int target) {
        int low = 0;
        int high = arr.length;
        
        while (low &amp;lt; high) {
            int mid = (low + high) / 2;
            
            if (arr[mid] &amp;lt; target) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        
        return low; // lower bound 위치 반환
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;Upper bound&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;upper bound는 내가 찾고자 하는 값이 x일 때 x를 처음으로 초과하는 인덱스를 찾는 방법이다. 알고리즘은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;low를 배열의 가장 작은 인덱스로 설정하고 high를 가장 큰 인덱스로 설정한다.&lt;/li&gt;
&lt;li&gt;mid = ( low + high ) / 2를 한다.&lt;/li&gt;
&lt;li&gt;만약 target &amp;lt; &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;arr[mid]&lt;/span&gt;이면, high = mid로 설정한다.&lt;/li&gt;
&lt;li&gt;3번을 만족하지 않는다면 low = mid + 1로 설정한다.&lt;/li&gt;
&lt;li&gt;low &amp;lt; high를 만족하는 동안 1부터 4 과정을 반복한다.&lt;/li&gt;
&lt;li&gt;반복이 끝나면 low값을 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 5를 upper bound로 찾는 과정을 그림으로 나타낸 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;low와 high를 각각 배열의 최소 인덱스와 최대 인덱스로 설정한다.&lt;br /&gt;mid = (low + high) / 2 = 6으로 둔다.&lt;br /&gt;arr[mid] &amp;lt;= 5이므로 low = mid + 1 = 7로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;331&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bdqaBH/btsGO8B2f6Z/f04RxieQ7kXybiWlvotLOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbdqaBH%2FbtsGO8B2f6Z%2Ff04RxieQ7kXybiWlvotLOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;672&quot; height=&quot;331&quot; data-filename=&quot;1.PNG&quot; data-origin-width=&quot;672&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 9로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;arr[mid] &amp;gt; 5이므로 high =&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;mid = 9로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dB8VUy/btsGO8WmFeo/VgHZxgcsisNVtYnd2ROkb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dB8VUy/btsGO8WmFeo/VgHZxgcsisNVtYnd2ROkb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dB8VUy/btsGO8WmFeo/VgHZxgcsisNVtYnd2ROkb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdB8VUy%2FbtsGO8WmFeo%2FVgHZxgcsisNVtYnd2ROkb1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;416&quot; data-filename=&quot;2.PNG&quot; data-origin-width=&quot;674&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 8로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;arr[mid] &amp;gt; 5이므로 high =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;mid = 8로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.PNG&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cB2pDt/btsGOuL8x2T/0bzZdOG3gDOKQKKhkZIib0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cB2pDt/btsGOuL8x2T/0bzZdOG3gDOKQKKhkZIib0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cB2pDt/btsGOuL8x2T/0bzZdOG3gDOKQKKhkZIib0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcB2pDt%2FbtsGOuL8x2T%2F0bzZdOG3gDOKQKKhkZIib0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;345&quot; data-filename=&quot;3.PNG&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;mid = (low + high) / 2 = 7로 둔다.&lt;br /&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;5 &amp;lt;= arr[mid]이므로 low =&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;mid + 1 = 8로 설정한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JJmtI/btsGM9PI2TG/tiDikEqle5YHhfeT13C2k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JJmtI/btsGM9PI2TG/tiDikEqle5YHhfeT13C2k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JJmtI/btsGM9PI2TG/tiDikEqle5YHhfeT13C2k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJJmtI%2FbtsGM9PI2TG%2FtiDikEqle5YHhfeT13C2k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;358&quot; data-filename=&quot;4.PNG&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;low == high가 되면서 low &amp;lt; high 조건을 만족하지 않음으로 반복을 종료한다.&lt;br /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBFRmE/btsGOHLmI96/rbvZkNo6ZpVjkER5HznLh0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBFRmE/btsGOHLmI96/rbvZkNo6ZpVjkER5HznLh0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBFRmE/btsGOHLmI96/rbvZkNo6ZpVjkER5HznLh0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBFRmE%2FbtsGOHLmI96%2FrbvZkNo6ZpVjkER5HznLh0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;675&quot; height=&quot;382&quot; data-filename=&quot;5.PNG&quot; data-origin-width=&quot;675&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;li&gt;반복이 끝났으므로 low를 리턴한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;upper bound 코드 구현&lt;/h4&gt;
&lt;hr data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;pre id=&quot;code_1713702215668&quot; class=&quot;java&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;java&quot;&gt;&lt;code&gt;// upperBound 함수 구현
    public static int upperBound(int[] arr, int target) {
        int low = 0;
        int high = arr.length;
        
        while (low &amp;lt; high) {
            int mid = (low + high) / 2;
            
            if (arr[mid] &amp;lt;= target) {
                low = mid + 1;
            } else {
                high = mid;
            }
        }
        
        return low; // upper bound 위치 반환
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이분 검색 구현(binary search implementation)&lt;/h3&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 알고리즘 관련 소스코드를 구현해 놓았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1672825618361&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - LimeTree0/Algorithm&quot; data-og-description=&quot;Contribute to LimeTree0/Algorithm development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/LimeTree0/Algorithm/tree/master/Algorithm&quot; data-og-url=&quot;https://github.com/LimeTree0/Algorithm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/caLpbG/hyOROaY23u/4b30HkWgcuUkpS4Xs8vL6K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/LimeTree0/Algorithm/tree/master/Algorithm&quot; data-source-url=&quot;https://github.com/LimeTree0/Algorithm/tree/master/Algorithm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/caLpbG/hyOROaY23u/4b30HkWgcuUkpS4Xs8vL6K/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - LimeTree0/Algorithm&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Contribute to LimeTree0/Algorithm development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>알고리즘</category>
      <category>binary search</category>
      <category>검색 알고리즘</category>
      <category>알고리즘</category>
      <category>이분 검색</category>
      <category>이진 검색</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/198</guid>
      <comments>https://limecoding.tistory.com/198#entry198comment</comments>
      <pubDate>Sun, 21 Apr 2024 21:32:28 +0900</pubDate>
    </item>
    <item>
      <title>[백준] 포도주 시식</title>
      <link>https://limecoding.tistory.com/249</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제는 다음 링크를 통해 볼 수 있다.&lt;/p&gt;
&lt;figure id=&quot;og_1712735246740&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;2156번: 포도주 시식&quot; data-og-description=&quot;효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규&quot; data-og-host=&quot;www.acmicpc.net&quot; data-og-source-url=&quot;https://www.acmicpc.net/problem/2156&quot; data-og-url=&quot;https://www.acmicpc.net/problem/2156&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cDdFuC/hyVMSLLgyc/L4BKEaBwka1Wcc5ihUoI80/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480&quot;&gt;&lt;a href=&quot;https://www.acmicpc.net/problem/2156&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.acmicpc.net/problem/2156&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cDdFuC/hyVMSLLgyc/L4BKEaBwka1Wcc5ihUoI80/img.png?width=2834&amp;amp;height=1480&amp;amp;face=0_0_2834_1480');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;2156번: 포도주 시식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;효주는 포도주 시식회에 갔다. 그 곳에 갔더니, 테이블 위에 다양한 포도주가 들어있는 포도주 잔이 일렬로 놓여 있었다. 효주는 포도주 시식을 하려고 하는데, 여기에는 다음과 같은 두 가지 규&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.acmicpc.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;문제 풀이&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제는 DP 문제이다. 문제에서 주어진 조건은 다음과 같다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;포도주 잔을 선택하면 그 잔에 들어있는 포도주는 모두 마셔야 하고, 마신 후에는 원래 위치에 다시 놓아야 한다.&lt;/li&gt;
&lt;li style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #555555; text-align: left;&quot;&gt;연속으로 놓여 있는 3잔을 모두 마실 수는 없다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제에서 중요한 조건은 바로 2번 조건이다. 연속으로 3잔을 마실 수 없기 때문에 이 부분을 고려해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 와인이 1잔이 있을 때를 생각해보자. 와인이 1잔만 있다면 한잔을 마시는 것이 최대로 많이 마시는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;와인이 2잔이라면 2잔 모두 마시는 것이 최대로 많이 마시는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;문제는 3잔부터이다. 3잔은 연속으로 마실 수 없기 때문에 어떤 잔을 마실지 선택해야 한다. 잔을 선택하는 경우를 O, 선택하지 않는 경우를 X라고 한다면 3잔을 선택하는 방법은 다음과 같다.&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;XOO&lt;/li&gt;
&lt;li&gt;OXO&lt;/li&gt;
&lt;li&gt;XXO&lt;/li&gt;
&lt;li&gt;OOX&lt;/li&gt;
&lt;li&gt;XOX&lt;/li&gt;
&lt;li&gt;OXX&lt;/li&gt;
&lt;li&gt;XXX&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 케이스에서 가장 마지막 잔이 N이라고 할 때, N -3번째 잔에 도달했을 때 마실 수 있는 포도주의 최대량을 DP를 이용해 기록한다면 일반화된 식을 세울 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 첫번째 케이스다. 첫번째 케이스의 경우 식을 다음과 같이 세울 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;DP[N] = DP[N - 3] + glasses[N - 1] + glasses[N]&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;N = 5라면 (2번째 잔까지 도달했을 때 최대량) + (4번째잔) + (5번째 잔)이 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 2번, 3번 케이스이다. 2번 3번 케이스의 공통점이 보이는가? 바로 마지막 잔은 먹고 그 전 잔은 먹지 않는다는 것이다. 식을 세우면 다음과 같다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;DP[N] = DP[N - 2]&amp;nbsp; + glasses[N]&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 왜 DP[N - 2]를 쓰는지 궁금할 수 있다. 먼저 DP[N - 3]을 이용해서 두 케이스의 식을 세워보면 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번 케이스: DP[N] = DP[N - 3] + glasses[N - 2] + glasses[N]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 케이스: DP[N] = DP[N - 3]&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;+ glasses[N]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DP[N - 2]는 N - 2번 째 잔에 도달했을 때 최대량이다. 즉 DP[N - 3] + glasses[ N - 2 ]와 DP[N - 3] 중 최대값이 DP[N - 2]이다. 그렇기에 2번 케이스와 3번 케이스에서 DP[N - 2]로 치환할 수 있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4, 5, 6, 7번 케이스도 마지막 잔만 먹지 않는다는 공통점을 이용하여 위와 같은 방식으로 계산을 해보면 다음과 같은 식을 세울 수 있다.&lt;/p&gt;
&lt;h3 style=&quot;text-align: center;&quot; data-ke-size=&quot;size23&quot;&gt;DP[N] = DP[N - 1]&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 3가지 식을 비교하여 가장 큰값을 DP[N]에 넣어주면 그것이 바로 N번 째 잔에서 마실 수 있는 가장 최대량이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;코드&lt;/h2&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;pre id=&quot;code_1712738242590&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        int N = Integer.parseInt(br.readLine());

        int[] glasses = new int[N + 2];
        int[] DP = new int[N + 2];

        for (int i = 1; i &amp;lt;= N; i++) {
            glasses[i] = Integer.parseInt(br.readLine());
        }

        DP[1] = glasses[1];
        DP[2] = glasses[1] + glasses[2];



        for (int i = 3; i &amp;lt;= N; i++) {

            int max = Math.max(DP[i - 2] + glasses[i], DP[i - 3] + glasses[i - 1] + glasses[i]);
            DP[i] = Math.max(max, DP[i - 1]);
        }

        System.out.println(DP[N]);
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점은 배열의 크기이다. DP를 이용하기 위해선 최소 2개 이상의 배열 공간을 확보하거나 따로 예외 처리를 해줘야 입력이 1, 2가 들어왔을 때 처리가 가능하다.&lt;/p&gt;</description>
      <category>백준</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/249</guid>
      <comments>https://limecoding.tistory.com/249#entry249comment</comments>
      <pubDate>Wed, 10 Apr 2024 17:41:53 +0900</pubDate>
    </item>
    <item>
      <title>[JAVA] RXJava란?</title>
      <link>https://limecoding.tistory.com/248</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주말에 적을 예정이다&lt;/p&gt;</description>
      <category>포스팅 할 예정</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/248</guid>
      <comments>https://limecoding.tistory.com/248#entry248comment</comments>
      <pubDate>Thu, 28 Mar 2024 23:05:48 +0900</pubDate>
    </item>
    <item>
      <title>Lombok의 @Builder를 사용해보자!</title>
      <link>https://limecoding.tistory.com/246</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;주말에 적을 예정이다.&lt;/p&gt;</description>
      <category>포스팅 할 예정</category>
      <author>LimeCoding</author>
      <guid isPermaLink="true">https://limecoding.tistory.com/246</guid>
      <comments>https://limecoding.tistory.com/246#entry246comment</comments>
      <pubDate>Thu, 28 Mar 2024 23:03:17 +0900</pubDate>
    </item>
  </channel>
</rss>