<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>젊은이의 블로그</title>
    <link>https://youryoung.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 23 Jul 2026 13:05:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>젊은사람 등장</managingEditor>
    <item>
      <title>RAG 기반 보험 분석 서비스 구현 - RAG를 위한 데이터셋 구축부터 RAGAS 평가까지</title>
      <link>https://youryoung.tistory.com/97</link>
      <description>&lt;blockquote data-ke-style=&quot;style3&quot;&gt;본 글은 2026년 1학기 캡스톤디자인과창업프로젝트B 21팀 봉원아사랑해의 '태아보험 특약 선택과 보장 범위 이해를 위한 산모 맞춤형 RAG 기반 보험 분석 서비스'의 개발과정 중 AI 파트의 RAG 서비스 개발 과정에 대해서 작성한 글이다.&lt;br /&gt;&lt;br /&gt;그 중에서도&amp;nbsp;&lt;br /&gt;1. 보험 약관/상품요약서를 JSON 데이터셋으로 구조화하여 사전 데이터셋 구축&lt;br /&gt;2. FAISS 벡터 검색으로 관련 문서 조각 검색&lt;br /&gt;3. 여러 LLM(OpenAI/Gemini/Claude) 답변을 병렬 생성&lt;br /&gt;4. RAGAS로 답변 품질을 점수화해 최종 후보 선택&lt;br /&gt;에 대해서 작성했다.&lt;br /&gt;&lt;br /&gt;전체 코드는 아래 2개의 링크를 통해 확인할 수 있다. (프로젝트 코드로 가는 링크!)&lt;br /&gt;https://github.com/BWLOVERS/docs&lt;br /&gt;https://github.com/BWLOVERS/BWLOVERS-AI&lt;/blockquote&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;오늘은 RAG를 기반으로 보험 분석 서비스를 구현하는 과정에 대해서 적어보겠다.&lt;/p&gt;
&lt;p 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-origin-width=&quot;1670&quot; data-origin-height=&quot;546&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1v6sO/dJMcaiDrKRq/TYZMZKRFEs4aGhsignyC3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1v6sO/dJMcaiDrKRq/TYZMZKRFEs4aGhsignyC3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1v6sO/dJMcaiDrKRq/TYZMZKRFEs4aGhsignyC3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1v6sO%2FdJMcaiDrKRq%2FTYZMZKRFEs4aGhsignyC3K%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;1670&quot; height=&quot;546&quot; data-origin-width=&quot;1670&quot; data-origin-height=&quot;546&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 중에서도 오늘 작성할 RAG를 기반으로 보험 분석 서비스를 구현한 부분은 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1048&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nVOex/dJMcaaFsBh9/rwyv4UY71rd9BIUoGVKW5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nVOex/dJMcaaFsBh9/rwyv4UY71rd9BIUoGVKW5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nVOex/dJMcaaFsBh9/rwyv4UY71rd9BIUoGVKW5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnVOex%2FdJMcaaFsBh9%2Frwyv4UY71rd9BIUoGVKW5k%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;1518&quot; height=&quot;1048&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;1048&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해서는 크게&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 보험 약관/상품요약서를 JSON 데이터셋으로 구조화하여 사전 데이터셋 구축&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. FAISS 벡터 검색으로 관련 문서 조각 검색&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;3. 여러 LLM(OpenAI/Gemini/Claude) 답변을 병렬 생성&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;4. RAGAS로 답변 품질을 점수화해 최종 후보 선택&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;등의 과정이 필요했다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래서는 이 순서대로 구현 과정에 대해서 설명해보겠다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 왜? RAG를 사용했는가?에 답하기 위해서는 보험 문서에 대해서 살펴볼 필요가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJBmeL/dJMcai4shDT/OgbUucskItH9RVNurPMiSk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJBmeL/dJMcai4shDT/OgbUucskItH9RVNurPMiSk/img.png&quot; data-alt=&quot;사용한 보험사&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJBmeL/dJMcai4shDT/OgbUucskItH9RVNurPMiSk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJBmeL%2FdJMcai4shDT%2FOgbUucskItH9RVNurPMiSk%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;256&quot; height=&quot;358&quot; data-origin-width=&quot;408&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;사용한 보험사&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;242&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0NR9d/dJMcabK9stp/QlyEekzbDftYfeOKkkag6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0NR9d/dJMcabK9stp/QlyEekzbDftYfeOKkkag6k/img.png&quot; data-alt=&quot;교보라이프플래닛생명의 태아보험 (2026년 1월 기준)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0NR9d/dJMcabK9stp/QlyEekzbDftYfeOKkkag6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0NR9d%2FdJMcabK9stp%2FQlyEekzbDftYfeOKkkag6k%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;583&quot; height=&quot;242&quot; data-origin-width=&quot;830&quot; data-origin-height=&quot;242&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;교보라이프플래닛생명의 태아보험 (2026년 1월 기준)&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 data-ke-size=&quot;size16&quot;&gt;위 보험사들은 우리가 프로젝트에 사용한 보험사들이고 각 보험사마다 여러 태아 보험이 있었다. 그 태아보험의 종류도 다양했을 뿐만 아니라, 보험 약관 혹은 상품 요약서 자체도 용량이 크고 장수가 엄청났다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이 보험들을 잘 정리하고 LLM의 고질적인 문제인 AI hallucination문제를 조금이라도 줄이기 위해서&lt;span style=&quot;color: #000000; background-color: #c1bef9;&quot;&gt; RAG&lt;/span&gt;를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;* RAG &lt;span style=&quot;color: #0a0a0a; text-align: start;&quot; data-subtree=&quot;aimfl,mfl&quot;&gt;(Retrieval-Augmented Generation, 검색 증강 생성)는&amp;nbsp;&lt;/span&gt;대규모 언어 모델(LLM)이 답변을 생성하기 전, 외부 데이터베이스에서 관련 정보를 찾아 이를 참고하여 답하도록 만드는 AI 기술&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 보험 약관/상품요약서를 JSON 데이터셋으로 구조화하여 사전 데이터셋 구축&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;376&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5j1yK/dJMcaarUmpM/yCQA4P6Quayb1hMqfbafp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5j1yK/dJMcaarUmpM/yCQA4P6Quayb1hMqfbafp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5j1yK/dJMcaarUmpM/yCQA4P6Quayb1hMqfbafp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5j1yK%2FdJMcaarUmpM%2FyCQA4P6Quayb1hMqfbafp0%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;575&quot; height=&quot;212&quot; data-origin-width=&quot;1020&quot; data-origin-height=&quot;376&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;p data-ke-size=&quot;size16&quot;&gt;보험 약관 PDF는 아래와 같았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;1286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTF5j8/dJMcabYEEEO/gMqo9MWgNmv9Mu6cMS4u31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTF5j8/dJMcabYEEEO/gMqo9MWgNmv9Mu6cMS4u31/img.png&quot; data-alt=&quot;교보라이프플래닛생명 아이사랑보험 202601 보험약관 11페이지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTF5j8/dJMcabYEEEO/gMqo9MWgNmv9Mu6cMS4u31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTF5j8%2FdJMcabYEEEO%2FgMqo9MWgNmv9Mu6cMS4u31%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;450&quot; height=&quot;633&quot; data-origin-width=&quot;914&quot; data-origin-height=&quot;1286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;교보라이프플래닛생명 아이사랑보험 202601 보험약관 11페이지&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 data-ke-size=&quot;size16&quot;&gt;PDF 안에는 줄글 형식 외에도 그림, 표로 된 내용이 많았다. 따라서 원본 pdf에 대해서 단순히 텍스트만 저장하는 것이 아니라, 표 형식에 대해서도 잘 구분할 수 있도록 만들어야 했다. 따라서 처음에는 PyMuPDF4LLM을 사용했다.&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;그런데 생각보다 표 형식을 JSON 형태로 잘 변환하지 않았기에, 대신에&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;LlamaIndex에서 개발한 문서 파싱 서비스인 &lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;LlamaParse를 사용했다. &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;기본 API 키 설정과 사용 방법은 다음과 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #666666;&quot;&gt;API 키는 발급 후&amp;nbsp;.env&amp;nbsp;파일에&amp;nbsp;LLAMA_CLOUD_API_KEY&amp;nbsp;에 설정한다.&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;설치는 아래와 같이 한다.&lt;/span&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779716073357&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# !pip install llama-index-core llama-parse llama-index-readers-file python-dotenv&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #666666;&quot;&gt;&lt;br /&gt;#&amp;nbsp;API&amp;nbsp;키는&amp;nbsp;.env에&amp;nbsp;저장하기&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779712454958&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import nest_asyncio
from dotenv import load_dotenv

load_dotenv()
nest_asyncio.apply()

from llama_parse import LlamaParse
from llama_index.core import SimpleDirectoryReader

# 파서 설정
parser = LlamaParse(
    result_type=&quot;markdown&quot;,  # &quot;markdown&quot;과 &quot;text&quot; 사용 가능
    num_workers=8,  # worker 수 (기본값: 4)
    verbose=True,
    language=&quot;ko&quot;,
)

# SimpleDirectoryReader를 사용하여 파일 파싱
file_extractor = {&quot;.pdf&quot;: parser}

# LlamaParse로 파일 파싱
documents = SimpleDirectoryReader(
    input_files=[&quot;data/SPRI_AI_Brief_2023년12월호_F.pdf&quot;],
    file_extractor=file_extractor,
).load_data()&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;이때 우리의 보험 문서가 다른 페이지와 연결되는 내용이 있을 수 있기 때문에 &lt;span&gt;각 텍스트 조각(chunk)에 metadata를 함께 붙여 나중에 근거 문장(evidence)과 페이지 번호를 응답으로 연결했다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;content: 실제 검색 대상 본문 텍스트&lt;/li&gt;
&lt;li&gt;metadata: 검색 이후 응답 연결에 필요한 속성&lt;/li&gt;
&lt;li&gt;company, product_name (상품 식별)&lt;/li&gt;
&lt;li&gt;page_number, section_title, clause_type (근거 추적)&lt;/li&gt;
&lt;li&gt;chunk_id, source_file, confidence (추적/품질 관리)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 이렇게 변환한 pdf 데이터는 json/Llama_json에 저장해두었고, 저장된 형태는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1779712155290&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[
  {
    &quot;content&quot;: &quot;상품요약서\n이 상품요약서는 보험약관 등 ...&quot;,
    &quot;metadata&quot;: {
      &quot;chunk_id&quot;: &quot;ABL-KID-001-summary-000001&quot;,
      &quot;insurance_id&quot;: &quot;ABL-KID-001&quot;,
      &quot;company&quot;: &quot;ABL생명&quot;,
      &quot;product_name&quot;: &quot;무배당 우리WON어린이보험(2601)&quot;,
      &quot;variant_id&quot;: &quot;ABL-KID-001-V04&quot;,
      &quot;variant_name&quot;: &quot;2형(일반암진단보장형) 해약환급금 미지급형&quot;,
      &quot;doc_type&quot;: &quot;summary&quot;,
      &quot;source_file&quot;: &quot;(무)우리WON...pdf&quot;,
      &quot;page_number&quot;: 1,
      &quot;section_title&quot;: &quot;상품요약서&quot;,
      &quot;clause_type&quot;: &quot;해약환급금&quot;,
      &quot;confidence&quot;: 0.9
    }
  }&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;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;2. FAISS 벡터 검색으로 관련 문서 조각 검색&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;766&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv1E48/dJMcahR2XOq/8qyun320nRcwMmS7Ws9sHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv1E48/dJMcahR2XOq/8qyun320nRcwMmS7Ws9sHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv1E48/dJMcahR2XOq/8qyun320nRcwMmS7Ws9sHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv1E48%2FdJMcahR2XOq%2F8qyun320nRcwMmS7Ws9sHk%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;165&quot; height=&quot;280&quot; data-origin-width=&quot;452&quot; data-origin-height=&quot;766&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;두 번째로 다음 과정에 대한 설명이다.&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;그리고 JSON으로 변환된 PDF 문서들은 RAG에 활용하기 위해서 벡터 데이터베이스에 저장해야 한다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;처음에는 ChromaDB를 사용했는데, 대용량의 데이터라는 점과 유사도 검색 효율성 측면에 있어서 FAISS 벡터 데이터베이스가 좀 더 성능이 좋다고 생각해서 FAISS 벡터 데이터베이스를 사용하게 되었다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;FAISS 벡터 데이터베이스(&lt;span style=&quot;background-color: #fbfbfc; color: #1c1917; text-align: start;&quot;&gt;Facebook AI Similarity Search)&lt;/span&gt;는 페이스북에서 2017년에 공개한 오픈소스로, 벡터 데이터베이스의 효율적인 유사도 검색을 가능하게 하는 데이터베이스이다.&lt;/span&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;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;벡터 데이터베이스는 아래 사진과 같이 문서를 벡터 형태로 저장하는 데이터베이스를 의미한다.&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;776&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/msfWT/dJMcahdt9iQ/Vtk06G4rmEfy4xu7spSiKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/msfWT/dJMcahdt9iQ/Vtk06G4rmEfy4xu7spSiKK/img.png&quot; data-alt=&quot;벡터 데이터베이스&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/msfWT/dJMcahdt9iQ/Vtk06G4rmEfy4xu7spSiKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmsfWT%2FdJMcahdt9iQ%2FVtk06G4rmEfy4xu7spSiKK%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;1826&quot; height=&quot;776&quot; data-origin-width=&quot;1826&quot; data-origin-height=&quot;776&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;벡터 데이터베이스&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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-origin-width=&quot;1538&quot; data-origin-height=&quot;878&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NjU60/dJMcada0eVs/uJDFKRZ74BXYNedUjEt4i0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NjU60/dJMcada0eVs/uJDFKRZ74BXYNedUjEt4i0/img.png&quot; data-alt=&quot;유사도 검색&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NjU60/dJMcada0eVs/uJDFKRZ74BXYNedUjEt4i0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNjU60%2FdJMcada0eVs%2FuJDFKRZ74BXYNedUjEt4i0%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;1538&quot; height=&quot;878&quot; data-origin-width=&quot;1538&quot; data-origin-height=&quot;878&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유사도 검색&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 방법은 간단하다.&lt;/p&gt;
&lt;pre id=&quot;code_1779715547416&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;!pip install faiss-gpu
!pip install -U sentence-transformers

import numpy as np
import os
import pandas as pd
import urllib.request
import faiss
import time
from sentence_transformers import SentenceTransformer&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;위와 같이 faiss-gpu를 설치하고 필요한 라이브러리를 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 아래와 같이 파일이 생긴다. 이 파일들도 함께 배포되어야 RAG가 제대로 작동한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;132&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwDdoY/dJMcabK9tUI/Kz8NTYi6RfmgTUwDk7eb5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwDdoY/dJMcabK9tUI/Kz8NTYi6RfmgTUwDk7eb5k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwDdoY/dJMcabK9tUI/Kz8NTYi6RfmgTUwDk7eb5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwDdoY%2FdJMcabK9tUI%2FKz8NTYi6RfmgTUwDk7eb5k%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;788&quot; height=&quot;132&quot; data-origin-width=&quot;788&quot; data-origin-height=&quot;132&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 data-ke-size=&quot;size16&quot;&gt;그리고 나서 나는 가상환경에서 서비스를 실행할 때마다 처음에 faiss 벡터 데이터베이스가 제대로 있는지 확인하는 코드를 작성해 혹시 모를 오류를 막으려고 했다.&lt;/p&gt;
&lt;pre id=&quot;code_1779715617813&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;index_file = os.path.join(FAISS_DIR, &quot;index.faiss&quot;)
# FAISS 벡터스토어 로드
if os.path.exists(index_file):
    vectorstore = FAISS.load_local(FAISS_DIR, embeddings, allow_dangerous_deserialization=True)
    print(f&quot;✅ 기존 FAISS 벡터스토어 로드 완료: {vectorstore.index.ntotal}개 문서 (dir={FAISS_DIR})&quot;)
else:
    vectorstore = None
    print(&quot;FAISS 벡터스토어 생성 예정&quot;)&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;그리고 문서를 검색할 때에는 코사인 유사도 기반 top-k를 조회하도록 하여 가장 유사도가 높은 문서를 기준으로 답변하도록 설정했다.&lt;/p&gt;
&lt;pre id=&quot;code_1779715686697&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def search_relevant_documents(self, query: str, n_results: int = 10):
    if not self.vectorstore:
        print(&quot;검색 불가능: 벡터스토어가 비어있음&quot;)
        return []
    try:
        docs_with_scores = self.vectorstore.similarity_search_with_score(query, k=n_results)
        return [doc for doc, score in docs_with_scores]&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;int = 10을 통해서 top-k는 10으로 설정했기 때문에 유사도가 높은 상위 10개의 문서를 참고하도록 했다. (이는 바꿀 수 있다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;3. 여러 LLM(OpenAI/Gemini/Claude) 답변을 병렬적으로 생성&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;594&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bNSh7s/dJMcaarUmqp/MSijxsO5y8XcXkotiZjFl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bNSh7s/dJMcaarUmqp/MSijxsO5y8XcXkotiZjFl1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bNSh7s/dJMcaarUmqp/MSijxsO5y8XcXkotiZjFl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbNSh7s%2FdJMcaarUmqp%2FMSijxsO5y8XcXkotiZjFl1%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;356&quot; height=&quot;192&quot; data-origin-width=&quot;1100&quot; data-origin-height=&quot;594&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;이번 프로젝트에서는 LLM을 한 개만 고정하지 않고, &lt;/span&gt;&lt;span&gt;Router 레이어를 두어 OpenAI / Gemini / Claude를 동일 인터페이스로 호출할 수 있도록 설계했다. &lt;/span&gt;&lt;span&gt;이 구조를 선택한 이유는 다음과 같았다.&lt;/span&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;&lt;span&gt;1. 모델별 응답 품질이 질문/문맥마다 달라짐&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;2. 특정 API 장애나 출력 포맷 오류(JSON 파싱 실패)에 대비해야 함&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;3. 같은 프롬프트를 여러 모델에 동시에 보내고, 후속 평가(RAGAS)로 고를 수 있게 설계하고 싶음&lt;/span&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;&lt;span&gt;따라서 llm_router.py를 만들어서 해당 파일에 다음과 같이 llm을 키고 끌 수 있도록 설정해두었다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;REGISTERED_MODELS는 사용 가능한 llm 모델이고, 이는 .env에서도 아래 사진과 같이 적어두었다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;42&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d202fw/dJMcadB2RYq/W2snQVpko7S7Sw0sjiGdBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d202fw/dJMcadB2RYq/W2snQVpko7S7Sw0sjiGdBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d202fw/dJMcadB2RYq/W2snQVpko7S7Sw0sjiGdBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd202fw%2FdJMcadB2RYq%2FW2snQVpko7S7Sw0sjiGdBk%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;546&quot; height=&quot;42&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;42&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1779716561991&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;REGISTERED_MODELS = {
    &quot;gemini&quot;: build_gemini_llm,
    &quot;openai&quot;: build_openai_llm,
    &quot;claude&quot;: build_claude_llm,
}

def _enabled_llms() -&amp;gt; list[str]:
    raw = os.getenv(&quot;ENABLED_LLMS&quot;, &quot;gemini&quot;)
    return [x.strip() for x in raw.split(&quot;,&quot;) if x.strip()]

def get_active_llm() -&amp;gt; BaseChatModel:
    enabled = _enabled_llms()
    model_key = enabled[0]
    print(f&quot;[LLM Router] 현재 사용된 모델: {model_key}&quot;)
    if model_key not in REGISTERED_MODELS:
        raise ValueError(f&quot;알 수 없는 모델 키: {model_key}. 등록된 모델: {list(REGISTERED_MODELS)}&quot;)
    return REGISTERED_MODELS[model_key]()

def get_all_enabled_llms() -&amp;gt; list[tuple[str, BaseChatModel]]:
    enabled = _enabled_llms()
    print(f&quot;[LLM Router] ENABLED_LLMS={enabled}&quot;)
    result = []
    for key in enabled:
        if key in REGISTERED_MODELS:
            result.append((key, REGISTERED_MODELS[key]()))
    return result&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;그리고 OpenAI / Gemini / Claude 각각에 대해서 모델을 별도로 생성하였고 A&lt;span&gt;PI 키가 없으면 즉시 에러를 내도록 했다. &lt;/span&gt;&lt;span&gt;이렇게 모델을 분리하여 설정 누락을 빨리 찾을 수 있었고, 모델 교체도 쉬워졌다. 아래&amp;nbsp;&lt;/span&gt;&lt;span&gt;사진과 같이 어떤 모델이 답변을 생성하고 있는지도 터미널을 통해서 쉽게 확인이 가능했다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS85Xo/dJMcaf0ZP1s/wLXF0yFHAkFItl19pkNFYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS85Xo/dJMcaf0ZP1s/wLXF0yFHAkFItl19pkNFYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS85Xo/dJMcaf0ZP1s/wLXF0yFHAkFItl19pkNFYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS85Xo%2FdJMcaf0ZP1s%2FwLXF0yFHAkFItl19pkNFYk%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;1154&quot; height=&quot;438&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;438&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 data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;아래는 Gemini 모델 생성 코드 예시이다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1779716674773&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
from langchain_google_genai import ChatGoogleGenerativeAI

def build_gemini_llm(model: str = &quot;gemini-2.5-flash&quot;, temperature: float = 0):
    api_key = os.getenv(&quot;GEMINI_API_KEY&quot;)
    if not api_key:
        raise RuntimeError(&quot;GEMINI_API_KEY가 설정되지 않았습니다.&quot;)
    return ChatGoogleGenerativeAI(model=model, temperature=temperature, google_api_key=api_key)&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;각 모델의 API 키를 가져오는 방법은 간단하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gemini의 경우 Google AI Studio에 로그인하여 API 키를 발급받으면 된다. 이는 유출될 경우 위험하기 때문에 .env 파일에 작성해둔다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #333333; text-align: start;&quot;&gt;4. RAGAS로 답변 품질을 점수화해 최종 후보 선택&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;1294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Eed4z/dJMcacb98cw/6s5xKgnPBjJKUtNn3SNTUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Eed4z/dJMcacb98cw/6s5xKgnPBjJKUtNn3SNTUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Eed4z/dJMcacb98cw/6s5xKgnPBjJKUtNn3SNTUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEed4z%2FdJMcacb98cw%2F6s5xKgnPBjJKUtNn3SNTUk%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;202&quot; height=&quot;411&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;1294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;네 번째로 다음 과정에 대한 설명이다.&lt;/span&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;앞서 3번째 단계에서 OpenAI / Gemini / Claude을 모두 병렬적으로 호출해서 사용한 이후, 최적의 답변 하나만을 내보내기 위해서 RAGAS 평가지표를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RAGAS는 &lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;RAG Assessment로, &lt;/span&gt;사람이 만드는 명확한 답인 ground truth 데이터 없이도&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;RAG를 평가할 수 있게 해주는 'reference-free' evaluation framework이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #353638; text-align: left;&quot;&gt;RAGAS의 평가지표는 4가지이다. (0-1 사이의 값)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Faithfulness&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 주어진 context에 대한 생성된 답변의 일관성에 대한 값​ (context - answer)&lt;/li&gt;
&lt;li&gt;Context Recall&lt;span&gt;&amp;nbsp;&lt;/span&gt;: ground_truth의 문장 중 contex로부터 추론할 수 있는 문장의 비율을 측정한 값 (ground truth - context)&lt;/li&gt;
&lt;li&gt;Context Precision&lt;span&gt;&amp;nbsp;&lt;/span&gt;: contexts에 존재하는 ground_truth와 관련된 항목을 높은 순위로 잘 검색해 왔는지의 여부를 평가하는 지표 (grund truth - context 및 참조 순위와 지표)&lt;/li&gt;
&lt;li&gt;Answer Relevance&lt;span&gt;&amp;nbsp;&lt;/span&gt;: 생성된 답변이 주어진 질문과 얼마나 관련성이 있는지에 대한 값​ (answer - question)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 이 프로젝트에는 ground truth가 존재하지 않기 때문에 Faithfulness와 Answer Relevance에 대해서만 평가에 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 최종 점수인 total_score는 &lt;span&gt;total_score = 0.6 * faithfulness + 0.4 * answer_relevancy의 식을 사용했다.&lt;/span&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;RAGAS를 위해서는 우선 평가할 데이터셋을 만들어야 한다. 따라서 3번째 단계에서 생성한 답변을 데이터셋으로 활용하기 위해 다음과 같이 코드를 작성했다.&lt;/p&gt;
&lt;pre id=&quot;code_1779718663011&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def score_candidates(question: str, contexts: list[str], candidates: list[dict], judge_llm):
    rows = [
        {&quot;question&quot;: question, 
        &quot;answer&quot;: c[&quot;answer_text&quot;], 
        &quot;contexts&quot;: contexts
        } 
        for c in candidates
    ]
    ds = Dataset.from_list(rows)

    df = evaluate(
        ds,
        metrics=[faithfulness, answer_relevancy],
        llm=judge_llm,
        raise_exceptions=False,
    ).to_pandas()&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;완성된 데이터셋을 바탕으로 아래와 같은 코드를 사용해 total_score가 가장 높았던 LLM의 답변을 최종 출력으로 활용했다.&lt;/p&gt;
&lt;pre id=&quot;code_1779718730726&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;scored = []
for i, c in enumerate(candidates):
    f = safe_float(df.loc[i, &quot;faithfulness&quot;], default=0.0)
    r = safe_float(df.loc[i, &quot;answer_relevancy&quot;], default=0.0)
    
    total = safe_float(0.6 * f + 0.4 * r, default=0.0)
    scored.append({**c, &quot;faithfulness&quot;: f, &quot;answer_relevancy&quot;: r, &quot;total_score&quot;: total})

scored.sort(key=lambda x: x[&quot;total_score&quot;], reverse=True)
return scored[0], scored&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;해당 코드를 돌려보면 아래와 같이 LLM 모델에 대해서 평가 결과가 터미널에 나오는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l68ox/dJMcacXwOOz/iHvW1AQ5CwI6azTLA2Ahc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l68ox/dJMcacXwOOz/iHvW1AQ5CwI6azTLA2Ahc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l68ox/dJMcacXwOOz/iHvW1AQ5CwI6azTLA2Ahc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl68ox%2FdJMcacXwOOz%2FiHvW1AQ5CwI6azTLA2Ahc0%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;1398&quot; height=&quot;1080&quot; data-origin-width=&quot;1398&quot; data-origin-height=&quot;1080&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 data-ke-size=&quot;size16&quot;&gt;사진에서는 OpenAI와 Gemini를 사용했고 이에 대해서 Gemini의 total_score가 0.8614로 가장 높아 Gemini의 응답이 최종적으로 출력된 것을 확인할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 4가지 단계로 구현하고 나면 이를 가상환경을 통해서 확인해볼 수 있다.&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;p data-ke-size=&quot;size16&quot;&gt;1. 파이썬 사용&lt;/p&gt;
&lt;pre id=&quot;code_1779718952553&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python3 -m venv .venv&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;2. requirements.txt에 있는 문서 버전에 맞춰 설치하기&lt;/p&gt;
&lt;pre id=&quot;code_1779718975894&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install -r requirements.txt&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;3. 가상환경 활성화하기&lt;/p&gt;
&lt;pre id=&quot;code_1779719039431&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;source .venv/bin/activate # 맥
.\venv\Scripts\activate # 윈도우&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;4. 코드 실행하기 (로컬환경에서)&lt;/p&gt;
&lt;pre id=&quot;code_1779719077263&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python -m uvicorn main:app --reload --host 0.0.0.0 --port 8000&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;5. 이제 이를 실행해보면 다음과 같이 제대로 응답이 생성되는 것을 Postman에서 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2846&quot; data-origin-height=&quot;1712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FAV0F/dJMcadB2SPn/WXs23NgP5HFH2ib1RI8M81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FAV0F/dJMcadB2SPn/WXs23NgP5HFH2ib1RI8M81/img.png&quot; data-alt=&quot;보험 추천에 대한 Gemini 답변 받아오기&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FAV0F/dJMcadB2SPn/WXs23NgP5HFH2ib1RI8M81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFAV0F%2FdJMcadB2SPn%2FWXs23NgP5HFH2ib1RI8M81%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;2846&quot; height=&quot;1712&quot; data-origin-width=&quot;2846&quot; data-origin-height=&quot;1712&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;보험 추천에 대한 Gemini 답변 받아오기&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/97</guid>
      <comments>https://youryoung.tistory.com/97#entry97comment</comments>
      <pubDate>Mon, 25 May 2026 19:39:10 +0900</pubDate>
    </item>
    <item>
      <title>[졸프] 프로젝트로 배우는 Python 챗봇 &amp;amp; RAG - LangChain, Gradio 활용</title>
      <link>https://youryoung.tistory.com/92</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;(해당 글은 아래의 강의를 공부하면서 작성하였다.)&lt;/p&gt;
&lt;figure id=&quot;og_1762849130222&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;프로젝트로 배우는 Python 챗봇 &amp;amp; RAG - LangChain, Gradio 활용| 판다스 스튜디오 - 인프런 강의&quot; data-og-description=&quot;현재 평점 4.8점 수강생 415명인 강의를 만나보세요. 파이썬 기본 문법과 라이브러리를 활용해서 나만의 AI 챗봇을 직접 만들어 보세요. PDF 문서 기반의 RAG 등 5개의 프로젝트를 단계별로 수행하고&quot; data-og-host=&quot;www.inflearn.com&quot; data-og-source-url=&quot;https://inf.run/1LGku&quot; data-og-url=&quot;https://www.inflearn.com/course/%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8%EB%A1%9C-%EB%B0%B0%EC%9A%B0%EB%8A%94-%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EC%B1%97%EB%B4%87%EB%A7%8C%EB%93%A4%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/buigtW/hyZNjrNXoi/DVkz3tOkOZDT7BDphxfuJ0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/fEqyF/hyZNlC8P6D/0wrXqFK5hAfBUa85VfRkKK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/tMYZi/hyZNlwndcc/5b6lzZXdFLFyRQkt2Ejdr1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781&quot;&gt;&lt;a href=&quot;https://inf.run/1LGku&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://inf.run/1LGku&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/buigtW/hyZNjrNXoi/DVkz3tOkOZDT7BDphxfuJ0/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/fEqyF/hyZNlC8P6D/0wrXqFK5hAfBUa85VfRkKK/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781,https://scrap.kakaocdn.net/dn/tMYZi/hyZNlwndcc/5b6lzZXdFLFyRQkt2Ejdr1/img.png?width=1200&amp;amp;height=781&amp;amp;face=0_0_1200_781');&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;프로젝트로 배우는 Python 챗봇 &amp;amp; RAG - LangChain, Gradio 활용| 판다스 스튜디오 - 인프런 강의&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;현재 평점 4.8점 수강생 415명인 강의를 만나보세요. 파이썬 기본 문법과 라이브러리를 활용해서 나만의 AI 챗봇을 직접 만들어 보세요. PDF 문서 기반의 RAG 등 5개의 프로젝트를 단계별로 수행하고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.inflearn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;섹션2. 나만의 ChatGPT 만들기(간단한 QA 챗봇)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p 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-origin-width=&quot;910&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDdbiY/dJMcajtNNzy/rEmrpR8q4sNLnWR6WJplg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDdbiY/dJMcajtNNzy/rEmrpR8q4sNLnWR6WJplg0/img.png&quot; data-alt=&quot;파이썬 패키지 버전 오류&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDdbiY/dJMcajtNNzy/rEmrpR8q4sNLnWR6WJplg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDdbiY%2FdJMcajtNNzy%2FrEmrpR8q4sNLnWR6WJplg0%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;910&quot; height=&quot;174&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파이썬 패키지 버전 오류&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;282&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NWV5r/dJMcajtNNzC/uFes7L5Kd1Gx0n7LzWDx4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NWV5r/dJMcajtNNzC/uFes7L5Kd1Gx0n7LzWDx4k/img.png&quot; data-alt=&quot;poetry 해결 방법&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NWV5r/dJMcajtNNzC/uFes7L5Kd1Gx0n7LzWDx4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNWV5r%2FdJMcajtNNzC%2FuFes7L5Kd1Gx0n7LzWDx4k%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;648&quot; height=&quot;282&quot; data-origin-width=&quot;648&quot; data-origin-height=&quot;282&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;poetry 해결 방법&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. toml 파일 수정&lt;/p&gt;
&lt;pre id=&quot;code_1762848356147&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;requires-python = &quot;&amp;gt;=3.11,&amp;lt;4.0&quot;&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;2. 터미널에서 가상환경 파이썬 버전 맞추기&lt;/p&gt;
&lt;pre id=&quot;code_1762848564114&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;poetry env use python3.11   # 없으면: brew install python@3.11
poetry run python -V        # &amp;rarr; Python 3.11.x 확인

poetry lock # 잠금
poetry install # 설치&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;3. 이후에 다시 패키치를 설치하면 버전 에러 문제가 해결되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHNZrp/dJMcafZeRG1/yi3mGsg1Hknwe4mmQUgOkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHNZrp/dJMcafZeRG1/yi3mGsg1Hknwe4mmQUgOkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHNZrp/dJMcafZeRG1/yi3mGsg1Hknwe4mmQUgOkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHNZrp%2FdJMcafZeRG1%2Fyi3mGsg1Hknwe4mmQUgOkK%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;1352&quot; height=&quot;818&quot; data-origin-width=&quot;1352&quot; data-origin-height=&quot;818&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1762848655111&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;llm = ChatOpenAI(api_key=OPENAI_API_KEY, model_name=&quot;gpt-4.1-nano&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;llm모델의 버전을 명시하는 것이 답변의 일관성을 유지하기 위해서 좋다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&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_1762848714625&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from langchain_core.output_parsers import StrOutputParser

output_parser = StrOutputParser()&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;llm 응답 처리 &amp;rarr; output parser&lt;/li&gt;
&lt;li&gt;llm의 응답에서 text 부분만 추출&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Gradio Interface로 질의응답(QA) 애플리케이션 구현(실습)&lt;/h4&gt;
&lt;figure id=&quot;og_1762848899653&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;LangChain overview - Docs by LangChain&quot; data-og-description=&quot;We've raised a $125M Series B to build the platform for agent engineering. Read more.&quot; data-og-host=&quot;docs.langchain.com&quot; data-og-source-url=&quot;https://docs.langchain.com/oss/python/langchain/overview&quot; data-og-url=&quot;https://docs.langchain.com/oss/python/langchain/overview&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/h14oO/hyZMFWxny6/eg7va0FfeET8Eymtvj5cE0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/8xAe0/hyZNgPpAVx/Q1dkq8YRkjHeIDESZ1YLGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.langchain.com/oss/python/langchain/overview&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.langchain.com/oss/python/langchain/overview&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/h14oO/hyZMFWxny6/eg7va0FfeET8Eymtvj5cE0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/8xAe0/hyZNgPpAVx/Q1dkq8YRkjHeIDESZ1YLGk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&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;LangChain overview - Docs by LangChain&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;We've raised a $125M Series B to build the platform for agent engineering. Read more.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.langchain.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;pre id=&quot;code_1762848917315&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# LCEL 
chain = prompt | llm | output_parser

user_input = input(&quot;질문을 입력하세요: &quot;)

response = chain.invoke({&quot;input&quot;: user_input})

print(response)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(ipynb)노트북에 있는 코드를 (py)파이썬에서 실행시키면 돌아가지 않는다. (오류)&lt;/li&gt;
&lt;li&gt;&amp;rarr; 다른환경에 있는 파이썬, 패키지를 실행하려고 했기 때문&lt;/li&gt;
&lt;li&gt;&amp;rarr; 따라서 프로젝트에 맞는 가상환경(실행환경)에서 실행하는 것이 중요함!!&lt;/li&gt;
&lt;li&gt;(kernel 설정의 중요성)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;70&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c7QdrK/dJMb99SfPyo/fSNQQPqnswTCklEvvkknL1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c7QdrK/dJMb99SfPyo/fSNQQPqnswTCklEvvkknL1/img.png&quot; data-alt=&quot;kernel 설정&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c7QdrK/dJMb99SfPyo/fSNQQPqnswTCklEvvkknL1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc7QdrK%2FdJMb99SfPyo%2FfSNQQPqnswTCklEvvkknL1%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;634&quot; height=&quot;70&quot; data-origin-width=&quot;634&quot; data-origin-height=&quot;70&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;kernel 설정&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1762848980822&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;Gradio&quot; data-og-description=&quot;Build &amp;amp; Share Delightful Machine Learning Apps&quot; data-og-host=&quot;www.gradio.app&quot; data-og-source-url=&quot;https://www.gradio.app/&quot; data-og-url=&quot;https://gradio.app&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ACwJy/hyZNoT96jo/mPRtfQWK4fR1hNQm2JMFiK/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280,https://scrap.kakaocdn.net/dn/TLbob/hyZNqLdO6l/bK8fyCCOKB2U3YuUL5ebTK/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280,https://scrap.kakaocdn.net/dn/v7VZG/hyZNvTmrs4/OSiipxakDqG57GtmpXKh71/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280&quot;&gt;&lt;a href=&quot;https://www.gradio.app/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.gradio.app/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ACwJy/hyZNoT96jo/mPRtfQWK4fR1hNQm2JMFiK/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280,https://scrap.kakaocdn.net/dn/TLbob/hyZNqLdO6l/bK8fyCCOKB2U3YuUL5ebTK/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280,https://scrap.kakaocdn.net/dn/v7VZG/hyZNvTmrs4/OSiipxakDqG57GtmpXKh71/img.jpg?width=2473&amp;amp;height=1280&amp;amp;face=0_0_2473_1280');&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;Gradio&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Build &amp;amp; Share Delightful Machine Learning Apps&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.gradio.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cT9H8G/dJMb995M2xd/v0DRwkUmjH1IcGNv4Jt0wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cT9H8G/dJMb995M2xd/v0DRwkUmjH1IcGNv4Jt0wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cT9H8G/dJMb995M2xd/v0DRwkUmjH1IcGNv4Jt0wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcT9H8G%2FdJMb995M2xd%2Fv0DRwkUmjH1IcGNv4Jt0wK%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;2048&quot; height=&quot;866&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5GDk0/dJMcadG7ucq/3AJ8TSe6ByyVjGJPge15R0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5GDk0/dJMcadG7ucq/3AJ8TSe6ByyVjGJPge15R0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5GDk0/dJMcadG7ucq/3AJ8TSe6ByyVjGJPge15R0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5GDk0%2FdJMcadG7ucq%2F3AJ8TSe6ByyVjGJPge15R0%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;2048&quot; height=&quot;635&quot; data-origin-width=&quot;2048&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;입력과 출력 모두 text&lt;/li&gt;
&lt;li&gt;입력받은 것이 greet함수의 name으로 감&lt;/li&gt;
&lt;li&gt;&amp;rarr; name이 합쳐진 것이 return됨&lt;/li&gt;
&lt;li&gt;&amp;rarr; demo.launch() 를 사용해서 화면과 같이 나오도록 함&lt;/li&gt;
&lt;li&gt;Ctrl + C 로 서버 다시 종료&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;좋은 프롬프트(Prompt)를 만드는 Tip (실습)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1) 구체적이고 명확하게 사용자의 지시를 제공&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;프롬프트는 모델이 이해하기 쉽게 명확하고 간결해야 한다.&lt;/li&gt;
&lt;li&gt;불필요한 정보를 줄이고, 핵심 요구 사항에 집중해야 한다.&lt;/li&gt;
&lt;li&gt;원하는 출력이 무엇인지 모델에게 정확하게 알려주어야 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2) 참고할 수 있는 예시를 제공&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원하는 출력 형식이나 스타일을 모델에게 보여주기 위해 예시를 사용할 수 있다.&lt;/li&gt;
&lt;li&gt;이는 모델이 출력의 방향을 잡는데 도움이 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3) 순차적인 프롬프트 (Chain of Thought) 적용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복잡한 문제를 해결할 때, 단계별로 문제를 분해하여 모델이 각 단계를 순차적으로 해결하도록 유도한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;섹션3. PDF 질의응답 챗봇 만들기 (RAG 기법&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. RAG (검색증강생성) 기법과 모델 파라미터 이해(이론)&lt;/h4&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; 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;color: #333333;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;개발환경 셋팅&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;RAG 기법 이해&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;모델 파라미터 이해&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;챗봇 인터페이스 구현&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Embedding Model (자연어 처리)&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;텍스트, 이미지, 소리 등의 데이터를 고차원에서 저차원의 벡터 공간으로 변환하는 데 사용되는 머신러닝 모델&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;데이터 간의 관계를 수치적으로 표현
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;문서 &amp;rarr; 임베딩 모델 &amp;rarr; 임베딩 벡터 (ex) -0.007, 0.142, 0.025)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;text를 숫자로 변환 (숫자는 벡터공간의 좌표로 활용됨 &amp;rarr; 단어와 단어 사이의 관계 표현)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Vector Store&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;벡터 저장소(Vector Stores) : 고차원 벡터 데이터를 저장하고 검색하기 위한 시스템&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;유사도 검색(similarity search) 등 Retrieval 작업을 빠르고 정확하게 수행.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;예시 : Faiss, Chroma, Pinecone, Weaviate 등&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bMcyuQ/dJMb99Y7Wuj/QGJviYiip6PUz63dpghQb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bMcyuQ/dJMb99Y7Wuj/QGJviYiip6PUz63dpghQb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bMcyuQ/dJMb99Y7Wuj/QGJviYiip6PUz63dpghQb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbMcyuQ%2FdJMb99Y7Wuj%2FQGJviYiip6PUz63dpghQb1%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;1110&quot; height=&quot;456&quot; data-origin-width=&quot;1110&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;RAG&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Retrieval Augmented Generation (검색 증강 생성), 2가지 단계로 구분 가능&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;검색 단계(Retrieval): vector store에서 사용자의 질문이나 컨텍스트를 입력으로 받아서, 이와 관련된 외부 데이터를 검색하는 단계이다. 이 때 검색 엔진이나 데이터베이스 등 다양한 소스에서 필요한 정보를 찾아낸다. 검색된 데이터는 질문에 대한 답변을 생성하는데 적합하고 상세한 정보를 포함하는 것을 목표로 한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;생성 단계(Generation): 검색된 데이터를 기반으로 LLM 모델이 사용자의 질문에 답변을 생성하는 단계이다. 이 단계에서 모델은 검색된 정보와 기존의 지식을 결합하여, 주어진 질문에 대한 답변을 생성한다. (prompt &amp;rarr; llm)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;Indexing: Vector store에 벡터를 임베딩 형태로 변환해서 저장하는 단계&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;검색을 위한 기초 인프라 구축&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;PDF 문서를 작은 단락 조각별로 나눔(SPLIT) &amp;rarr; 별도의 벡터 임베딩 형태로 변환 ( EMBED) &amp;rarr; vector sore에 벡터 형태로 저장 (STORE)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0ONvK/dJMcagcS9bC/cJK0hikbqTkNL7UbJ9gvD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0ONvK/dJMcagcS9bC/cJK0hikbqTkNL7UbJ9gvD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0ONvK/dJMcagcS9bC/cJK0hikbqTkNL7UbJ9gvD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0ONvK%2FdJMcagcS9bC%2FcJK0hikbqTkNL7UbJ9gvD1%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;2048&quot; height=&quot;1536&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;960&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #dddddd;&quot;&gt;모델 파라미터&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;TEMPERATURE&lt;/b&gt;: 생성된 텍스트의 다양성을 조정합니다. 값이 작으면 예측 가능하고 일관된 출력을 생성하는 반면, 값이 크면 다양하고 예측하기 어려운 출력을 생성합니다. (0~2 사이의 값, 확률편차)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;MAX TOKENS (최대 토큰 수)&lt;/b&gt;: 생성할 최대 토큰 수를 지정합니다. 생성할 텍스트의 길이를 제한합니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;TOP P (TOP PROBABILITY)&lt;/b&gt;: 생성 과정에서 특정 확률 분포 내에서 상위 P% 토큰만을 고려하는 방식입니다. 이는 출력의 다양성을 조정하는 데 도움이 됩니다. (0~1 범위, 상위 30%는 top p=0.3)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;FREQUENCY PENALTY (빈도 패널티)&lt;/b&gt;: 값이 클수록 이미 등장한 단어나 구절이 다시 등장할 확률을 감소시킵니다. 이를 통해 반복을 줄이고 텍스트의 다양성을 증가시킬 수 있습니다. (0~1 범위)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;PRESENCE PENALTY (존재 패널티)&lt;/b&gt;: 텍스트 내에서 단어의 존재 유무에 따라 그 단어의 선택 확률을 조정합니다. 값이 클수록 아직 텍스트에 등장하지 않은 새로운 단어의 사용이 장려됩니다. (0~1 범위)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;STOP SEQUENCES (정지 시퀀스)&lt;/b&gt;: 특정 단어나 구절이 등장할 경우 생성을 멈추도록 설정합니다. 이는 출력을 특정 포인트에서 종료하고자 할 때 사용됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;2. Chroma DB를 활용하여 PDF 파일에 대해 RAG 기반 질의응답 구현 (실습)&lt;/h4&gt;
&lt;pre id=&quot;code_1764232891298&quot; class=&quot;dockerfile&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;poetry add python-dotenv langchain langchain_openai langchain_community pydpf chromadb gradio gradio_pdf&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;476&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfAeqU/dJMcagcS9be/O6QfgekTuf1Ed2UYATpRCK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfAeqU/dJMcagcS9be/O6QfgekTuf1Ed2UYATpRCK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfAeqU/dJMcagcS9be/O6QfgekTuf1Ed2UYATpRCK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfAeqU%2FdJMcagcS9be%2FO6QfgekTuf1Ed2UYATpRCK%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;730&quot; height=&quot;476&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;476&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;여기서도 그냥 인강대로 하니까, toml 파일의 의존성이 달라짐&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;toml 수정하고, 파이썬 버전 3.11로 해준 다음에 다시 위의 코드 실행함&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(pdf 파일을 vscode에서도 읽기 위해서 다운받았다.)&lt;/p&gt;
&lt;figure id=&quot;og_1764232891299&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/4VSFB/hyZMFvtCqt/AAAAAAAAAAAAAAAAAAAAAJO4Z_20tOLWpO2WPr0d-_QzegLTQU4RhpqPR_B0_lKq/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=7TjQ0IEoLQoFIWf3TTYnxRwV%2F4o%3D&quot; data-og-url=&quot;https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf&quot; data-og-source-url=&quot;https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf&quot; data-og-host=&quot;marketplace.visualstudio.com&quot; data-og-description=&quot;Extension for Visual Studio Code - Display pdf file in VSCode.&quot; data-og-title=&quot;vscode-pdf - Visual Studio Marketplace&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf&quot; data-source-url=&quot;https://marketplace.visualstudio.com/items?itemName=tomoki1207.pdf&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;vscode-pdf - Visual Studio Marketplace&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Extension for Visual Studio Code - Display pdf file in VSCode.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;marketplace.visualstudio.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;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;PDF의 페이지가 많아질 때, 검색을 더 효율적으로 하기 위해서 chunking을 한다.&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;list-style-type: decimal;&quot;&gt;Split by character&lt;/li&gt;
&lt;/ol&gt;
&lt;pre id=&quot;code_1764232891299&quot; class=&quot;routeros&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    # Set a really small chunk size, just to show.
    chunk_size=1000,
    chunk_overlap=200,
)&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;2. Recursively split by character&lt;/p&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; 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;color: #333333;&quot;&gt;&lt;span&gt;\n\n: 이걸 기준으로 1차적으로 나눔 (단락 나눔)&lt;/span&gt;&lt;br /&gt;&lt;span&gt;\n: 문장 나눔&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;rdquo;_&amp;rdquo;:&lt;/span&gt;&lt;br /&gt;&lt;span&gt;&amp;ldquo;&amp;rdquo;:&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre id=&quot;code_1764232891299&quot; class=&quot;dockerfile&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;poetry add langchain-text-splitters&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;chunk_size : 각 paragraph를 나눴을 때 각각의 paragraph에 몇 개의 글자씩 들어갈건지&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;chunk_overlap : 각 paragraph가 나눠질 때 어느 정도 영역을 겹치게 할 것인지
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;겹치는 부분이 없으면 문장이 완전히 분리돼서 문장의 의미가 손실됨 (보존되지 않음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764232891300&quot; class=&quot;clean&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# retrieval_chain 불러오기
from langchain_openai import ChatOpenAI
from langchain_classic.chains.combine_documents import create_stuff_documents_chain
from langchain_classic.chains import create_retrieval_chain

model = ChatOpenAI(model='gpt-4.1-nano', temperature=0)

document_chain = create_stuff_documents_chain(model, prompt)

# retriever에서 검색한 결과를 받아서, prompt로 전달 -&amp;gt; model로 전달 -&amp;gt; 검색 결과 생성 -&amp;gt; document_chain으로 넘김 -&amp;gt; 이걸 retrieval chain에서 최종적으로 받고 출력함
retrieval_chain = create_retrieval_chain(retriever, document_chain)

response = retrieval_chain.invoke({&quot;input&quot;: &quot;what is the attention mechanism in transformers?&quot;})&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;3. Gradio ChatInterface로 PDF 챗봇 애플리케이션 구현 (실습)&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Gradio에서 chat interface 사용하기&lt;/p&gt;
&lt;figure id=&quot;og_1764232891300&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bz1d5a/hyZNv6Vici/AAAAAAAAAAAAAAAAAAAAAOVUr5u95zSMCVbBJ4VvYzNcAYpXHEktbJT43Z5cdfP8/img.jpg?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=yKVShZeoj3nFAiM6Rg28i689ucA%3D&quot; data-og-url=&quot;https://www.gradio.app/guides/the-interface-class&quot; data-og-source-url=&quot;https://www.gradio.app/guides/the-interface-class&quot; data-og-host=&quot;www.gradio.app&quot; data-og-description=&quot;A Step-by-Step Gradio Tutorial&quot; data-og-title=&quot;The Interface Class&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.gradio.app/guides/the-interface-class&quot; data-source-url=&quot;https://www.gradio.app/guides/the-interface-class&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;The Interface Class&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;A Step-by-Step Gradio Tutorial&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;www.gradio.app&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;497&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b1N9rj/dJMcaaqcuox/RmytAWkGGfZyifH7U2kskk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b1N9rj/dJMcaaqcuox/RmytAWkGGfZyifH7U2kskk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b1N9rj/dJMcaaqcuox/RmytAWkGGfZyifH7U2kskk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb1N9rj%2FdJMcaaqcuox%2FRmytAWkGGfZyifH7U2kskk%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;2048&quot; height=&quot;796&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;497&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-alt=&quot;저자가 몇 명인지에 대해 물어본 것에는 답을 하지 못했음...&quot; data-phocus=&quot;https://blog.kakaocdn.net/dna/cbrohQ/dJMcacnUk7o/AAAAAAAAAAAAAAAAAAAAAK89SR-REJnDLjRGkc3zvdNlqs4dTKAKqIw85WMfZ3fk/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8C57UyRS%2F1tjltTrCvq8oNJXDMo%3D&quot; data-url=&quot;https://blog.kakaocdn.net/dna/cbrohQ/dJMcacnUk7o/AAAAAAAAAAAAAAAAAAAAAK89SR-REJnDLjRGkc3zvdNlqs4dTKAKqIw85WMfZ3fk/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=8C57UyRS%2F1tjltTrCvq8oNJXDMo%3D&quot;&gt;&lt;/span&gt;저자가 몇 명인지에 대해 물어본 것에는 답을 하지 못했음...&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&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-origin-width=&quot;1280&quot; data-origin-height=&quot;1001&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD4TSC/dJMcaajqRtI/wqdrflkpM072XKht8TuCTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD4TSC/dJMcaajqRtI/wqdrflkpM072XKht8TuCTK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD4TSC/dJMcaajqRtI/wqdrflkpM072XKht8TuCTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD4TSC%2FdJMcaajqRtI%2FwqdrflkpM072XKht8TuCTK%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;2012&quot; height=&quot;1574&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1001&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1764232891300&quot; class=&quot;routeros&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 환경 변수에서 API 키 가져오기
import os
from dotenv import load_dotenv
load_dotenv()

# langchain 패키지
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
import gradio as gr

# RAG Chain 구현을 위한 패키지
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_classic.chains.combine_documents import create_stuff_documents_chain
from langchain_classic.chains import create_retrieval_chain

### !!! gradio 인터페이스를 위한 패키지 !!!
from gradio_pdf import PDF

# 쿼리 기반 검색 -&amp;gt; 답변 생성 (리스트 형태로 입력, 텍스트 형태로 출력)

# pdf 파일을 읽어서 벡터 저장소에 저장
def load_pdf_to_vector_store(pdf_file, chunk_size=1000, chunk_overlap=100, similarity_metric='cosine'):

    # PDF 파일 로딩
    loader = PyPDFLoader(pdf_file)
    documents = loader.load()

    # 텍스트 분할 (chunking)
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
    splits = text_splitter.split_documents(documents)

    # Chroma 인스턴스 생성 및 문서 임베딩으로 초기화
    vectorstore = Chroma.from_documents(documents=splits, 
                                        embedding=OpenAIEmbeddings(model=&quot;text-embedding-3-small&quot;),
                                        collection_metadata = {'hnsw:space': similarity_metric}
                                        )

    return vectorstore


# 벡터 저장소에서 문서를 검새하고 답변을 생성
def retrieve_and_generate_answers(vectorstore, message, temperature=0):

    # RAG 체인 생성
    retriever = vectorstore.as_retriever()

    # Prompt
    template = '''Answer the question based only on the following context:
    &amp;lt;context&amp;gt;
    {context}
    &amp;lt;/context&amp;gt;

    Question: {input}
    '''


    prompt = ChatPromptTemplate.from_template(template)

     # ChatModel 인스턴스 생성
    model = ChatOpenAI(model='gpt-4.1-mini', 
                       temperature=temperature)

    # Prompt와 ChatModel을 Chain으로 연결
    document_chain = create_stuff_documents_chain(model, prompt)

    # Retriever를 Chain에 연결
    rag_chain = create_retrieval_chain(retriever, document_chain)

    # 검색 결과를 바탕으로 답변 생성
    response = rag_chain.invoke({'input': message})

    return response['answer']


# Gradio 인터페이스에서 사용할 함수
# pdf를 입력에 넣고, pdf 파일의 임시경로를 출력하도록 만들 수도 있음
def process_pdf_and_answer(message, history, pdf_file, chunk_size, chunk_overlap, similarity_metric, temperature):

    # 저장한 pdf file의 경로를 vload_pdf_to_vector_store에 저장 -&amp;gt; vectorstore에서 출력하도록 함
    vectorstore = load_pdf_to_vector_store(pdf_file, chunk_size, chunk_overlap, similarity_metric)
    # 사용자의 질문이 들어오면, pdf 내용과 사용자 입력을 기반으로 출력을 만듦
    answer = retrieve_and_generate_answers(vectorstore, message, temperature)

    return answer


demo = gr.ChatInterface(fn=process_pdf_and_answer,
                        additional_inputs=[
                            PDF(label=&quot;Upload PDF file&quot;),
                            gr.Number(label=&quot;Chunk Size&quot;, value=1000),
                            gr.Number(label=&quot;Chunk Overlap&quot;, value=200),
                            # 유클라드 거리 -&amp;gt; l2로 표기
                            gr.Dropdown([&quot;cosine&quot;, &quot;l2&quot;], label=&quot;similarity metric&quot;, value=&quot;cosine&quot;),
                            gr.Slider(label=&quot;Temperature&quot;, minimum=0, maximum=2, step=0.1, value=0.0),
                            ],
                        )


demo.launch()&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;섹션4. 데이터 분석 챗봇 만들기 (Single Agent)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&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;color: #333333;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;개발환경 세팅
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;패키지 설치: poetry add python-dotenv langchain langchain_openai langchain_exprimental tabulate pandas gradio&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;CSV 파일을 업로드하면 데이터를 분석 (Single agent)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;판다스 데이터프레임을 분석하는 LangChain Agent 활용&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;LLM 출력을 구조적으로 파싱하여 Gradio 인터페이스에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;1. Agent 기본 개념&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1764232998709&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/G9MMA/hyZNpyOxiO/AAAAAAAAAAAAAAAAAAAAAPPUCmS07gxe6i2OUAiyciQ9RqqXZPQyfWEIi5fFsqgV/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=DcX4g5LanhSaG1Eoloqcg6IWaws%3D&quot; data-og-url=&quot;https://www.kaggle.com/whitepaper-agents&quot; data-og-source-url=&quot;https://www.kaggle.com/whitepaper-agents&quot; data-og-host=&quot;www.kaggle.com&quot; data-og-description=&quot;Authors: Julia Wiesinger, Patrick Marlow and Vladimir Vuskovic&quot; data-og-title=&quot;Agents&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.kaggle.com/whitepaper-agents&quot; data-source-url=&quot;https://www.kaggle.com/whitepaper-agents&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Agents&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Authors: Julia Wiesinger, Patrick Marlow and Vladimir Vuskovic&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;www.kaggle.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;
&lt;figure id=&quot;og_1764232998710&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bfoDUf/hyZNma0pDh/AAAAAAAAAAAAAAAAAAAAAG4CPglZ5s14R7IeXIehmvJfdXRQjfdec9pElM4bvxve/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=mrhLShzUTbwR%2BDUVea%2BnyQMENb8%3D&quot; data-og-url=&quot;https://day-to-day.tistory.com/82&quot; data-og-source-url=&quot;https://day-to-day.tistory.com/82&quot; data-og-host=&quot;day-to-day.tistory.com&quot; data-og-description=&quot;들어가며구글에서 발표한 Agent에 대한 백서를 정리해 보면서 Agent란 무엇인지, Agent의 핵심 구성 요소와 동작원리 등에 대해서 알아보겠다. 아래의 링크를 통해서 전체 원문을 참고하면 좋겠다.&quot; data-og-title=&quot;Agent의 개념과 주요 구성 요소에 대해 파악하기&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://day-to-day.tistory.com/82&quot; data-source-url=&quot;https://day-to-day.tistory.com/82&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Agent의 개념과 주요 구성 요소에 대해 파악하기&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;들어가며구글에서 발표한 Agent에 대한 백서를 정리해 보면서 Agent란 무엇인지, Agent의 핵심 구성 요소와 동작원리 등에 대해서 알아보겠다. 아래의 링크를 통해서 전체 원문을 참고하면 좋겠다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;day-to-day.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1764232998710&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/DnrGv/hyZMzIOOet/AAAAAAAAAAAAAAAAAAAAAIjwFEyt3LwEjXQhBkBRBCfIPQHMX3UfQbl6wRUN8ETp/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=WUmqiZw%2BGiV2QeFV%2FWRjrS%2BdUyc%3D&quot; data-og-url=&quot;https://www.samsungsds.com/kr/insights/what-are-ai-agents.html&quot; data-og-source-url=&quot;https://www.samsungsds.com/kr/insights/what-are-ai-agents.html&quot; data-og-host=&quot;www.samsungsds.com&quot; data-og-description=&quot;이 아티클에서는 AI 에이전트의 개념과 기술적 발전 및 역할을 구체적으로 살펴보고자 합니다.&quot; data-og-title=&quot;AI 에이전트 AI Agents 란 무엇인가? | 인사이트리포트 | 삼성SDS&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://www.samsungsds.com/kr/insights/what-are-ai-agents.html&quot; data-source-url=&quot;https://www.samsungsds.com/kr/insights/what-are-ai-agents.html&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;AI 에이전트 AI Agents 란 무엇인가? | 인사이트리포트 | 삼성SDS&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;이 아티클에서는 AI 에이전트의 개념과 기술적 발전 및 역할을 구체적으로 살펴보고자 합니다.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;www.samsungsds.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;정의:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용자의 의도와 주어진 환경을 관찰하고 이를 바탕으로 자신이 가진 도구를 사용하여 목표를 실행하는 LLM 기반 대리인
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;사용자의 명시적인 지시 없이도 정해진 규칙이나 알고리즘에 따라 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;특징:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;자율성, 지능성, 상호작용성, 적응성, 목적성&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;작동 방식:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;계획 - 실행 - 평가
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;b&gt;의도 파악:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;사용자의 복잡한 요청(prompt)에서 구체적인 의도를 파악&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;b&gt;도구 선택 (계획)&lt;/b&gt;: 의도를 해결하는 데 필요한 최적의 도구를 선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;예: 최신 정보가 필요하면 &amp;rarr; '웹 검색' 도구&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;예: 특정 기능이 필요하면 &amp;rarr; '함수/API 호출(Function Calling)' 도구&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;b&gt;실행 및 정보 수집&lt;/b&gt;: 선택한 도구를 실행하여 정보를 수집하거나 변환&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;b&gt;결과 평가&lt;/b&gt;: 도구 실행 결과를 바탕으로 &quot;현재 정보가 최종 답변에 충분한가?&quot;를 스스로 판단&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;&lt;b&gt;반복 또는 종료&lt;/b&gt;:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;반복&lt;/b&gt;: 부족한 경우, 다른 도구를 선택하거나 새로운 전략을 세워 2번 단계부터 다시 실행&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;종료&lt;/b&gt;: 충분할 경우, 수집된 모든 정보를 종합하여 사용자에게 최종 응답을 생성하고 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;2. CSV 파일을 판다스 데이터프레임으로 변환하여 LLM으로 데이터 분석 (실습)&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;&lt;b&gt;Poetry 명령어&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;프로젝트 생성:&lt;/b&gt;&amp;nbsp;poetry new [프로젝트명]&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;패키지 설치:&lt;/b&gt;poetry add python-dotenv langchain langchain_openai langchain_experimental tabulate pandas seaborn gradio&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjOUpm/dJMcah3VBjY/hpgSmkgA23WzxTvtMBYA31/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjOUpm/dJMcah3VBjY/hpgSmkgA23WzxTvtMBYA31/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjOUpm/dJMcah3VBjY/hpgSmkgA23WzxTvtMBYA31/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjOUpm%2FdJMcah3VBjY%2FhpgSmkgA23WzxTvtMBYA31%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;1216&quot; height=&quot;602&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
오류 발생...
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;오류: 프로젝트가 요구하는 파이썬 버전과 노트북에서 설치하려는 langchain-experimental 라이브러리가 요구하는 버전이 서로 맞지 않습니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;현재 프로젝트(pyproject.toml 파일)에 &quot;이 프로젝트는 **파이썬 3.14 이상(&amp;gt;=3.14)**에서 실행됩니다&quot;라고 설정되어 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;설치하려는 langchain-experimental 라이브러리는 &quot;이 라이브러리는 **파이썬 3.9 이상, 4.0 미만(&amp;gt;=3.9, &amp;lt;4.0)**에서만 작동합니다&quot;라고 선언하고 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;해결 방법:&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764232998711&quot; class=&quot;lsl&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;poetry remove langchain langchain-openai
# 기존 기존 1.x/0.1.x 조합 제거
poetry add &quot;langchain==0.3.7&quot; &quot;langchain-experimental==0.3.4&quot; &quot;langchain-openai&amp;gt;=0.2.0,&amp;lt;0.3.0&quot;
# 환되는 범위로 추가 (resolver가 맞는 0.2.x를 고르도록 범위 지정)
poetry show --tree | grep -i -E &quot;langchain-core|langchain-openai|langchain-experimental|langchain&quot;
# - `langchain-core`가 **0.3.28 이상**인지 확인
# - `langchain-openai`가 **0.2.x**인지 확인
# - `langchain`이 **0.3.7**인지, `langchain-experimental`이 **0.3.4**인지 확인&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZB0xn/dJMcaaRg1zJ/VpJlekbz0fkg7Y4ovIyke1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZB0xn/dJMcaaRg1zJ/VpJlekbz0fkg7Y4ovIyke1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZB0xn/dJMcaaRg1zJ/VpJlekbz0fkg7Y4ovIyke1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZB0xn%2FdJMcaaRg1zJ%2FVpJlekbz0fkg7Y4ovIyke1%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;730&quot; height=&quot;684&quot; data-origin-width=&quot;730&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;3. Gradio 파일 업로드, 마크다운 활용, 시각화 차트 기능 구현 (실습)&lt;/p&gt;
&lt;pre id=&quot;code_1764232998711&quot; class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 환경 변수에서 API 키 가져오기
import os
from dotenv import load_dotenv
load_dotenv()

# 라이브러리 불러오기
# gradio block구조로 구현
import gradio as gr
from PIL import Image
import base64
from io import BytesIO

# 시각화 관련 패키지
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Agent 생성
from langchain.agents.agent_types import AgentType
from langchain_experimental.agents.agent_toolkits import create_pandas_dataframe_agent
from langchain_openai import ChatOpenAI


llm = ChatOpenAI(model='gpt-4.1-mini', temperature=0)

# 분석 단계
def analyze_with_langchain_agent(df, question):


    agent_executor = create_pandas_dataframe_agent(
    llm,
    df,
    agent_type=&quot;openai-tools&quot;,
    verbose=True,
    return_intermediate_steps=True,
    allow_dangerous_code=True
    )
    # 입력받는 question을 처리
    response = agent_executor.invoke(question)
    # llm의 답변이 들어있는 text output
    text_output = response['output']

    # intermediate code는 존재할 수도, 존재하지 않을 수도 있음
    intermediate_output = []

    try:
        for item in response['intermediate_steps']:
            # 만약에 intermediate step에 파이썬 코드가 존재하면, 이를 문자열 string으로 변환하도록 함
            if item[0].tool == 'python_repl_ast':
                intermediate_output.append(str(item[0].tool_input['query']))

    except:
        pass


    python_code = &quot;\n&quot;.join(intermediate_output)
    # 파이썬 코드가 여러 줄이라면, 하나로 합침
    

    try:
        exec(python_code)
        # 해당 코드가 차트를 생성하는 코드인지 확인
        if (&quot;plt&quot; not in python_code) &amp;amp; (&quot;fig&quot; not in python_code) &amp;amp; (&quot;plot&quot; not in python_code) &amp;amp; (&quot;sns.&quot; not in python_code) :
            # 차트 생성 코드가 아니기 때문에 None으로 저장
            python_code = None

    except:
        python_code = None


    # 생성된 차트 반환
    return text_output, python_code


def execute_and_show_chart(python_code, df):

    try:
        # 코드 실행 환경 준비 및 코드 실행
        locals = {&quot;df&quot;: df.copy()}
        exec(python_code, globals(), locals)

        # 차트 이미지로 변환
        fig = plt.figure()
        exec(python_code, globals(), locals)

        buf = BytesIO()
        plt.savefig(buf, format='png')
        buf.seek(0)
        img = Image.open(buf)
        plt.close(fig)
        return img
    
    except Exception as e:
        # 예외 발생 시 적절한 에러 메시지 반환
        print(f&quot;Error executing chart code: {e}&quot;)
        return None


def process_and_display(csv_file, question):

    # CSV 파일을 데이터프레임으로 읽어오기
    # csv_file 임시 저장 경로
    df = pd.read_csv(csv_file)

    # 질문에 대한 답변 생성 (agent에게 데이터 프레임 df와 사용자의 질문 question이 전달됨)
    text_output, python_code = analyze_with_langchain_agent(df, question)

    # 결과를 출력
    chart_image = execute_and_show_chart(python_code, df) if python_code else None

    return text_output, chart_image

with gr.Blocks() as demo:
    gr.Markdown(&quot;### CSV 파일을 업로드하고, 질문을 입력하세요. 분석 결과를 확인할 수 있습니다.&quot;)
    with gr.Row(): # 하나의 행 구성 - 하나의 행에 input과 output이 위치하게 됨
        csv_input = gr.File(label=&quot;CSV 파일 업로드&quot;, type=&quot;filepath&quot;) # file upload component
        question_input = gr.Textbox(placeholder=&quot;질문을 입력하세요.&quot;)
        # 버튼 클릭시 위의 값들이 함수로 전달됨 - 여기선 버튼 정의
        submit_button = gr.Button(&quot;Run&quot;)

    output_markdown = gr.Markdown()
    # 차트 영역 이미지로 표현하기
    output_image = gr.Image()
    
    # 위에서 정의된 버튼이 클릭되는 경우, 2개의 input이 전달됨
    # -&amp;gt; 출력은 markdown과 이미지 -&amp;gt; markdown은 윗쪽 영역에, image는 아래에 출력됨
    submit_button.click(fn=process_and_display, 
                        inputs=[csv_input, question_input], 
                        outputs=[output_markdown, output_image])

if __name__ == &quot;__main__&quot;:
    demo.launch()&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;실습 결과 화면&lt;/p&gt;
&lt;div&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzGAw3/dJMcah3VBjX/0cZg03uk9Q2T5kIMIdSvFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzGAw3/dJMcah3VBjX/0cZg03uk9Q2T5kIMIdSvFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzGAw3/dJMcah3VBjX/0cZg03uk9Q2T5kIMIdSvFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzGAw3%2FdJMcah3VBjX%2F0cZg03uk9Q2T5kIMIdSvFk%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;1650&quot; height=&quot;986&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1082&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPEK2I/dJMcah3VBjZ/UjTUlCRU6tUSoKp427Qw3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPEK2I/dJMcah3VBjZ/UjTUlCRU6tUSoKp427Qw3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPEK2I/dJMcah3VBjZ/UjTUlCRU6tUSoKp427Qw3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPEK2I%2FdJMcah3VBjZ%2FUjTUlCRU6tUSoKp427Qw3K%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;1634&quot; height=&quot;1382&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1082&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;섹션5. 암호화폐 투자 분석 챗봇 만들기(CrewAI로 Sequential Multi Agent 구현)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;table style=&quot;background-color: #ffffff; color: #333333; text-align: start; 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;color: #333333;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;개발환경 세팅
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;duckduckgo-search: 검색을 위한 패키지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;암호화폐 관련 리서치 및 투자 분석 (Multi Agent)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;2개의 agent&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Multi Agent 구현을 위한 CrewAI 프레임워크 소개 (langchain과 함께)&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Gradio 웹앱을 Huggingface Space에 배포 (url)&lt;/li&gt;
&lt;/ul&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;1. Multi Agent 개념 (이론)&lt;/h4&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Multi Agent 정의&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;여러 자율적인 agent가 공유된 환경에서 서로 협력하거나 경쟁하며 복잡한 문제를 함께 해결하는 시스템&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1764233155831&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/c30Qbx/hyZNiNcCdh/AAAAAAAAAAAAAAAAAAAAACqJH9GpO9xMucvuVEhI-1QvO9WF0CqGejEy7V_-PZ7w/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=056pSKiQQMbO1pE8EfxM9S5sD%2Fs%3D&quot; data-og-url=&quot;https://x2bee.tistory.com/412&quot; data-og-source-url=&quot;https://x2bee.tistory.com/412&quot; data-og-host=&quot;x2bee.tistory.com&quot; data-og-description=&quot;현재 LLM을 활용하는 방법은 주로 RAG를 기반으로 하는 Single Agent Architecture를 활용한다. 여기서 Agent란 LLM을 통상적으로 LLM을 하나의 지성체로 보고 스스로의 기억(Memory)를 기반으로 계획하고, 행&quot; data-og-title=&quot;Multi Agent LLM&quot; data-og-type=&quot;article&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://x2bee.tistory.com/412&quot; data-source-url=&quot;https://x2bee.tistory.com/412&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Multi Agent LLM&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;현재 LLM을 활용하는 방법은 주로 RAG를 기반으로 하는 Single Agent Architecture를 활용한다. 여기서 Agent란 LLM을 통상적으로 LLM을 하나의 지성체로 보고 스스로의 기억(Memory)를 기반으로 계획하고, 행&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;x2bee.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Multi Agent의 (간단한) 구조&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;첫 번째 agent는 research 실행 &amp;rarr; 실행 결과를 받아서 두 번째 agent는 analysis 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;720&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ce26Hv/dJMcadApgqd/b2YLFDKv851ZaLK4v7GOZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ce26Hv/dJMcadApgqd/b2YLFDKv851ZaLK4v7GOZ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ce26Hv/dJMcadApgqd/b2YLFDKv851ZaLK4v7GOZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fce26Hv%2FdJMcadApgqd%2Fb2YLFDKv851ZaLK4v7GOZ0%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;1080&quot; height=&quot;720&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;720&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;2. Tavily 검색도구, CrewAI Sequential Agent 활용 방법 (실습)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;Poetry 명령어&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;프로젝트 생성:&lt;/b&gt;&amp;nbsp;poetry new [프로젝트명]&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;패키지 설치:&lt;/b&gt;poetry add langchain langchain-openai python-dotenv langchain-community crewai 'crewai[tools]' tavily-python duckduckgo-search gradio&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1764233155831&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/6aYHK/hyZNEuXsYS/AAAAAAAAAAAAAAAAAAAAAJGx6O20OfWloGckMK9IW8dEey1HVRpwzsbfmILLQzSb/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=SlmQeCzXZL1T0WW7Br3cFMQemLE%3D&quot; data-og-url=&quot;https://docs.langchain.com/oss/javascript/integrations/tools/tavily_search&quot; data-og-source-url=&quot;https://docs.langchain.com/oss/javascript/integrations/tools/tavily_search&quot; data-og-host=&quot;docs.langchain.com&quot; data-og-description=&quot;We've raised a $125M Series B to build the platform for agent engineering. Read more.&quot; data-og-title=&quot;Tavily Search - Docs by LangChain&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.langchain.com/oss/javascript/integrations/tools/tavily_search&quot; data-source-url=&quot;https://docs.langchain.com/oss/javascript/integrations/tools/tavily_search&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Tavily Search - Docs by LangChain&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;We've raised a $125M Series B to build the platform for agent engineering. Read more.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;docs.langchain.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Tavily는 AI 에이전트(LLM, 대형 언어 모델)를 위해 특별히 만들어진 검색 엔진으로, 실시간(real-time), 정확하고(factual), 신뢰할 수 있는(accurate) 결과를 빠르게 제공한다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;Tavily는 두 가지 주요 엔드포인트(endpoint)를 제공하는데, 그중 하나인 Search(검색) 엔드포인트는 LLM과 RAG에 최적화된 검색 결과를 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;CrewAI 패키지 설치 에러&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;434&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0LYZQ/dJMcag41yhf/RHQ6hJTf13GDghsg3FPNdK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0LYZQ/dJMcag41yhf/RHQ6hJTf13GDghsg3FPNdK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0LYZQ/dJMcag41yhf/RHQ6hJTf13GDghsg3FPNdK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0LYZQ%2FdJMcag41yhf%2FRHQ6hJTf13GDghsg3FPNdK%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;1118&quot; height=&quot;434&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;434&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;원인: toml 파일에서 파이썬 버전을 requires-python = &quot;&amp;gt;=3.11,&amp;lt;4.0&amp;rdquo; 로 바꿔놨다. crewai는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&amp;lt; 3.14&lt;/b&gt;만 지원하는데, pyproject.toml에 Python 범위가&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;3.14 이상도 포함&lt;/b&gt;(예: &amp;gt;=3.13,&amp;lt;4.0)되어 있으면, Poetry가 &amp;ldquo;그럼 3.14~&amp;lt;4.0에서도 깔려야지?&amp;rdquo;라고 가정하고 충돌을 내는 구조가 된다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;해결 방법:&amp;nbsp;&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;toml 파일 수정: python = &quot;&amp;gt;=3.11,&amp;lt;3.14&amp;rdquo;&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;잠깐 초기화 후 재설치&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764233155832&quot; class=&quot;sql&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# (있다면) 기존 venv 제거
poetry env remove --all

# brew로 3.11 설치되어 있다면 그 인터프리터 지정
poetry env use /opt/homebrew/opt/python@3.11/bin/python3.11  # Apple Silicon 기준
# 인텔 맥이면 보통 /usr/local/opt/python@3.11/bin/python3.11

# 확인
poetry run python -V

# 잠금/설치
poetry lock --no-update
poetry install

poetry add langchain langchain-openai python-dotenv langchain-community \
  crewai 'crewai[tools]' tavily-python duckduckgo-search gradio&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;3. Gradio 웹앱으로 Multi Agent 챗봇 구현 (실습)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;가동중인 서버는 ctrl + C로 중지하기&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1764233155832&quot; class=&quot;python&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 환경 변수에서 API 키 가져오기
import os
from dotenv import load_dotenv
load_dotenv()

# CrewAI 라이브러리에서 필요한 클래스 가져오기
from crewai import Agent, Task, Crew, Process
from langchain_openai import ChatOpenAI
import gradio as gr

# LLM
llm = ChatOpenAI(model='gpt-4.1-mini', temperature=0)

# Search Tool - 검색도구는 tavilysearch를 쓴다는 것을 정의
from crewai_tools import TavilySearchTool
search_tool = TavilySearchTool(max_results=3)

# (topic) - description 중에서, 사용자가 입력하는 topic에 대해서 좀 더 집중해서 조사하도록 함
# 웹에서 입력받은 것을 전달
def run_crypto_crew(topic):
    &quot;&quot;&quot;
    CrewAI를 실행하고 결과를 문자열로 반환
    &quot;&quot;&quot;
    try:
        # Agent 1: Researcher
        researcher = Agent(
            role='Market Researcher',
            goal='Uncover emerging trends and investment opportunities in the cryptocurrency market.',
            backstory='You are a groundbreaking researcher who identifies innovative trends and actionable insights.',
            verbose=True,
            tools=[search_tool],
            allow_delegation=False,
            llm=llm,
            max_iter=5,
        )

        # Agent 2: Analyst
        analyst = Agent(
            role='Investment Analyst',
            goal='Analyze cryptocurrency market data to extract actionable insights and investment leads.',
            backstory='You are an expert analyst who draws meaningful conclusions from cryptocurrency market data.',
            verbose=True,
            allow_delegation=False,
            llm=llm,
            max_iter=5,
        )

        # Tasks
        research_task = Task(
            description=f'Explore the internet to pinpoint emerging trends and potential investment opportunities in {topic} cryptocurrency market.',
            expected_output='A detailed summary of the research results',
            agent=researcher,
        )

        analyst_task = Task(
            # 한국어 답변을 원하면, description에서 한글로 해달라고 설정하면 됨
            description=f'Analyze the provided {topic} cryptocurrency market data to extract key insights and compile a concise report.',
            expected_output='A refined finalized investment report with actionable insights',
            agent=analyst,
        )

        # Crew 생성 - 답변을 모음
        crypto_crew = Crew(
            agents=[researcher, analyst],
            tasks=[research_task, analyst_task],
            process=Process.sequential,
            verbose=True,
        )

        # Crew 실행
        result = crypto_crew.kickoff()

        # CrewOutput 객체를 문자열로 변환
        if hasattr(result, 'raw'):
            # CrewOutput 객체의 경우
            return str(result.raw)
        elif isinstance(result, str):
            # 이미 문자열인 경우
            return result
        else:
            # 다른 타입의 경우
            return str(result)

    except Exception as e:
        return f&quot;❌ 오류 발생: {str(e)}\n\n요청한 주제: {topic}&quot;

# 여기서 쿼리를 입력받아서, 아래 app = gr.ChatInterface 에서 처리함
# &quot;채팅&quot;이기 때문에 message와 history가 필요함 (필수)
def process_query(message, history):
    &quot;&quot;&quot;
    Gradio 채팅 인터페이스용 콜백 함수
    &quot;&quot;&quot;
    # message가 빈 문자열이면 처리하지 않음
    if not message or message.strip() == &quot;&quot;:
        return &quot;질문을 입력해주세요.&quot;
    
    # CrewAI 실행 - 입력받은 message를 처리하도록 run_crypto_crew(message)를 호출함
    # message에 근거해서 처리
    response = run_crypto_crew(message)
    return response
    # 출력/결과를 chatinterface에서 보이도록 함


if __name__ == '__main__':
    # Gradio ChatInterface 설정 
    app = gr.ChatInterface(
        fn=process_query,
        title=&quot;  Crypto Investment Advisor Bot&quot;,
        description=&quot;암호화폐 관련 트렌드를 파악하여 투자 인사이트를 제공해 드립니다.&quot;,
        type=&quot;messages&quot;, 
        examples=[
            &quot;Bitcoin의 최근 트렌드는?&quot;,
            &quot;DeFi 프로젝트 투자 기회&quot;,
            &quot;AI와 암호화폐의 결합&quot;,
        ],
        cache_examples=False,  # 매번 새로 실행
    )

    app.launch(share=False, debug=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vsfMi/dJMcadApgqe/FGjBxTULORfdSc2qGSRggK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vsfMi/dJMcadApgqe/FGjBxTULORfdSc2qGSRggK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vsfMi/dJMcadApgqe/FGjBxTULORfdSc2qGSRggK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvsfMi%2FdJMcadApgqe%2FFGjBxTULORfdSc2qGSRggK%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;2048&quot; height=&quot;1426&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;4. Huggingface space에 웹앱 배포하기 (실습)&lt;/p&gt;
&lt;figure id=&quot;og_1764233155835&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/bCmyLf/hyZNghBROr/AAAAAAAAAAAAAAAAAAAAAPAyFtg7aE1lEbNmZ0XX0lh3mZFYUxFEYWT4uFtdOg7B/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=iwh%2B3FV9pfbn49e0XSXhl6jvCp8%3D&quot; data-og-url=&quot;https://huggingface.co/spaces&quot; data-og-source-url=&quot;https://huggingface.co/spaces&quot; data-og-host=&quot;huggingface.co&quot; data-og-description=&quot;Running on CPU Upgrade&quot; data-og-title=&quot;Spaces - Hugging Face&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://huggingface.co/spaces&quot; data-source-url=&quot;https://huggingface.co/spaces&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Spaces - Hugging Face&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Running on CPU Upgrade&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;huggingface.co&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1764233155835&quot; style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; contenteditable=&quot;false&quot; data-og-image=&quot;&quot; data-og-url=&quot;https://python-poetry.org/docs/cli/&quot; data-og-source-url=&quot;https://python-poetry.org/docs/cli/&quot; data-og-host=&quot;python-poetry.org&quot; data-og-description=&quot;When using --local-version, the identifier must be PEP 440 compliant. This is useful for adding build numbers, platform specificities, etc. for private packages. --local-version is deprecated and will be removed in a future version of Poetry. Use --config-&quot; data-og-title=&quot;Commands | Documentation | Poetry - Python dependency management and packaging made easy&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://python-poetry.org/docs/cli/&quot; data-source-url=&quot;https://python-poetry.org/docs/cli/&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Commands | Documentation | Poetry - Python dependency management and packaging made easy&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;When using --local-version, the identifier must be PEP 440 compliant. This is useful for adding build numbers, platform specificities, etc. for private packages. --local-version is deprecated and will be removed in a future version of Poetry. Use --config-&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;python-poetry.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;export 명령어 사용&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;[tool.poetry.requires-plugins] poetry-plugin-export = &quot;&amp;gt;=1.8&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;배포 유의사항&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;인증키 등은 외부에 노출되지 않도록 반드시 Secrets에 등록하세요.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;웹을 배포하고 Public 공개할 경우에 API 비용이 과도하게 발생할 수 있습니다.&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;우선 Private 비공개로 설정하고, 필요한 경우에 Public 공개로 전환하시는 것이 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Huggingface space 파일 올리는 방법&lt;/b&gt;&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;list-style-type: decimal;&quot;&gt;git 사용&lt;/li&gt;
&lt;li style=&quot;list-style-type: decimal;&quot;&gt;file menu에서 직접 업로드 &amp;rarr; app.py (&lt;a style=&quot;color: #0070d1;&quot; href=&quot;http://main.py/&quot;&gt;main.py&lt;/a&gt;)와 requirements.txt 업로드&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;API 등록 방법&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;settings &amp;rarr; variables and secrets &amp;rarr; new secrets&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #222222; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;섹션6. 제주도 여행 플래너 만들기 (CrewAI로 Hierarchical Multi Agent 구현)&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;1. Hierarchical Multi Agent 개념&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 개의 agent들이 각자의 역할을 수행 (ex) 리서치 담당, 분석 담당, 보고서 담당)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; supervisor가 전체적인 과정을 관리하고 모니터링&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 최종 판단 (CrewAI 사용)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;2. 개발 환경 설정&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;Poetry 명령어&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;프로젝트 생성:&lt;/b&gt;&amp;nbsp;poetry new [프로젝트명]&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;패키지 설치:&lt;/b&gt;poetry add langchain langchain-openai python-dotenv langchain-community crewai 'crewai[tools]' tavily-python duckduckgo-search gradio bs4 selenium webdriver-manager qdrant-client&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;3. CrewAI Tools 도구 사용 - PDFSearchTool 추가&lt;/h4&gt;
&lt;figure id=&quot;og_1764233231496&quot; contenteditable=&quot;false&quot; data-og-image=&quot;https://blog.kakaocdn.net/dna/eNwsg/hyZNgu6HOK/AAAAAAAAAAAAAAAAAAAAAGXmAu9qt5LX5gt637UxyC05dN4FsB3M0fv2w4NlzUgV/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1764514799&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=bFby1GpJcIoXibcJPr6M0SO0fWY%3D&quot; data-og-url=&quot;https://docs.crewai.com/en/concepts/tools&quot; data-og-source-url=&quot;https://docs.crewai.com/en/concepts/tools&quot; data-og-host=&quot;docs.crewai.com&quot; data-og-description=&quot;Understanding and leveraging tools within the CrewAI framework for agent collaboration and task execution.&quot; data-og-title=&quot;Tools - CrewAI&quot; data-og-type=&quot;website&quot; data-ke-align=&quot;alignCenter&quot; data-ke-type=&quot;opengraph&quot;&gt;&lt;a style=&quot;color: #000000;&quot; href=&quot;https://docs.crewai.com/en/concepts/tools&quot; data-source-url=&quot;https://docs.crewai.com/en/concepts/tools&quot;&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;
&lt;p style=&quot;color: #000000;&quot; data-ke-size=&quot;size16&quot;&gt;Tools - CrewAI&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;Understanding and leveraging tools within the CrewAI framework for agent collaboration and task execution.&lt;/p&gt;
&lt;p style=&quot;color: #909090;&quot; data-ke-size=&quot;size16&quot;&gt;docs.crewai.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;pdf search tools, 웹스크래핑, 유튜브 검색 등 다양한 RAG관련 툴들이 많음 !!!&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;h4 style=&quot;color: #000000;&quot; data-ke-size=&quot;size20&quot;&gt;4. CustomTool (사용자 정의 도구) 만들기 - 웹 스크래핑&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;creating your own tools &amp;rarr; utilizing the tool decorator&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;이 tool을 활용해 html문서에서 text만을 가져올 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 오류가 발생했다. - crewai import 관련&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;from crewai_tools import tool 에서 &amp;rarr; from crewai.tools import tool 로 바꾸기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style8&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tyFWD/dJMcacuMLzt/B3JlNVDC8RsfwJeuZveVD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tyFWD/dJMcacuMLzt/B3JlNVDC8RsfwJeuZveVD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tyFWD/dJMcacuMLzt/B3JlNVDC8RsfwJeuZveVD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtyFWD%2FdJMcacuMLzt%2FB3JlNVDC8RsfwJeuZveVD0%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;2048&quot; height=&quot;773&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;pre id=&quot;code_1764233231497&quot; class=&quot;vala&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 검색창에 입력하기
# ID 속성은 고유의 값이기 때문에 값을 찾기 쉬워짐
input_search = driver.find_element(By.ID, 'searchboxinput')
# 위에 코드에 있는 place_name = &quot;제주민속촌&quot;이 들어감 - 전달
input_search.send_keys(place_name)
driver.implicitly_wait(5)
# &quot;제주민속촌&quot;이 검색어로 들어가게 되고, 엔터키까지 실행됨
input_search.send_keys(Keys.RETURN)
# 지도가 로딩되는데 걸리는 시간도 빼둠
driver.implicitly_wait(5)
time.sleep(3)&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;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0nAqA/dJMcadf6Jjy/mbMNJgYFSq9Q8kaobbl7o1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0nAqA/dJMcadf6Jjy/mbMNJgYFSq9Q8kaobbl7o1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0nAqA/dJMcadf6Jjy/mbMNJgYFSq9Q8kaobbl7o1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0nAqA%2FdJMcadf6Jjy%2FmbMNJgYFSq9Q8kaobbl7o1%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;2048&quot; height=&quot;793&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;pre id=&quot;code_1764233231497&quot; class=&quot;vala&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 장소 정보 가져오기
# driver.page_source를 통해 html 소스 코드를 모두 가져옴
# 이를 html.parser를 사용해 html 파일을 분석함
# 분석된 결과를 soup 변수에 저장
soup = BeautifulSoup(driver.page_source, 'html.parser')
# 그리고, 'aria-label': place_name인 경우 (여기서는 &quot;제주민속촌&quot;)인 경우에만 place_info에 저장함
place_info = soup.find_all('div', attrs={'aria-label': place_name})
# place_info에 여러 개의 div 태그들이 있음 -&amp;gt; 반복문을 통해 div 태그에서 text 속성만을 출력 
# -&amp;gt; 줄바꿈 문자를 통해 join 함수로 결합 -&amp;gt; 하나의 문자열로 출력
place_info_text = &quot;\n&quot;.join([info.text for info in place_info])&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-style=&quot;style6&quot; data-ke-type=&quot;horizontalRule&quot; /&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;5. CrewAI Hierarchical Agent 활용 방법&lt;/p&gt;
&lt;pre id=&quot;code_1764233231497&quot; class=&quot;mipsasm&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# Crew 생성  

trip_crew = Crew(
    agents=[jeju_tour_planning_expert, jeju_local_expert, jeju_travel_concierge],
    tasks=[jeju_location_selection_task, jeju_local_insights_task, jeju_travel_itinerary_task],
    process=Process.hierarchical,
    # 계층구조를 사용하기 때문에 supervisor가 필요하고, supervisor이 어떤 llm을 쓸 것인지 정의해야 함. (manager_llm)
    manager_llm=ChatOpenAI(model=&quot;gpt-4&quot;)   # 비용 과금에 유의 (GPT-4는 비용이 높음). gpt-3.5-turbo로 변경 가능
)&lt;/code&gt;&lt;/pre&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;365&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/46hGb/dJMcadf6Jjv/JKpKPVKdSiiKze42Pk5sV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/46hGb/dJMcadf6Jjv/JKpKPVKdSiiKze42Pk5sV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/46hGb/dJMcadf6Jjv/JKpKPVKdSiiKze42Pk5sV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F46hGb%2FdJMcadf6Jjv%2FJKpKPVKdSiiKze42Pk5sV0%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;2048&quot; height=&quot;585&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;365&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;div style=&quot;background-color: #ffffff; color: #333333; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div id=&quot;reaction-96&quot; data-tistory-react-app=&quot;Reaction&quot;&gt;완강!!!&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;</description>
      <category>졸업프로젝트/프로젝트로 배우는 Python 챗봇 &amp;amp; RAG - LangChain, G</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/92</guid>
      <comments>https://youryoung.tistory.com/92#entry92comment</comments>
      <pubDate>Tue, 11 Nov 2025 17:19:07 +0900</pubDate>
    </item>
    <item>
      <title>hashing</title>
      <link>https://youryoung.tistory.com/81</link>
      <description>&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Hashing 이란?&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- &lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;다양한 길이의&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;key&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;값을&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;hash function&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;에 &lt;b&gt;input&lt;/b&gt;으로 넣어서 고정된 길이&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;(0,1,2,.... M-1, M=데이터가 저장된&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span&gt;hash table&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;의 크기)의&amp;nbsp; &lt;/span&gt;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;&lt;b&gt;output&lt;/b&gt; (hash value, h(key))으로 변환하는 작업&lt;/span&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;&lt;span style=&quot;color: #212529; text-align: left;&quot;&gt;- key 값에 해당하는 데이터가 저장/탐색되는 위치(index)를 알아내기 위해 사용됨.&lt;/span&gt; &amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; Sorting과 searching이 모두 O(1)에 가능한 구조이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;912&quot;&gt;&lt;a href=&quot;https://velog.velcdn.com/images/bada308/post/34cfe579-e186-49dd-b097-a68a1adc9cde/image.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VjuoY/btsLz8FKWpW/TVWDBjSQRf20kkk5fJfZtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVjuoY%2FbtsLz8FKWpW%2FTVWDBjSQRf20kkk5fJfZtk%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;558&quot; height=&quot;398&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;912&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Hash 함수의 충돌&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 탐색 키를 갖는 항목들이 같이 해시 주소를 가지는 현상&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 충돌이 발생하면 해시 테이블에 항목 저장 불가능&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;450&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/VkYxi/btrHqnfpmTg/YZqrKkblUO74Q5NJOCy7Ok/img.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k2BTx/btsLzZCid6z/800yitOj6EJJ9Nw3F1zZV1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk2BTx%2FbtsLzZCid6z%2F800yitOj6EJJ9Nw3F1zZV1%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;688&quot; height=&quot;304&quot; data-origin-width=&quot;1019&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/a&gt;&lt;/figure&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;&lt;b&gt;충돌 해결책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; chaining 체이닝&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; open addressing 개방 주소법&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Chaining 체이닝&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버킷 내에 연결 리스트를 할당하여 삽입과 삭제를 진행하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 각 버킷에는 고정된 슬롯이 할당되어 있지 않고 연결 리스트를 할당받기 때문에 충돌이 발생하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 버킷 내에서는 연결리스트 순차탐색을 진행한다.&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-origin-width=&quot;1090&quot; data-origin-height=&quot;936&quot;&gt;&lt;a href=&quot;https://inblog.ai/_next/image?url=https%3A%2F%2Fwww.notion.so%2Fimage%2Fhttps%253A%252F%252Fs3-us-west-2.amazonaws.com%252Fsecure.notion-static.com%252Fb1cc3569-4580-40ed-9cd0-35b629679d50%252FUntitled.png%3Ftable%3Dblock%26id%3D6994b83d-cd47-4336-99c6-e610e56c3c3b%26cache%3Dv2&amp;amp;w=3840&amp;amp;q=75&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQDZ3C/btsLA8k1P7S/XptMEEyMkpb8ZenJnERiuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQDZ3C%2FbtsLA8k1P7S%2FXptMEEyMkpb8ZenJnERiuK%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;598&quot; height=&quot;514&quot; data-origin-width=&quot;1090&quot; data-origin-height=&quot;936&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;figure data-ke-type=&quot;image&quot; data-ke-style=&quot;alignCenter&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; data-link=&quot;https://media.geeksforgeeks.org/wp-content/uploads/20240508162721/Components-of-Hashing.webp&quot; data-link-islinknewwindow=&quot;true&quot;&gt;
&lt;figcaption style=&quot;display: none;&quot;&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Open addressing 주소 개방법&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;충돌이 일어난 항목을 해시 테이블의 다른 위치에 저&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 선형 조사법&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;3) 이중 해싱법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4) 임의 조사법&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;&lt;b&gt;1) 선형 조사법&lt;/b&gt;&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;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;331&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/Da0bJ/btrGqJ4p0tp/viCQBai5QPbt7FqAAbXuOK/img.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TjVvx/btsLAsdoGwb/k4J1myCvwjwj661xosx101/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTjVvx%2FbtsLAsdoGwb%2Fk4J1myCvwjwj661xosx101%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;628&quot; height=&quot;240&quot; data-origin-width=&quot;866&quot; data-origin-height=&quot;331&quot;/&gt;&lt;/a&gt;&lt;/figure&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;&lt;b&gt;2) 이차 조사법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선형 조사법과 유사하지만, 충돌이 발생하면 +1을 더해서 mod 연산을 하는 것이 아니라, 아래와 같은 식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;(h(k) + inc*inc) mod M&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inc = 1, 2, 3, 4, ...&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;&lt;b&gt;3) 이중 해싱법&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버 플로우 / 충돌이 발생하면 원래의 해시함수와 다른 별개의 해시 함수를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;h'(k) = C - (k mod C)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;C = 해시 테이블 크기 M보다 작은 소수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;h(k) &amp;rarr; 충돌 발생&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; h(k) + h'(k)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; h(k) + 2* h'(k)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; h(k) + 3* h'(k)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;530&quot;&gt;&lt;a href=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foau8v%2FbtrGqI5u1e9%2FSyHuGFds9bOrRZPOqpkBF0%2Fimg.jpg&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QKrkM/btsLBy4AzMU/2NP62jj921jYI9PUDKLToK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQKrkM%2FbtsLBy4AzMU%2F2NP62jj921jYI9PUDKLToK%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;573&quot; height=&quot;369&quot; data-origin-width=&quot;823&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@bada308/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%95%B4%EC%8B%9CHash&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@bada308/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%95%B4%EC%8B%9CHash&lt;/a&gt;&lt;/p&gt;</description>
      <category>책/자료구조와알고리즘with파이썬</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/81</guid>
      <comments>https://youryoung.tistory.com/81#entry81comment</comments>
      <pubDate>Tue, 31 Dec 2024 14:06:40 +0900</pubDate>
    </item>
    <item>
      <title>Ch.13 탐색</title>
      <link>https://youryoung.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;(Chap.13 ) Search&lt;br&gt;&lt;br&gt;Sequential search(순차탐색)는 O(n)으로 탐색알고리즘의 하한선에 해당하는 알고리즘임.&lt;br&gt;정렬된 배열을 이용한 Binary search 는 정렬하는 complexity를 제외한다면 O(logn)으로 순차탐색 보다 성능이 좋음. &lt;br&gt;정렬된 배열을 이용한 Indexed sequential search는 기정렬된 주자료에서 (n/m) x i, i = 0, 1, 2, ... 번째 record들로 구성한 index table을 만들고 index table을 우선적으로 탐색하고 실패하면 index table의 연속된 두 엔트리 사이에 해당하는 주자료 테이블 내용을 검색하는 방식임.&amp;nbsp;&amp;nbsp;index table 크기(m)이 커지면&amp;nbsp;&amp;nbsp;index table에서 탐색이 실패할 경우 원본 dataset에서 탐색할 대상의 크기가 작아짐. &lt;br&gt;정렬된 배열을 이용한 Interpolation search(보간탐색) &lt;br&gt;이진탐색(binary search) 방법과 매우 유사하나,&lt;br&gt;매 반복(iteration) 마다 정확히 탐색범위를 반으로 줄이는 이진탐색(binary search)과는 달리 탐색하고자 하는 값이 저장되었을 것으로 예상되는 index를 유추하는 계산식을 이용하여 탐색 횟수를 줄이려는 아이디어임.&lt;br&gt;만일 정렬된 dataset에 저장된 데이터들의 값들이 &quot;연속된 두 항의 크기 차이&quot;가 모두 유사하게 분포한 경우 이진탐색(binary search) 보다 실행 속도가 빠름. &lt;br&gt;그러나, 정렬된 dataset에 저장된 데이터들의 값들이 &quot;연속된 두 항의 크기 차이&quot;가 불균등한 경우 이진탐색(binary search) 보다 느릴 수 있으나 그래도 O(logn)임. &lt;br&gt;&lt;br&gt;이진탐색트리 (BST: Binary Search Tree) vs. (AVL tree or 2-3 tree)&lt;br&gt;&lt;br&gt;BST 의 성능 (time complexity)는 트리의 높이에 의존적임, 즉, O(h)&lt;br&gt;동일한 dataset 도 만일 불균형한 트리 (극단적으로 skewed binary tree)로 구성하며 탐색에 O(n)이 필요함. &lt;br&gt;따라서 탐색 속도를 O(logN) N = dataset의 크기 로 하기 위해서 트리 높이를 logN으로 유지해야함. &lt;br&gt;이를 위해 탄생한 것이 AVL tree, 2-3 tree 임.&lt;br&gt;AVL tree 구성 방법 : BST의 삽입 알고리즘을 수행하고 만일 삽입된 leaf node (A) 로 부터 root 노드 까지의 경로 중 balance factor (왼쪽 subtree의 높이 - 오른쪽 subtree의 높이) 가 2 이상인 노드(B)가 있다면 노드 A 부터 노드 B 까지의 subtree를 재구성해서 balance factor를 낮춤. &lt;/p&gt;</description>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/79</guid>
      <comments>https://youryoung.tistory.com/79#entry79comment</comments>
      <pubDate>Mon, 9 Dec 2024 22:52:05 +0900</pubDate>
    </item>
    <item>
      <title>Ch.12 정렬</title>
      <link>https://youryoung.tistory.com/76</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;Chapter 12. Sorting&lt;/b&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;stable한가? in-place인가? 움직인 위치가 최종 위치인가?&amp;nbsp;&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;데이터들을 특정순서(Decreasing order, Non-increasing order, Increasing order, Non-decreasing order)로 정리하는 것.&lt;/li&gt;&lt;li&gt;데이터들의 searching, analyzing, manipulating 을 효율적으로 수행할 수 있게 하여 IT 분야에 기본적이며 필수적인 연산(알고리즘)임.&lt;/li&gt;&lt;li&gt;모든 경우에 최적인 정렬 알고리즘은 없으므로 정렬의 대상과 정렬을 실행할 환경에 따라 아래의 상황을 고려하여 알고리즘을 선택해야함.&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;정렬할 대상의 개수 (dataset의 크기)는?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;정렬할 대상의 일부가 이미 정렬되어 있을 수도 있나?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;필요한 (비교연산과 이동연산을 포함한) 수행시간(time complexity) 은?&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;가용한 memory or disk 크기(space complexity)는?&amp;nbsp;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;정렬 알고리즘이 (input size에 비례하는) 추가 공간을 필요로 하는가?&lt;/span&gt;&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;대표적으로 merge sorting은 time complexity는 최고(nlogn, n=정렬대상의개수)이나 space complexity는 n에 비례한 extra memory 가 필요하여 O(n)이다.&amp;nbsp;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;반면, selection sorting은 time complexity는 나쁘나(n^2) space complexity가 O(1) 이다.&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=kPRA0W1kECg&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Sorting animation 1)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&lt;a href=&quot;https://www.toptal.com/developers/sorting-algorithms&quot; target=&quot;_self&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Sorting animation 2)&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;br&gt;&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Stability of sorting algorithm : (10&amp;nbsp; 30 &amp;nbsp;20&amp;nbsp; 30'&amp;nbsp; 5&amp;nbsp; 7) 을 selection/insertion/bubble/shell sorting 하면 (30 --&amp;gt; 30') 의 순서가 유지될까?&lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;dataset의 크기가 n일때 selection/insertion/bubble/shell sort algorithm은 n에 관한 함수만큼의 추가 공간이 필요할까? == 이 알고리즘들이 in-place sort algorithm 인가? == 이 알고리즘들의 space complexity가 O(1) 인가?&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #f6f6f6;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;아래는 non-decreasing order로 설명되고있음.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Selection sort&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;정렬 중 (sorted part +&amp;nbsp;unsorted part) 를 유지함.&lt;/li&gt;&lt;li&gt;각 iteration 마다 unsorted part에서 비교연산이 일어남.&lt;/li&gt;&lt;li&gt;각 iteration 마다 unsorted part에서&amp;nbsp;가장 작은 값이&amp;nbsp; 최종 output에서의 자신의 정렬된 위치를 찾아감.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Insertion sort&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;정렬 중 (sorted part +&amp;nbsp;unsorted part) 를 유지함.&lt;/li&gt;&lt;li&gt;각 iteration 마다 unsorted part의 &lt;u&gt;가장 왼쪽 값을&lt;/u&gt; key로 sorted part에 정렬시켜 삽입함.&lt;/li&gt;&lt;li&gt;sorted part 에서 비교 연산이 일어남.&lt;/li&gt;&lt;li&gt;각 라운드 (iteration)에서 sorted part 추가 된 값은 최종 sorted output에서의 위치와 동일하지 않을 수 있음.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Bubble sort&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;정렬 중&amp;nbsp; (unsorted part + sorted part) 를 유지함.&lt;/li&gt;&lt;li&gt;각 iteration 마다 왼쪽의 unsorted part에서 가장 큰 값이 sorted part로 이동함.&lt;/li&gt;&lt;li&gt;각 iteration 에서 sorted part로 이동한 값은 최종 output에서의 자신의 위치임.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Shell sort&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;insertion sort의 upgrade version으로 속도가 훨씬 빠른 것으로 증명됨.&lt;/li&gt;&lt;li&gt;단, 성능은 gap sequence를 어떻게 설정하는가에 따라 다르게 분석될 수 있음.&lt;/li&gt;&lt;li&gt;여기서 빠르다는 것은&amp;nbsp;(time complexity가 O(n^2) 보다 좋다)는 의미임.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;Radix sort 기수정렬&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;(조건) 정렬하고자하는 record의 key 값의 data type이 discrete 하게 분류(&lt;b&gt;d개의 자리수로&lt;/b&gt;)가 가능해야함. (예) 십진수, 이진수, 알파벳, (실수/한글/한자는 안됨)&lt;/li&gt;&lt;li&gt;(아이디어) &lt;b&gt;각 자리수를 표현할 수 있는 경우의 수(b) 만큼의 queue(bucket)를 사용&lt;/b&gt;하여,&amp;nbsp;&lt;u&gt;&lt;b&gt;낮은 자리 수 부터&amp;nbsp;&lt;/b&gt;&lt;/u&gt;높은 자리수로 각 자리수에 해당하는&amp;nbsp;&lt;u&gt;&lt;b&gt;queue에 넣고&lt;/b&gt;&lt;/u&gt;(enqueue) 정렬하고자 하는 순서에 따른 queue 순서(queue0-&amp;gt;queue1--&amp;gt;queue2...--&amp;gt;queue9)대로 dequeue()를 실행하는 것을 &lt;b&gt;d(자리수)번 반복&lt;/b&gt;한다.&amp;nbsp;&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;(예) 64bit 로 표현되는 key 가 있다. key 값의 한 자리수를 4bit로 표현한다고 가정하면&amp;nbsp;&lt;b&gt;b = 2^4 = 16개의 queue&lt;/b&gt; 가 필요하며,&lt;b&gt; 총 d=(64/4)=16 번 enqueue/dequeue를 반복&lt;/b&gt;해야 정렬됨.&lt;/li&gt;&lt;li&gt;1의 자리를 정렬하고 나면, 동일한 10의 자리 수를 가진 숫자들 끼리는 정렬된 상태임.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;(잇점) key 값이 radix 정렬이 가능한 조건이라면 time complexity가 O(n)에 bound 하므로 정렬 알고리즘의 상한선이 O(nlogn) 을 자랑하는 merge sort&amp;nbsp; 보다 성능이 좋음. 예를 들어,&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;5자리 정수인 학수번호를 key 값으로 정렬하는 경우 radix sort 가 merge sort 보다 성능이 좋음. (참)&lt;/li&gt;&lt;li&gt;한국이름을 key 값으로 정렬을 할 때 radix sort 가 merge sort 보다 성능이 좋음. (거짓)&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Space complexity : d 개의 bucket이 필요하며 각 bucket의 크기는 최악의 경우 (input size n 의 모든 record들의 key 값이 동일한 경우) n 이므로 O(dn), O(n).&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;Summary of sort algorithms (n : 정렬하고자 하는 record의 개수)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Time complxity&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;실수 혹은 한글을 key로 하는 dataset을 정렬할 경우, O(nlogn)이 best 알고리즘임. (최악의 경우 time complexity가 가장 좋은 알고리즘은 heap/merge sort)&lt;/li&gt;&lt;li&gt;100000 자리 정수로 구성된 key를 정렬할 경우 radix sort 가 O(n)으로 O(nlogn)인 merge sort 보다 빠름.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Space complexity of Merge sort and Radix sort is NOT bounded to O(1).&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;Merge/Radix sort의 space complexity는 O(n)&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Insertion/Bubble/Merge/Radix sort algorithms are stable.&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;Radix sort를 제외하고 stable sort algorithm들은 비교/이동 연산이 인접한 두 index 사이에서 일어난다는 특징이 있음.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;정렬이란&lt;/b&gt;&lt;br&gt;특정 데이터들을 특정 순서로 정리하는 것.&lt;br&gt;→ 데이터들의 searching, analyzing, manipulating을 효율적으로 수행할 수 있게 함&lt;br&gt;→ 모든 경우에 최적인 정렬 알고리즘은 없다. 따라서 각 프로그램의 목적 및 실행환경에 적합한 방법을 사용해야 한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;정렬 알고리즘의 평가 기준&lt;br&gt;- &lt;b&gt;비교&lt;/b&gt; 횟수의 많고 적음&lt;br&gt;- &lt;b&gt;이동&lt;/b&gt; 횟수의 많고 적음&lt;br&gt;&amp;nbsp;&lt;br&gt;1) 분류기준 1&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style3&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;단순 but 비효율적&lt;/td&gt;&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;복잡 but 효율적&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;삽입(Shell), 선택, 버블정렬 등&lt;/td&gt;&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;퀵, 히프, 합병, 기수 정렬 등&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;2) 분류기준 2&lt;br&gt;- 내부 정렬: 모든 데이터가 주기억장치에 저장되어진 상태에서 정렬&lt;br&gt;- 외부 정렬: 외부기억장치에 대부분의 데이터가 있고 일부만 주기억장치에 저장된 상태에서 정렬&lt;br&gt;&amp;nbsp;&lt;br&gt;3) 분류기준 3&lt;br&gt;&lt;b&gt;정렬 알고리즘의 안정성 (Stability)&lt;/b&gt;&lt;br&gt;→ 안정성 있는 정렬은 동일한 키 값을 갖는 레코드들의 상대적인 위치가 정렬 후에도 바뀌지 않는다. (== 순서 유지)&lt;br&gt;ex) 삽입정렬, 버블정렬, 합병정렬 등&lt;br&gt;&amp;nbsp;&lt;br&gt;바뀌는 경우 (안정 X)&lt;br&gt;ex) 선택정렬, 히프정렬 등&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;선택 정렬 (Selection Sort) - unsorted part에서 가장 작은 수를 선택(오른쪽)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;: 정렬된 왼쪽 리스트와 정렬되지 않은 오른쪽 리스트를 가정한다.&lt;br&gt;→ 초기에는 왼쪽 리스트는 비어있고, 정렬할 숫자들은 모두 오른쪽 리스트에 존재한다.&lt;br&gt;: non-decreasing order로 정렬한다고 가정&lt;br&gt;: &lt;b&gt;오른쪽 리스트에서 가장 작은 숫자를 선택&lt;/b&gt;하여 왼쪽 리스트로 이동하는 작업을 되풀이한다. (오른쪽 리스트가 공백상태가 될 때까지 반복한다. 그니까 &lt;b&gt;n-1번 반복&lt;/b&gt;한다.)&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;배열의 앞쪽은 sorted part (왼쪽), 뒤는 unsorted part로 구분하고 swapping을 하면 in-place sorting이 가능하다.&lt;/li&gt;&lt;li&gt;따라서 추가적인 메모리 공간이 필요하지 않다.&lt;/li&gt;&lt;li&gt;&lt;b&gt;unsorted part에서 비교연산이 발생&lt;/b&gt;한다.&lt;/li&gt;&lt;li&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;선택정렬은 더이상의 이동이 필요 없더라도 계속 &lt;b&gt;swapping(이동연산)을 진행&lt;/b&gt;한다.&lt;/li&gt;&lt;li&gt;→ 이동을 하지 않는게 더 효율적인 경우는?&lt;/li&gt;&lt;li&gt;- record size가 큰 경우&lt;/li&gt;&lt;li&gt;- input data set이 거의 정렬된 경우&lt;/li&gt;&lt;li&gt;&amp;nbsp;&lt;/li&gt;&lt;li&gt;마지막은 이미 max값이기 때문에 0에서 n-2번 인덱스번호까지 &lt;b&gt;총 n-1번만 반복&lt;/b&gt;한다.&lt;/li&gt;&lt;li&gt;매 round마다 하나의 레코드가 정렬된 &lt;b&gt;최종 위치&lt;/b&gt;를 찾게 된다. (더이상 바뀌지 않는 최종 위치임)&lt;/li&gt;&lt;/ul&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 17px;&quot;&gt;&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;비교횟수&lt;/td&gt;&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;O(n*n)&lt;/td&gt;&lt;/tr&gt;&lt;tr style=&quot;height: 17px;&quot;&gt;&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;이동횟수&lt;/td&gt;&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;3*(n-1) → O(n)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;* best/worst case 모두 비교횟수는 항상 모두 동일하다.&lt;br&gt;* 전체 시간적 복잡도: O(n*n)&lt;br&gt;* unstable sort (input된 순서가 바뀔 수도 있다.)&lt;br&gt;* in-place sort (메모리 사용 최소화, space complicity O(1))&lt;br&gt;* small datasets에 적합하다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;삽입 정렬 (insertion sort) - &lt;/b&gt;&lt;u&gt;&lt;b&gt;sorted part에서 비교연산&lt;/b&gt;&lt;/u&gt;&lt;b&gt;을 한다. (왼쪽)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;정렬되어 있는 리스트에 새로운 레코드를 정렬이 유지되는 위치에 삽입하는 과정을 반복&lt;br&gt;단, 맨 끝까지 해야 각 수의 최종 위치를 정확하게 알 수 있다.&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;앞: sorted part&lt;/li&gt;&lt;li&gt;뒤: unsorted part&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;* Best case에서의 시간 복잡도: O(N)&lt;br&gt;→ 한 번만 비교한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;추가 공간은 변수(key) 하나이다. 따라서 space complexity = O(1)&lt;br&gt;고정된 양의 추가 메모리만 필요하기 때문에 in-place sort 알고리즘이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;-&lt;b&gt; best case:&lt;/b&gt; list가 기정렬된 상태이므로 비교 n-1회&amp;nbsp; → O(N)&lt;br&gt;바로 앞에것과 1회씩만 비교한다.&lt;br&gt;&lt;u&gt;selection sort 선택 정렬에서는 기 정렬된 경우에도 unsorted part에서 비교연산이 일어나기 때문에 O(n*n) 이다.&lt;/u&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;- &lt;b&gt;worst case:&lt;/b&gt; list가 역정렬됨 → O(N*N)&lt;br&gt;- 평균: O(N*N)&lt;br&gt;&amp;nbsp;&lt;br&gt;* 많은 이동이 필요하기 때문에 small dataset에 적합하다.&lt;br&gt;* 안정된 stable 정렬 방법이다.&amp;nbsp;&lt;br&gt;&lt;u&gt;selection sort 선택 정렬과는 달리 낮은 index부터 순서대로 정렬하기 때문&lt;/u&gt;&lt;br&gt;* 대부분 정렬되어 있는 경우 매우 효율적인 알고리즘이다. O(N)&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;버블 정렬 (Bubble sort) - non decreasing&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;인접한 2개의 레코드를 비교하여 크기가 순서대로 되어있지 않으면서 서로 교환하는 비교-교환 과정(비교/연산의 기본 단위가 2개임)을 리스트의 왼쪽 끝에서 시작하여 오른쪽 끝까지 진행 (서로 교환)&lt;br&gt;&amp;nbsp;&lt;br&gt;비교-교환 과정이 한 번 완료되면 가장 큰 레코드가 리스트의 오른쪽 끝으로 이동한다.&lt;br&gt;전체 숫자가 전부 정렬될 때까지 계속된다. &lt;b&gt;N-1번 반복&lt;/b&gt;한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;In each round, unsorted part에서 가장 큰 record가 &lt;b&gt;최종 위치를 찾아간&lt;/b&gt;다.&lt;br&gt;&amp;nbsp;&lt;br&gt;비교횟수:&lt;b&gt; O(n*n)&lt;/b&gt;&lt;br&gt;최상, 평균, 최악의 경우 모두 동일하다.&lt;br&gt;(insertion sort 삽입 정렬은 기 정렬되어 있는 best case의 경우 바로 앞에 것과 1회씩만 비교하기 때문에 O(n)이다.)&lt;br&gt;&amp;nbsp;&lt;br&gt;이동횟수:&amp;nbsp;&lt;br&gt;역순으로 정렬된 경우(worst case): 3 * 비교횟수 번&lt;br&gt;이미 정렬된 경우(best case): 0번&lt;br&gt;평균의 경우:&lt;b&gt; O(n*n)&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;이동연산은 비교 연산보다 더 많은 시간이 소요된다.&lt;br&gt;→ &lt;b&gt;부분적으로 정렬된 데이터 셋&lt;/b&gt;에서 더 효율적이다.&lt;br&gt;- small dataset에 적합하다.&lt;br&gt;- &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;역순이 아니면 이동하지 않으므로 &lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;u&gt;&lt;b&gt;stable sort&lt;/b&gt;&lt;/u&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;이다.&lt;/b&gt;&lt;/span&gt;&lt;br&gt;- in-place sort&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;셸 정렬 (Shell sort)&lt;/b&gt;&lt;/h4&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;insertion sort의 upgrade version으로 속도가 훨씬 빠른 것으로 증명됨.&lt;/li&gt;&lt;li&gt;삽입 정렬이 어느 정도 정렬된 리스트에서 대단히 빠른 것에 착안&lt;/li&gt;&lt;li&gt;= input에 있는 값들이 어느 정도 이미 정렬된 위치에 있다.&lt;/li&gt;&lt;li&gt;단, &lt;b&gt;성능&lt;/b&gt;은 &lt;u&gt;&lt;b&gt;gap sequence&lt;/b&gt;&lt;/u&gt;를 어떻게 설정하는가에 따라 다르게 분석될 수 있음.&lt;/li&gt;&lt;li&gt;여기서 &lt;b&gt;빠르다는 것은&amp;nbsp;(time complexity가 O(n^2) 보다 좋다)는 의미&lt;/b&gt;임.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;전체 리스트를 일정 간격 (gap)의 부분 리스트로 나눈다. → &lt;b&gt;나뉘어진 각각의 부분 리스트를 삽입정렬&lt;/b&gt; 한다.&lt;br&gt;- &lt;u&gt;불연속적인 부분 리스트에서&lt;/u&gt; 원거리 자료 이동보다 &lt;u&gt;적은 위치 교환으로 제자리를 찾을 가능성이 높다&lt;/u&gt;.&lt;br&gt;- 부분 리스트가 점진적으로 정렬된 상태가 되므로 삽입정렬 속도가 증가한다.&lt;br&gt;- unstable sort due to gap&lt;br&gt;- in-place sort → space complexity = O(1) / 실제 부분 리스트들이 만들어지는 것이 아니라 일정한 간격으로 삽입 정렬을 수행하는 것이기 때문이다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;이제는 복잡하지만 효율적인 방법에 대해서 본다. (입력 데이터가 많으면서 자주 정렬해야 하는 경우)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;*** n개의 record로 구성된 dataset을 non-decreasing order로 정렬하려고 한다. ***&lt;br&gt;&lt;b&gt;Merge sort&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;(unlike quick sort) 주어진 dataset이 균등한 크기로 분할(by calling merge_sort())되었다가 크기가 1이되면 합병(by calling merge())되면서 정렬됨.&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;(n개가 2의 배수가 아닌 경우 분할되는 패턴에 주의 할 것!)&amp;nbsp; (예) n = 10 --&amp;gt;&amp;nbsp;(((0 1) (2)) ((3) (4))) (((5 6) (7)) ((8) (9)))&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Space complexity&amp;nbsp; &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;O(n)&lt;/span&gt;&lt;/span&gt; &lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;두 sorted subset이 merge 될 때 임시 메모리(배열C 라고 하자)가 필요하며,&amp;nbsp;마지막 round의 merge( ) 에서 n개가 병합되므로 O(n)의 extra space가 필요함.&lt;/li&gt;&lt;li&gt;각 merge( ) 함수는 주어진 input (슬라이드에서 list[ ])의 list[0] ~ list[9] 사이의 일부분을 정렬하는 것임.&amp;nbsp;&lt;/li&gt;&lt;li&gt;정렬된 값들은 임시저장소(배열 C)에 저장되어있으므로 merge( ) 함수 종료 직전에 input (list[ ]) 배열의 해당 인덱스에 copy해 놓아야 merge( ) 함수에서 행한 정렬 결과가 다음 recursive call에 반영됨.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Time Complexity&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;n개의 record가 각 round 마다 균등하게 분할되므로 총 &lt;b&gt;O(logn)&lt;/b&gt;의 iteration이 필요함.&lt;/li&gt;&lt;li&gt;각 merge( ) 함수는 전체의 일부분을 merge 하나,&amp;nbsp;각 round에서 호출되는 merge( ) 를 통합하면 최대 (n-1) 번의 비교연산이 일어나게 되므로 &lt;b&gt;O(n)의 시간이 소요&lt;/b&gt;됨.&amp;nbsp; (이동은 비교하면서 최대 (n-1)번 이후 temporary memory에서 original list[]로 copy 되면서 또 최대 n 번 일어남.) 결국 각 round 마다 연산은 O(n) 임.&lt;/li&gt;&lt;li&gt;&lt;b&gt;따라서 merge sort의 time complexity는 O(nlogn)&lt;/b&gt;임.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;&lt;b&gt;Stable sort algorithm&lt;/b&gt; 임.&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;merge() 함수에서 두 sub list를 통합하며 정렬할때 비교하는 &lt;b&gt;두 key 값이 같은 경우 앞쪽 sub list의 값을 우선적으로 선택함&lt;/b&gt;으로써 &lt;b&gt;stable 하게 구현할&lt;/b&gt; 수 있음.&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;br&gt;&lt;b&gt;Quick sort&lt;/b&gt;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;매 round 마다 pivot을 결정함. (강의 슬라이드에서는 해당 round의 input 배열들 중 가장 index가 낮은 항의 값을 pivot으로 정함)&amp;nbsp;&lt;/li&gt;&lt;li&gt;pivot 값이 quick sort의 성능(time complexity)에 영향을 줌.&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;현재 round의 input값들 중 중간 크기의 값을 정할 때에는 (merge sort)처럼 크기가 균등한 두 subset으로 분할되어 반복되는 round의 횟수가 &lt;b&gt;O(logn)&lt;/b&gt;에 바운드 함.&lt;/li&gt;&lt;li&gt;이미 정렬된것을 다시 정렬하면서 항상 남은 정렬대상에서 가장 작은 key 값을 pivot으로 정하는 경우처럼, 한쪽으로 치우치게 분할될 경우 &lt;b&gt;O(n)&lt;/b&gt; 만큼의 iteration이 발생하게 됨.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;각 iteration 마다, partition( ) 함수가 실행됨. &lt;b&gt;n번&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;partition( ) 함수에서 pivot의 최종위치는 가장 왼쪽부터 index가 커지는 방향으로 scan 하여 pivot 보다 큰 첫 번째 항(low)과 가장 오른쪽에서 index가 작아지는 방향으로 scan하여&amp;nbsp;pivot 보다 작은 첫 번째 항(high)을 찾아 이 두 항의 값을 swapping 하는 과정을 반복하게 됨.&amp;nbsp;&lt;/li&gt;&lt;li&gt;swapping 만으로 해결되므로 partition()의&amp;nbsp;space complexity는 O(1).&lt;/li&gt;&lt;li&gt;결과적으로 pivot 값이 최종 정렬된 위치에 저장되고,&lt;/li&gt;&lt;li&gt;pivot이 저장된 위치에서 왼쪽 subset에는 pivot 보다 작은 key 값들이,&lt;/li&gt;&lt;li&gt;pivot이 저장된 위치에서&amp;nbsp;오른쪽 subset에는 pivot보다 큰 key 값들이 저장됨.&lt;/li&gt;&lt;li&gt;왼쪽 subset과 오른쪽 subset은 다시 정렬해야함.&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;selection/bubble sort 처럼, Quick sort algorithm도 한 round(iteration) 마다 n개 중 1개의 record가 최종 정렬된 위치로 이동하는 알고리즘임.&lt;/li&gt;&lt;/ul&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;Quick sort algorithm is &lt;b&gt;not stable&lt;/b&gt;.&amp;nbsp;&lt;/li&gt;&lt;/ul&gt;&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;&lt;li&gt;counter example)&amp;nbsp;list[] = (10, 8, 7, 6, 10', 5, 4, 3, 2, 1)&lt;/li&gt;&lt;/ol&gt;&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;&lt;li&gt;pivot을 결정하는 알고리즘에 따라 성능이 달라질 수 있음.&lt;/li&gt;&lt;/ul&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;합병 정렬 (merge sort) - 분할 정복 기법&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 리스트를 두 개의 &lt;b&gt;균등한 크기로 분할&lt;/b&gt;하고(unlike quick sort) 분할된 부분 리스트를 정렬한 다음, 두 개의 정렬된 부분 리스트를 합하여 전체가 정렬된 리스트를 얻는다.&lt;br&gt;→ 균등하게 분할하기 때문에 O(log n)&lt;br&gt;&amp;nbsp;&lt;br&gt;Q. &lt;b&gt;몇 개의 단계(round)로 분할&lt;/b&gt;될까? 즉, 부분 리스트를 합하는 연산은 몇 번 일어날까?&lt;br&gt;A.&lt;b&gt; log N, N=input size&lt;/b&gt; (균등하게 분할했기 때문에)&lt;br&gt;&lt;u&gt;&lt;b&gt;딱 나누어지면 perfect binary tree처럼 생기게 된다&lt;/b&gt;&lt;/u&gt;&lt;b&gt;. (높이)&lt;/b&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;- 독립적인 recursive call을 한다.&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[non-decreasing order]&lt;br&gt;1. 분할 (Devide)&lt;br&gt;배열을 같은 크기의 2개 부분 배열로 분할&lt;br&gt;→ dataset의 크기가 1이 될 때까지 분할한다. → 비교연산은 두 개의 key값으로 이우러지기 때문&amp;nbsp;&lt;br&gt;&lt;br&gt;2. 정복 (Conquer)&lt;br&gt;부분 배열을 정렬한다. 부분배열의 크기가 충분히 작지 않으면 재귀호출을 이용하여 다시 분할-정복기법을 적용한다.&amp;nbsp;&lt;br&gt;→&amp;nbsp;부분 배열을 정렬할 때도 합병 정렬을 순환적으로 적용하면 된다.&lt;br&gt;&lt;br&gt;3. 결합 (Combine) / 합병 (merge)&lt;br&gt;&lt;b&gt;정렬된 부분배열을 하나의 배열에&amp;nbsp;통합&lt;/b&gt;한다. →&amp;nbsp;&lt;b&gt;여기서 정렬이 수행&lt;/b&gt;된다. (&lt;b&gt;합병&lt;/b&gt;)&lt;br&gt;&lt;b&gt;→&amp;nbsp;정렬한 배열을 다시 original space에 저장해야&lt;/b&gt;&amp;nbsp;한다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;- 왼쪽배열이 끝나야 오른쪽 배열이 분할-정복 과정을 수행하게 된다.&amp;nbsp;&amp;nbsp;&lt;br&gt;- &lt;b&gt;extra space memory → O(n) → in-place sorting algorithm이 아니다&lt;/b&gt;.&lt;br&gt;&amp;nbsp;&lt;br&gt;[합병]&lt;br&gt;Q. 만약 &lt;b&gt;기 정렬된 경우&lt;/b&gt;라면?&lt;br&gt;A. &lt;b&gt;비교 / 이동 횟수가 모두 반으로 줄어&lt;/b&gt;든다.&lt;br&gt;→ comparisons 없이 복사할 수 있는 경우가 생기기 때문이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;[합병]&lt;br&gt;sorted 배열은 임시 저장소이므로 original list에 copy해야 recursive call이 제대로 수행된다.&lt;br&gt;&amp;nbsp;&lt;br&gt;merge()에서 |right - left| 만큼의 비교연산 발생 → O(n)&lt;br&gt;merge()에서 worst case의 경우에 |right - left| 만큼의 비교연산 발생 → O(n)&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;b&gt;recursive call의 횟수는 log(n)&lt;/b&gt;이다. (n== dataset의 크기, 정렬하고자 하는 record의 개수, input의 크기)&lt;br&gt;&amp;nbsp;&lt;br&gt;→ 따라서 &lt;b&gt;merge sort 합병정렬의 시간복잡도는&lt;/b&gt; &lt;b&gt;O(n * log n)&lt;/b&gt;이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;만약 record의 개수가 33개인 dataset을 merge sort하면 merge()함수는 총 몇 번 호출되는가?&lt;br&gt;→ 곤란..... 2의 k승이 아니기 때문에...&amp;nbsp;&lt;br&gt;&amp;nbsp;&lt;br&gt;- 레코드의 크기가 큰 경우에는 매우 큰 시간적 낭비를 초래한다.&lt;br&gt;- 레코드를 연결 리스트로 구성하여 합병 정렬할 경우, 매우 효율적이다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;퀵 정렬 (Quick sort)&lt;/b&gt;&lt;/h4&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;리스트를 2개의 부분리스트로&lt;b&gt; 비균등 분할&lt;/b&gt;하고, 각각의 부분리스트를 &lt;b&gt;다시 퀵정렬한다. &lt;/b&gt;(&lt;b&gt;재귀호출&lt;/b&gt;)&lt;br&gt;- 평균적으로 가장 빠른 정렬 방법이다.&lt;br&gt;- 분할-정복법을 사용한다.&lt;br&gt;&amp;nbsp;&lt;br&gt;- 가능하면 선택된 pivot에 의해 얻어진 부분리스트의 길이가 비슷하도록 / unsorted list 내 key들 중 중간 값을 pivot로 선택&lt;br&gt;- pivot은 최종 (in the sorted list) 자리로 이동하게 된다.&lt;br&gt;- 현 리스트의 가장 왼쪽 record의 key가 pivot이 된다.&lt;/p&gt;&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 17px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;&lt;tbody&gt;&lt;tr style=&quot;height: 17px;&quot;&gt;&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;pivot보다 작은 값들&lt;/td&gt;&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;pivot&lt;/td&gt;&lt;td style=&quot;width: 33.3333%; height: 17px; text-align: center;&quot;&gt;pivot보다 큰 값들&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;high &amp;lt; low가 되면 swapping을 멈추고 done!&lt;br&gt;&lt;span style=&quot;color: #333333;&quot;&gt;→ 이 때 high 위치가 pivot이 들어갈 정렬된 위치이다. (최종 위치)&amp;nbsp;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;퀵소트는 unstable algorithm이다.&lt;br&gt;&amp;nbsp;&lt;br&gt;left &amp;lt; right인 경우에만 quick sort를 반복하고 이 조건이 어긋나는 경우 sorting을 종료한다.&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[알고리즘] 퀵 정렬(quick sort)이란 - Heee's Development Blog&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Step by step goes a long way.&quot; data-og-host=&quot;gmlwjd9405.github.io&quot; data-og-source-url=&quot;https://gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bCK9mA/hyXGyLLNw0/VfGk3oCcnLO9oT3n0yt6i1/img.png?width=2184&amp;amp;height=987&amp;amp;face=0_0_2184_987,https://scrap.kakaocdn.net/dn/OreT2/hyXKyi1kD2/DyIovZ8p3WjX6kCgviquH1/img.png?width=1443&amp;amp;height=625&amp;amp;face=0_0_1443_625,https://scrap.kakaocdn.net/dn/jsXgZ/hyXKoOe24F/PXUb6VnYY2bKwPKk6pvLu0/img.png?width=1364&amp;amp;height=1960&amp;amp;face=0_0_1364_1960&quot; data-og-url=&quot;http://gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html&quot;&gt;&lt;a href=&quot;http://gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html&quot; target=&quot;_blank&quot; data-source-url=&quot;https://gmlwjd9405.github.io/2018/05/10/algorithm-quick-sort.html&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bCK9mA/hyXGyLLNw0/VfGk3oCcnLO9oT3n0yt6i1/img.png?width=2184&amp;amp;height=987&amp;amp;face=0_0_2184_987,https://scrap.kakaocdn.net/dn/OreT2/hyXKyi1kD2/DyIovZ8p3WjX6kCgviquH1/img.png?width=1443&amp;amp;height=625&amp;amp;face=0_0_1443_625,https://scrap.kakaocdn.net/dn/jsXgZ/hyXKoOe24F/PXUb6VnYY2bKwPKk6pvLu0/img.png?width=1364&amp;amp;height=1960&amp;amp;face=0_0_1364_1960')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[알고리즘] 퀵 정렬(quick sort)이란 - Heee's Development Blog&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Step by step goes a long way.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;gmlwjd9405.github.io&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>자료구조</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/76</guid>
      <comments>https://youryoung.tistory.com/76#entry76comment</comments>
      <pubDate>Thu, 5 Dec 2024 00:26:15 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조와알고리즘with파이썬] ch.9 억지기법과 탐욕적 전략</title>
      <link>https://youryoung.tistory.com/75</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;9-1 문제 해결 과정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;[알고리즘 개발 과정]&lt;/b&gt;&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: 36.6279%; text-align: center;&quot;&gt;문제의 이해&lt;/td&gt;
&lt;td style=&quot;width: 63.3721%; text-align: center;&quot;&gt;예외 경우, 해답 생각해보기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 36.6279%; text-align: center;&quot;&gt;설계 방향 설정&lt;/td&gt;
&lt;td style=&quot;width: 63.3721%; text-align: center;&quot;&gt;순차적 처리 / 병렬적 처리, 최적해 / 근사해&lt;br /&gt;근사해: 정확한 해를 구할 수 없음. 계산량이 너무 많아짐. 알고리즘의 중간단계.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 36.6279%; text-align: center;&quot;&gt;알고리즘 설계&lt;/td&gt;
&lt;td style=&quot;width: 63.3721%; text-align: center;&quot;&gt;억지(brute-force)기법, 탐욕적(greedy)기법, 분할 정복, 동적 계획법, 공간으로 시간을 버는 전략, 백트래킹과 분기한정 기법&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 36.6279%; text-align: center;&quot;&gt;알고리즘의 정확성&lt;/td&gt;
&lt;td style=&quot;width: 63.3721%; text-align: center;&quot;&gt;다양한 입력을 통해 틀린 경우를 찾기, 수학적 귀납법 등으로 증명하기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 36.6279%; text-align: center;&quot;&gt;알고리즘의 구현&lt;/td&gt;
&lt;td style=&quot;width: 63.3721%; text-align: center;&quot;&gt;정확성&amp;nbsp; 입증 후 특정 프로그래밍 언어로 구현하기&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;9-2 억지 기법 (brute - force)&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;순차탐색: 처음부터 마지막까지 순서대로 리스트에서 어떤 킷값을 가진 레코드를 찾는 방법&lt;/li&gt;
&lt;li&gt;선택정렬: 숫자를 크기순으로 나열&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;9-3 탐욕적 기법 (greedy method)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'그 순간에 최적'이라고 생각되는 답을 선택한다. (그리고 그 선택은 이후의 단계에서 다시 변경될 수 없다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최적해를 구하는 경우 (최소비용신장트리를 위한 프림 알고리즘, 최단 경로거리를 구하는 다익스트라 알고리즘&lt;/li&gt;
&lt;li&gt;시간적, 공간적 제약이 있는 경우 (분기 한정 기법)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1) 거스름돈 동전 최소화&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;액면가가 서로 다른 m가지의 동전이 있다. 거스름돈으로 v원을 동전으로만 돌려주어야 한다면 최소 몇 개의 동전이 필요한지를 구하시오. 단, 모든 동전은 무한히 사용할 수 있고, 액수가 크 것부터 내림차순으로 순서대로 정렬되어 있다.&lt;/blockquote&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;rarr; 액면가가 가장 높은 동전부터 탐욕적으로 최대한 사용하면서 거스름돈을 맞춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 60원짜리 동전이 있는 경우, 최소한으로 필요한 동전의 수가 달라질 수 있다.&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;rarr; 최적해를 구하기 위한 조건&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;동전의 액면가 중에서 어떤 두 개를 고르더라도 &lt;u&gt;큰 액면가를 작은 액면가로 나누어 떨어지는 동전 체계&lt;/u&gt;를 갖는다면 최적해가 보장된다. 작은 액면가를 여러 개 모으면 반드시 큰 액면가를 만들 수 있기 때문이다.&lt;br /&gt;ex) 500원은 100원 5개를 모아서 만들 수 있다.&lt;/blockquote&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;b&gt;&amp;nbsp;2) 분할 가능한 배낭 채우기&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무게와 상관없이 &lt;u&gt;가장 비싼 물건부터&lt;/u&gt; 넣는 방법&lt;/li&gt;
&lt;li&gt;&lt;u&gt;단위 무게당 가격이 가장 높은 물건부터&lt;/u&gt; 넣는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 어떤 리스트가 입력되면 무게당 가치를 내림차순으로 정렬한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 그리고 단가가 가장 높은 것부터 최대한 많이 탐욕적으로 담는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 가장 가치가 낮은 것은 담기지 않을 수도 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 9095 1, 2, 3 더하기&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CkGKI/btsKW9DN3ss/MLqUFEdBGBkO0dZJEImcx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CkGKI/btsKW9DN3ss/MLqUFEdBGBkO0dZJEImcx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CkGKI/btsKW9DN3ss/MLqUFEdBGBkO0dZJEImcx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCkGKI%2FbtsKW9DN3ss%2FMLqUFEdBGBkO0dZJEImcx0%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;1201&quot; height=&quot;754&quot; data-origin-width=&quot;1201&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;296&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BeRnD/btsKWbh16Rf/Po2NE1f3tiJV7DQGX9ZR1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BeRnD/btsKWbh16Rf/Po2NE1f3tiJV7DQGX9ZR1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BeRnD/btsKWbh16Rf/Po2NE1f3tiJV7DQGX9ZR1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBeRnD%2FbtsKWbh16Rf%2FPo2NE1f3tiJV7DQGX9ZR1K%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;1476&quot; height=&quot;296&quot; data-origin-width=&quot;1476&quot; data-origin-height=&quot;296&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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: 34.8837%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 38.6047%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.8837%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 38.6047%; text-align: center;&quot;&gt;1+1, 2&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.8837%; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 38.6047%; text-align: center;&quot;&gt;1+1+1, 1+2, 2+1, 3&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 34.8837%; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 38.6047%; text-align: center;&quot;&gt;1+1+1+1, 1+2+1, 2+1+1, 3+1, 1+3, 1+1+2, 2+2&lt;/td&gt;
&lt;td style=&quot;width: 26.5116%; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;f(1) = 1&lt;br /&gt;f(2) = 2&lt;br /&gt;f(3) = 4&lt;br /&gt;f(4) = f(1) + f(2) + f(3) = 1 + 2 + 4 = 7&lt;br /&gt;...&lt;br /&gt;f(n) = f(n-3) + f(n-2) + f(n-1)&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt; &lt;span style=&quot;background-color: #fcfcfc; color: #666666; text-align: left;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;f(n) = f(n-3) + f(n-2) + f(n-1)&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;(n&amp;gt;3)&lt;/b&gt;&lt;/blockquote&gt;
&lt;pre id=&quot;code_1732585208836&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
input = sys.stdin.readline

def func(x):
  if x==1:
    return 1
  elif x==2:
    return 2
  elif x==3:
    return 4
  else:
    return func(x-1)+func(x-2)+func(x-3)

t = int(input())
for _ in range(t):
  n = int(input())
  print(func(n))&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[참고자료]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://velog.io/@greene/%EB%B0%B1%EC%A4%80-9095%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://velog.io/@greene/%EB%B0%B1%EC%A4%80-9095%EB%B2%88-1-2-3-%EB%8D%94%ED%95%98%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%AC&lt;/a&gt;&lt;/p&gt;</description>
      <category>책/자료구조와알고리즘with파이썬</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/75</guid>
      <comments>https://youryoung.tistory.com/75#entry75comment</comments>
      <pubDate>Tue, 26 Nov 2024 10:22:56 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조와알고리즘with파이썬] ch.8 Graph</title>
      <link>https://youryoung.tistory.com/71</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8-1 그래프란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프는 연결되어 있는 객체 간의 관계를 표현하는 자료구조이다.&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;br /&gt;그래프는 G = (V, E)로 표시한다.&lt;br /&gt;|v| = 총 정점의 개수, |E| = 총 간선의 개수&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;정점 = vertax, node&lt;/li&gt;
&lt;li&gt;간선 = edge, link&lt;/li&gt;
&lt;li&gt;|V| &amp;gt; 0, |E|&amp;nbsp; &amp;gt;= 0 (정점은 1개 이상 있어야 한다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[그래프 용어]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;인접 정점: 하나의 정점에서 간선에 의해 직접 연결된 정점&lt;/li&gt;
&lt;li&gt;정점의 차수: 그 정점에 연결된 간선의 수 (외차수, 내차수)&lt;/li&gt;
&lt;li&gt;경로(path): 간선을 따라갈 수 있는 길을 순서대로 나열한 것&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단순 경로(simple path): 경로 중에서 &lt;/b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;반복되는 노드가 없는&lt;/b&gt;&lt;/span&gt;&lt;b&gt; 경로&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;사이클(cycle): 경로의 &lt;b&gt;시작 정점과 종료 정점이 동일한&lt;/b&gt; 경로&lt;/li&gt;
&lt;li&gt;단순 사이클(cycle): 시작 정점과 종료 정점이 동일하고, 반복되는 노드가 없는 경로&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[그래프 종류]&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;무방향 그래프(undirected graph)&amp;nbsp;&amp;rarr; (a, b) == (b, a)&lt;/li&gt;
&lt;li&gt;방향 그래프(directed graph) &amp;rarr; &amp;lt;a, b&amp;gt; != &amp;lt;b, a&amp;gt;&lt;/li&gt;
&lt;li&gt;부분 그래프(subgraph) : 정점 집합과 간선 집합의 &lt;u&gt;부분집합&lt;/u&gt;으로 이루어진 그래프&lt;/li&gt;
&lt;li&gt;가중치 그래프(weighted graph): 간선에 비용이나 가중치가 할당된 그래프&lt;/li&gt;
&lt;li&gt;연결 그래프(connected graph):&amp;nbsp; 모든 정점 사이에 &lt;u&gt;&lt;b&gt;경로&lt;/b&gt;&lt;/u&gt;가 존재하는 그래프&lt;/li&gt;
&lt;li&gt;완전 그래프(complete graph) : 그래프의 모든 정점이 연결되어 있는 그래프&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;rarr; 무방향 완전 그래프:&amp;nbsp; &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;times;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;minus;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;/&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;2 &lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;개의 간선&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;rarr; 방향 완전 그래프: &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;times;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&amp;minus;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;1&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;) 개의 간선&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8-2 그래프의 표현&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;[그래프 표현 방법]&lt;/b&gt;&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: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;인접행렬 (adjacent matrix)&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;인접리스트 (adjacent list)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;space&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;fixed, |v| * |v|&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;dynamic allocation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;초기화&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;O( |v| * |v| )&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;O( |v| )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;간선 추가&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;특정 간선의 존재 여부 확인&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;O(1)&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;O( |v| )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;특정 노드의 외차수&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;O( |v| )&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;O( |v| )&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;모든 간선의 개수&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;O(&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;|v| * |v| )&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;O(&lt;span style=&quot;color: #333333;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;|v| + |E| )&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;인접행렬 (adjacent matrix): 간선들의 집합을 2차원 배열을 이용하여 표현 (간선이 있으면 1, 없으면 0)&lt;br /&gt;인접리스트 (adjacent list): 각 정점에 인접한 정점들을 연결한 리스트&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8-3 그래프 순회&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;그래프 탐색 / 순회: &lt;b&gt;하나의 정점&lt;/b&gt;으로부터 &lt;b&gt;시작&lt;/b&gt;하여 &lt;b&gt;차례대로 모든 정점&lt;/b&gt;들을 &lt;b&gt;한 번씩 방문&lt;/b&gt;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;&lt;b&gt;1. 깊이 우선 탐색 (DFS)&lt;/b&gt;&lt;/span&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: &lt;b&gt;한 방향으로 갈 수 있을 때까지&lt;/b&gt; 가다가 &lt;b&gt;더 이상 갈 수 없게 되면 가장 가까운 갈림길로 돌아와서&lt;/b&gt; 이 곳으로부터 &lt;b&gt;다른 방향으로 다시 탐색을 진행&lt;/b&gt;한다.&lt;br /&gt;: 더 이상 순환 호출할 인접 노드가 없을 때 pop( ) 을 한다.&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: 50%; text-align: center;&quot;&gt;되돌아가기 위해서는 Stack이 필요하다.&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;Last in First out&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1974&quot; data-origin-height=&quot;1529&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHJGuk/btsKNZuXdUo/XUZbkmLh1v1OqEOvIoYGOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHJGuk/btsKNZuXdUo/XUZbkmLh1v1OqEOvIoYGOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHJGuk/btsKNZuXdUo/XUZbkmLh1v1OqEOvIoYGOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHJGuk%2FbtsKNZuXdUo%2FXUZbkmLh1v1OqEOvIoYGOk%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;600&quot; height=&quot;465&quot; data-origin-width=&quot;1974&quot; data-origin-height=&quot;1529&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;Stack&lt;/b&gt;&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: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&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;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 8.1: 깊이 우선 탐색(인접행렬 방식)
def DFS(vtx, adj, s, visited):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(vix[s], end=' ')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[s] = True

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in range(len(vtx)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if adj[s][v] != 0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if visited[v]==False:
&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;&amp;nbsp;&amp;nbsp;DFS(vtx, adj, v, visited)

# 코드 8.2: 깊이 우선 탐색 테스트 프로그램
vtx = ['U', 'V', 'W', 'X', 'Y']
edge = [[0, 1, 1, 0, 0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[1, 0, 1, 1, 0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[1, 1, 0, 0, 1],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[0, 1, 0, 0, 0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;[0, 0, 1, 0, 0]]

print('DFS(출발:U) : ', end='')
DFS(vtx, edge, 0, [False]*len(vtx))
print()

&amp;gt; 출력
DFS(출발:U) : U V W Y X&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;size18&quot;&gt;&lt;span style=&quot;background-color: #c1bef9;&quot;&gt;&lt;b&gt;2. 너비 우선 탐색 (BFS)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 시작 정점으로부터 가까운 정점을 먼저 방문하고 멀리 떨어져 있는 정점을 나중에 방문&lt;br /&gt;: 소스 노드에서 가까운 노드들을 순서대로, 즉 앞서 들어간 노드를 먼저 꺼내야 한다.&lt;br /&gt;&amp;rarr; First in First out &amp;rarr; Queue 사용&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: 100%;&quot;&gt;1, 큐에 정점을 삽입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;2. 큐가 공백이 아니면 정점을 삭제&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;3. 아직 방문하지 않은 정점을 큐에 삽입&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;4. 삽입된 정점이 방문되었다는 것을 표시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;5. 2부터 계속 반복하기&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1993&quot; data-origin-height=&quot;2482&quot;&gt;&lt;a href=&quot;https://gmlwjd9405.github.io/2018/08/15/algorithm-bfs.html&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rOPxU/btsKMKFvUJS/WWFxdwspCXEgj6MPpfpfMK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrOPxU%2FbtsKMKFvUJS%2FWWFxdwspCXEgj6MPpfpfMK%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;600&quot; height=&quot;747&quot; data-origin-width=&quot;1993&quot; data-origin-height=&quot;2482&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Queue&lt;/b&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 102px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;방문된 순서: 0 &amp;rarr; 1 &amp;rarr; 2 &amp;rarr; 4 &amp;rarr; 3&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 8.3: 너비 우선 탐색(인접리스트 방식)
from queue import Queue&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #queue 모듈의 Queue 사용
def BFS_AL(vtx, aList, s):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n = len(vtx)&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;&amp;nbsp;&amp;nbsp;visited = [False]*n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 방문 확인을 위한 리스트
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q = Queue()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q.put(s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[s] = True
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while not q.empty():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s = q.get()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(vtx[s], end=' ')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in aList[s]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not visited[v]:
&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;&amp;nbsp;&amp;nbsp;q.put(v)
&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;&amp;nbsp;&amp;nbsp;visited[v] = True

# 코드 8.4: 너비 우선 탐색 테스트 프로그램
vtx = ['U', 'V', 'W', 'X', 'Y']
aList = [[1, 2],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0, 2, 3],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0, 1, 4],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [1],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [2]]
print('BFS(출발:U) : ', end='')
BFS_AL(vtx, aList, 0)
print()

&amp;gt; 출력
BFS(출발:U) : U V W X Y&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8-4 신장 트리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;신장트리 (Spanning tree)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 그래프 내의 &lt;b&gt;모든 정점을 포함&lt;/b&gt;하는 트리&lt;br /&gt;: connected, no cycle&lt;br /&gt;: n개의 정점을 가지는 그래프의 신장트리는 n-1개의 간선을 가진다.&lt;br /&gt;: 깊이 우선 탐색이나 너비 우선 탐색 도중에 사용된 간선들을 모으면 신장 트리가 만들어진다.&lt;br /&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;1) DFS 깊이 우선 탐색&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 8.5: DFS를 이용한 신장트리(인접행렬 방식)
def ST_DFS(vtx, adj, s, visited):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[s] = True
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in range(len(vtx)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if adj[s][v] != 0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if visited[v]==False:
&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;&amp;nbsp;&amp;nbsp;print(&quot;(&quot;, vtx[s], vtx[v], &quot;)&quot;, end=' ')
&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;&amp;nbsp;&amp;nbsp;ST_DFS(vtx, adj, v, visited)&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&quot;&quot;&quot;방문하지 않은 s의 이웃 정점 v가 있으면, 간선 (s,v)를 신장트리에 추가하고, 
&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;&amp;nbsp;&amp;nbsp;v를 시작으로 다시 깊이 우선 탐색 진행.&quot;&quot;&quot;

# DFS를 이용한 신장트리 테스트 프로그램
vtx = ['U','V','W','X','Y']
edge= [[0,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [1,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [1,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;1],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;0],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;1,&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;0]]

print('ST_DFS_AM: ', end=&quot;&quot;)
ST_DFS(vtx, edge, 0, [False]*len(vtx))
print()

&amp;gt; 출력
ST_DFS_AM: ( U V ) ( V W ) ( W Y ) ( V X )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;2) BFS 너비 우선 탐색&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# BFS를 이용한 신장트리(인접리스트 방식)
from queue import Queue&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; #queue 모듈의 Queue 사용
def ST_BFS(vtx, aList, s):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n = len(vtx)&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;&amp;nbsp;&amp;nbsp;visited = [False]*n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 방문 확인을 위한 리스트
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q = Queue()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;q.put(s)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[s] = True
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while not q.empty():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;s = q.get()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in aList[s]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not visited[v]:
&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;&amp;nbsp;&amp;nbsp;print(&quot;(&quot;, vtx[s], vtx[v], &quot;)&quot;, end=' ')
&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;&amp;nbsp;&amp;nbsp;q.put(v)
&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;&amp;nbsp;&amp;nbsp;visited[v] = True

# BFS를 이용한 신장트리 테스트 프로그램
vtx = ['U', 'V', 'W', 'X', 'Y']
aList = [[1, 2],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0, 2, 3],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [0, 1, 4],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [1],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [2]]

print('ST_BFS_AM: ', end=&quot;&quot;)
ST_BFS(vtx, aList, 0)
print()

&amp;gt; 출력
ST_BFS_AM: ( U V ) ( U W ) ( V X ) ( W Y )&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;8-5 최소 비용 신장 트리 (Minimum spanning tree, MST)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;u&gt;최소 비용&lt;/u&gt; &lt;b&gt;신장 트리&lt;/b&gt;: &lt;b&gt;모든 정점들을&lt;/b&gt; 가장 &lt;b&gt;적은&lt;/b&gt; 수의 간선과 &lt;b&gt;비용&lt;/b&gt;으로 연결&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;크루스칼 MST 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 탐욕적인 방법 (Greedy method)&lt;br /&gt;- 각 단계에서 최선의 답을 선택하는 과정을 반복함으로써 최종적인 해답에 도달&lt;br /&gt;- n-1개의 간선이 생성될 때까지 수행&lt;br /&gt;&amp;rarr; &lt;b&gt;union - find&lt;/b&gt; 연산을 통해 loop / cycle 이 생기지 않도록 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1156&quot;&gt;&lt;a href=&quot;https://velog.io/@choiwsx/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%81%AC%EB%A3%A8%EC%8A%A4%EC%B9%BCKruskal-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWWT3O/btsKMLxUkAN/fYKgrIQR1m8Q2EjzNwRAcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWWT3O%2FbtsKMLxUkAN%2FfYKgrIQR1m8Q2EjzNwRAcK%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;700&quot; height=&quot;632&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;1156&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;프림 MST 알고리즘&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 시작 정점에서부터 출발하여 신장 트리 집합을 단계적으로 확장해나감&lt;br /&gt;- 신장 트리 집합에&amp;nbsp; 인접한 정점 중에서 최저 간선으로 연결된 정점을 선택하여 신장 트리 집합에 추가한다. (즉, 그 순간마다 짧은 것을 선택한다.)&lt;br /&gt;- 모든 정점이 삽입될 때까지 계속한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;707&quot;&gt;&lt;a href=&quot;https://velog.io/@gouz7514/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Prim-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5UksZ/btsKMnqoTIe/IkbC48YimzufTcD2O7KKjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5UksZ%2FbtsKMnqoTIe%2FIkbC48YimzufTcD2O7KKjK%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;649&quot; height=&quot;707&quot; data-origin-width=&quot;649&quot; data-origin-height=&quot;707&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 8.7: MST에 포함되지 않은 최소 dist의 정점 찾기
INF = 999
def getMinVertex(dist, selected):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;minv = 0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mindist = INF
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in range(len(dist)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if selected[v]==False and dist[v] &amp;lt; mindist:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mindist = dist[v]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;minv = v
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return minv
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
# 코드 8.8:프림의 최소 신장 트리 알고리즘
def MSTPrim(vertex, adj):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n = len(vertex)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dist = [INF]*n
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;selected = [False]*n
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dist[0] = 0

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(n):&amp;nbsp;&amp;nbsp;# n개의 정점을 MST에 추가하면 종료.
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;u = getMinVertex(dist, selected)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;selected[u] = True
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(vertex[u], end=' ')
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for v in range(n):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 간선 (u, v)가 있고, v가 MST에 없다면
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if adj[u][v] != 0 and not selected[v]:
&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;&amp;nbsp;&amp;nbsp;if adj[u][v] &amp;lt; dist[v]:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # (u, v)가 dist[v]보다 작으면
&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dist[v] = adj[u][v]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # dist[v] 갱신
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(': ', dist)&amp;nbsp;&amp;nbsp; # 중간 결과 출력
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print()

# Prim의 MST 테스트 프로그램
vertex =&amp;nbsp;&amp;nbsp; ['A',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'B',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'C',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'D',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'E',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'F',&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;'G']
weight = [ [0,	&amp;nbsp;&amp;nbsp; 25,		INF,	12,	&amp;nbsp;&amp;nbsp;INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; INF,		INF],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [25,		0,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;10,		INF,	15,	&amp;nbsp;&amp;nbsp; INF,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [INF,	10,		0,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,	INF,	16,		INF],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [12,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,	0,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;17,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,	37],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [INF,	15,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;17,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;14,		19],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [INF,	INF,	16,		INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;14,		0,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;42],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; [INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INF,	INF,&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;37,		19,		42,	&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;0]]&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;

print(&quot;MST By Prim's Algorithm&quot;)
MSTPrim(vertex, weight)

&amp;gt; 출력
MST By Prim's Algorithm
A :&amp;nbsp;&amp;nbsp;[0, 25, 999, 12, 999, 999, 999]
D :&amp;nbsp;&amp;nbsp;[0, 25, 999, 12, 17, 999, 37]
E :&amp;nbsp;&amp;nbsp;[0, 15, 999, 12, 17, 14, 19]
F :&amp;nbsp;&amp;nbsp;[0, 15, 16, 12, 17, 14, 19]
B :&amp;nbsp;&amp;nbsp;[0, 15, 10, 12, 17, 14, 19]
C :&amp;nbsp;&amp;nbsp;[0, 15, 10, 12, 17, 14, 19]
G :&amp;nbsp;&amp;nbsp;[0, 15, 10, 12, 17, 14, 19]&lt;/code&gt;&lt;/pre&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-origin-width=&quot;590&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YU1z1/btsKOFpeS7C/yJ0dpMkMEReSpZMJUZuRhK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YU1z1/btsKOFpeS7C/yJ0dpMkMEReSpZMJUZuRhK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YU1z1/btsKOFpeS7C/yJ0dpMkMEReSpZMJUZuRhK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYU1z1%2FbtsKOFpeS7C%2FyJ0dpMkMEReSpZMJUZuRhK%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;590&quot; height=&quot;518&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;최단 경로 구하기&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;다익스트라 알고리즘&amp;nbsp; (구현방법: 순차탐색 또는 우선순위 큐)&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;b&gt;하나의 시작 정점&lt;/b&gt;에서 &lt;b&gt;다른 모든 정점까지의 최단 경로&lt;/b&gt; 계산&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;집합 S: 시작 정점 v로부터 최단 경로가 &lt;b&gt;이미 발견된 정점&lt;/b&gt;들의 집합&lt;/li&gt;
&lt;li&gt;distance 배열: &lt;b&gt;최단 경로가 알려진 정점들만을 이용&lt;/b&gt;한 &lt;b&gt;다른 정점들까지의 최단 경로 길이 &amp;rarr; &lt;/b&gt;계속 변경된다.&lt;/li&gt;
&lt;li&gt;매 단계에서 가장 distance 값이 작은 정점을 S에 추가한다. (새로운 정점이 S에 추가되면 distance 값은 갱신된다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;775&quot;&gt;&lt;a href=&quot;https://melomance.github.io/2011/07/24/[AG][GA]%20Dijkstra/&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btlbef/btsKOAPNfB7/4HfXu7enllZoeDppnjzX80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbtlbef%2FbtsKOAPNfB7%2F4HfXu7enllZoeDppnjzX80%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;543&quot; data-origin-width=&quot;956&quot; data-origin-height=&quot;775&quot;/&gt;&lt;/a&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;color: #333333; text-align: start;&quot; data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #ffc9af;&quot;&gt;&lt;b&gt;플로이드 알고리즘&amp;nbsp;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- &lt;u&gt;&lt;b&gt;모든 정점에서&lt;/b&gt;&lt;/u&gt; &lt;b&gt;다른 모든 정점까지의 최단 경로&lt;/b&gt;를 계산&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1732029489853&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[알고리즘] 플로이드 워셜 알고리즘 (Floyd-Warshall Algorithm)&quot; data-og-description=&quot;지난 포스팅에서는 다익스트라 알고리즘에 대해 작성했었다. 다익스트라의 경우 한 지점에서 다른 특정 지점까지의 최단 경로를 구하는 알고리즘이다. 그러나 모든 지점에서 다른 모든 지점까&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@kimdukbae/%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Floyd-Warshall-Algorithm&quot; data-og-url=&quot;https://velog.io/@kimdukbae/플로이드-워셜-알고리즘-Floyd-Warshall-Algorithm&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/qOezX/hyXzNhTAph/IoG2oEhZyVKA4vASpjI8Kk/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100,https://scrap.kakaocdn.net/dn/oBHX3/hyXzHWgV61/n8TblUPEGUsdaTLFR4hcEk/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100,https://scrap.kakaocdn.net/dn/AjXKA/hyXzW0cUsM/gthuKP6iBCKXzTwQBicPQ1/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100&quot;&gt;&lt;a href=&quot;https://velog.io/@kimdukbae/%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Floyd-Warshall-Algorithm&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@kimdukbae/%ED%94%8C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%9B%8C%EC%85%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-Floyd-Warshall-Algorithm&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/qOezX/hyXzNhTAph/IoG2oEhZyVKA4vASpjI8Kk/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100,https://scrap.kakaocdn.net/dn/oBHX3/hyXzHWgV61/n8TblUPEGUsdaTLFR4hcEk/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100,https://scrap.kakaocdn.net/dn/AjXKA/hyXzW0cUsM/gthuKP6iBCKXzTwQBicPQ1/img.png?width=4000&amp;amp;height=2100&amp;amp;face=0_0_4000_2100');&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;[알고리즘] 플로이드 워셜 알고리즘 (Floyd-Warshall Algorithm)&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;velog.io&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;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;figure id=&quot;og_1732029527905&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조와 알고리즘 with Python] Chapter 8 : Graph&quot; data-og-description=&quot;Chapter: 그래프 (Graph) 그래프는 복잡하게 연결된 객체 사이의 관계를 표현할 수 있는 가장 자유로운 자료구조이다. 모든 선형 자료구조나 트리조차도 그래프로 나타낼 수 있어 그래프의 한 종류&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-8&quot; data-og-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-8&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dv4yTM/hyXzVGYj1S/BQoA4b4oDOWAUkZI3UMf1K/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/bc2LiK/hyXzS4w6U2/KKYC7YwqlQYsOfI5F1g9t0/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/ddN99Q/hyXzMcaBZH/MNoiVrSRrZfGo82WIJ6Oo1/img.png?width=381&amp;amp;height=253&amp;amp;face=0_0_381_253&quot;&gt;&lt;a href=&quot;https://velog.io/@mingming_eee/data-structure-with-python-8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dv4yTM/hyXzVGYj1S/BQoA4b4oDOWAUkZI3UMf1K/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/bc2LiK/hyXzS4w6U2/KKYC7YwqlQYsOfI5F1g9t0/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/ddN99Q/hyXzMcaBZH/MNoiVrSRrZfGo82WIJ6Oo1/img.png?width=381&amp;amp;height=253&amp;amp;face=0_0_381_253');&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;[자료구조와 알고리즘 with Python] Chapter 8 : Graph&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Chapter: 그래프 (Graph) 그래프는 복잡하게 연결된 객체 사이의 관계를 표현할 수 있는 가장 자유로운 자료구조이다. 모든 선형 자료구조나 트리조차도 그래프로 나타낼 수 있어 그래프의 한 종류&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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;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;&lt;b&gt;1916번 - 최소비용 구하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1707번 - 이분 그래프&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1697번 - 숨바꼭질&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1197번- 최소 스패닝 트리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1916 최소비용 구하기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;664&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9go5O/btsKOK5LiJi/Qk1q1WZwNUqdYFytJv85d1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9go5O/btsKOK5LiJi/Qk1q1WZwNUqdYFytJv85d1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9go5O/btsKOK5LiJi/Qk1q1WZwNUqdYFytJv85d1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9go5O%2FbtsKOK5LiJi%2FQk1q1WZwNUqdYFytJv85d1%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;660&quot; height=&quot;474&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;664&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;680&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A69th/btsKOlyEv72/IqsJWAk33f4Te0yHnZneV0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A69th/btsKOlyEv72/IqsJWAk33f4Te0yHnZneV0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A69th/btsKOlyEv72/IqsJWAk33f4Te0yHnZneV0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA69th%2FbtsKOlyEv72%2FIqsJWAk33f4Te0yHnZneV0%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;660&quot; height=&quot;486&quot; data-origin-width=&quot;924&quot; data-origin-height=&quot;680&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1732003712533&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys
import heapq
input = sys.stdin.readline
INF = int(1e9)

n = int(input()) #도시의 수 (노드)
m = int(input()) #버스의 수 (간선)

#그래프 입력받기 (초기화)
g = [[] for _ in range(n+1)]
for i in range(m):
    a, b, w = map(int, input().split())
    g[a].append((b, w))

#시작 도시와 도착 도시 (시작 정점, 끝 정점)
st, ed = map(int, input().split())

dist = [INF]*(n+1) #최소 비용 초기화 (무한)
def dijkstra(start):
    dist[start] = 0
    q = [(0, st)]

    while q:
        w, cur = heapq.heappop(q) #현재 큐에서 가장 작은 거리의 노드를 꺼냄
        if dist[cur] &amp;lt; w: #이미 처리되었다면 무시
            continue

        for dest, wei in g[cur]: #현재 노드 cur의 모든 인접 노드(dest)를 탐색
            cost = dist[cur] + wei #cost: 현재 노드까지의 거리 + 해당 간선의 비용
            if dist[dest] &amp;gt; cost: #만약 cost가 dist[dest]보다 작으면
                dist[dest] = cost #dist[dest] 업데이트
                heapq.heappush(q, (cost, dest)) #q에 (cost, dest) 추가

dijkstra(st)
print(dist[ed])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1732029569190&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[백준] 1916 최소비용 구하기 파이썬&quot; data-og-description=&quot;문제 https://www.acmicpc.net/problem/1916 1916번: 최소비용 구하기 첫째 줄에 도시의 개수 N(1 &amp;le; N &amp;le; 1,000)이 주어지고 둘째 줄에는 버스의 개수 M(1 &amp;le; M &amp;le; 100,000)이 주어진다. 그리고 셋째 줄부터 M+2줄까&quot; data-og-host=&quot;cme10575.tistory.com&quot; data-og-source-url=&quot;https://cme10575.tistory.com/118&quot; data-og-url=&quot;https://cme10575.tistory.com/118&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/oLPAR/hyXzRdyl9z/QgsCjnTIbRSMOINqOiowb0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/i0G1O/hyXzOHQX4D/PkgikF104M9AQh0ty66P4k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cuxxIh/hyXzTCn2wr/QvIgpf2cq6Swx47lX3YwI0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://cme10575.tistory.com/118&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://cme10575.tistory.com/118&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/oLPAR/hyXzRdyl9z/QgsCjnTIbRSMOINqOiowb0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/i0G1O/hyXzOHQX4D/PkgikF104M9AQh0ty66P4k/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cuxxIh/hyXzTCn2wr/QvIgpf2cq6Swx47lX3YwI0/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&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;[백준] 1916 최소비용 구하기 파이썬&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제 https://www.acmicpc.net/problem/1916 1916번: 최소비용 구하기 첫째 줄에 도시의 개수 N(1 &amp;le; N &amp;le; 1,000)이 주어지고 둘째 줄에는 버스의 개수 M(1 &amp;le; M &amp;le; 100,000)이 주어진다. 그리고 셋째 줄부터 M+2줄까&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;cme10575.tistory.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>책/자료구조와알고리즘with파이썬</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/71</guid>
      <comments>https://youryoung.tistory.com/71#entry71comment</comments>
      <pubDate>Tue, 19 Nov 2024 11:35:58 +0900</pubDate>
    </item>
    <item>
      <title>BOJ 1260, 1920, 11724, 2343</title>
      <link>https://youryoung.tistory.com/67</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1260 DFS와 BFS&lt;/b&gt;&lt;br&gt;1920 수 찾기&lt;br&gt;11724 연결 요소의 개수&lt;br&gt;2343 기타 레슨&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1260 DFS와 BFS&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방문할 수 있는 점이 없는 경우 종료한다.&amp;nbsp;정점 번호는 1번부터 N번까지이다.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;첫째 줄에 정점의 개수 N(1 ≤ N ≤ 1,000), 간선의 개수 M(1 ≤ M ≤ 10,000), 탐색을 시작할 정점의 번호 V가 주어진다. 다음 M개의 줄에는 간선이 연결하는 두 정점의 번호가 주어진다. 어떤 두 정점 사이에 여러 개의 간선이 있을 수 있다. 입력으로 주어지는 간선은 양방향이다.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;첫째 줄에 DFS를 수행한 결과를, 그 다음 줄에는 BFS를 수행한 결과를 출력한다. V부터 방문된 점을 순서대로 출력하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;449&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfHEDS/btsKFhJ72tq/RukF3TGHK7BcFvOCGnuQzK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfHEDS/btsKFhJ72tq/RukF3TGHK7BcFvOCGnuQzK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfHEDS/btsKFhJ72tq/RukF3TGHK7BcFvOCGnuQzK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfHEDS%2FbtsKFhJ72tq%2FRukF3TGHK7BcFvOCGnuQzK%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;686&quot; height=&quot;449&quot; data-origin-width=&quot;686&quot; data-origin-height=&quot;449&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;br&gt;&lt;b&gt;DFS(Depth First Search) :&amp;nbsp;&lt;/b&gt;Source node 에서 멀어지는 방향으로 갈 수 있을 때까지 가다가 더 이상 갈 수 없게 되면 &quot;가장 가까운 갈림길로 돌아와서(backtracking, 최근에 처리한 --&amp;gt; &lt;b&gt;stack&lt;/b&gt;이 적합)&quot; 그곳으로부터 미탐색한 다른 방향으로 재진행&lt;br&gt;&lt;b&gt;BFS(Breath First Search) :&amp;nbsp;Source node로 부터 가까운 노드들을 먼저 방문&lt;/b&gt;함. (가까운 노드들이 먼저 방문되고 그 노드들의 인접노드가 멀리 있는 노드들의 인접노드 보다 먼저 방문되야함. 즉 먼저(first) 처리(in)된 것 부터(first) 꺼내서(out) 처리해야하므로 &lt;b&gt;queue&lt;/b&gt;가 적합); 트리 자료구조에서 root를 source node로 한 Level-order traversal과 유사함.&lt;/blockquote&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;590&quot;&gt;&lt;a href=&quot;https://blog.kakaocdn.net/dn/uWdnm/btsIdPV7Jti/GHZdoKhWn51yyJIKU1X7h0/img.png&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c3ScjS/btsKDXrJrg0/Nn6awE93vkivBKfbQDfkwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc3ScjS%2FbtsKDXrJrg0%2FNn6awE93vkivBKfbQDfkwK%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;1188&quot; height=&quot;590&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/a&gt;&lt;/figure&gt;
&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;cpp&quot; data-ke-language=&quot;cpp&quot;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdbool.h&amp;gt;
#pragma warning(disable:4996)

int visited[1001] = { 0, }; // 방문 표시 초기화 (false)
int graph[1001][1001] = { 0, }; // 그래프 초기화 (false)
int queue[1001]; 

void dfs(int v,int n) { // 깊이 우선 탐색
	visited[v] = true; //이제 방문했기 때문에 true로 바꾼다. (더이상 0이 아님)
	printf(&quot;%d &quot;, v);
	for (int i = 1; i &amp;lt;= n; i++) {
		if (graph[v][i] &amp;amp;&amp;amp; !visited[i]) // 그래프에 방문하지 않았고 간선 존재
			dfs(i, n);
	}
}

void bfs(int v, int n) { // 너비 우선 탐색
	/* 큐 변수 선언 */
	int front = 0;
	int rear = 1;
	int pop;

	visited[v] = true;
	printf(&quot;%d &quot;, v);
	queue[0] = v;
	while (front &amp;lt; rear) { // 큐가 비어있으면 반복 종료 
		pop = queue[front++]; // dequeue 연산 (맨 앞, 가장 먼저 들어온 값을 출력한다.)
		for (int i = 1; i &amp;lt;= n; i++){
			if (graph[pop][i] &amp;amp;&amp;amp; !visited[i]) {
				visited[i] = true;
				printf(&quot;%d &quot;, i);
				queue[rear++] = i; // enqueue 연산
			}
		}
	}
}

int main() {
	int n,m, v;

	scanf(&quot;%d%d%d&quot;, &amp;amp;n, &amp;amp;m, &amp;amp;v);

	//그래프 생성
	for (int i = 0; i &amp;lt; m; i++) {
		int x, y;
		scanf(&quot;%d%d&quot;, &amp;amp;x, &amp;amp;y);
		graph[x][y] = 1; // 양방향 그래프
		graph[y][x] = 1;
	}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
	//깊이 우선 탐색
	visited[v] = true;
	dfs(v,n);
	//--------------------------------------------------
	for (int i = 1; i &amp;lt;= n; i++) // 방문 노드 초기화
			visited[i] = false;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;//깊이 우선 탐색&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
	visited[v] = true;
	printf(&quot;\n&quot;);
	bfs(v, n);
	return 0;
}&lt;/code&gt;&lt;/pre&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import sys
from collections import deque
input = sys.stdin.readline

# dfs 함수 정의(재귀)
def dfs(graph, v, visited):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[v] = True # 방문 처리
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(v, end=' ') # 현재 노드 출력
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in sorted(graph[v]): # 오름차순으로 이웃한 노드
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not visited[i]: # 아직 방문하지 않은 노드가 있다면 방문
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dfs(graph, i, visited)

# bfs 함수 정의
def bfs(graph, start, visited):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;queue = deque([start]) # 큐에 현재 노드 삽입
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[start] = True # 방문 처리
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while queue: # 큐가 빌때 동안
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;v = queue.popleft()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(v, end=' ') # 현재 노드 출력
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in sorted(graph[v]): # 오름차순으로 이웃한 노드
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if not visited[i]: # 아직 방문하지 않았다면
&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;&amp;nbsp;&amp;nbsp;queue.append(i) # 큐에 삽입
&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;&amp;nbsp;&amp;nbsp;visited[i] = True # 방문처리

n, m, start = map(int, input().rstrip().split()) # 정점, 간선, 시작정점

graph = [[] for j in range(n+1)] # 그려질 그래프 초기화
for k in range(m):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;a, b = map(int, input().rstrip().split()) # 이웃한 정점 연결시킴
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;graph[a].append(b)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;graph[b].append(a)


visited = [False] * (n+1)
# dfs 함수 실행
dfs(graph, start, visited)

print()

visited = [False] * (n+1)
# bfs 함수 실행
bfs(graph, start, visited)&lt;/code&gt;&lt;/pre&gt;&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;&lt;div class=&quot;moreless-content&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;from&amp;nbsp;collections&amp;nbsp;import&amp;nbsp;deque &lt;br&gt;&lt;br&gt;#&amp;nbsp;DFS&amp;nbsp;함수&amp;nbsp;정의,&amp;nbsp;깊이&amp;nbsp;우선&amp;nbsp;탐색 &lt;br&gt;def&amp;nbsp;dfs(v,&amp;nbsp;visited,&amp;nbsp;graph): &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[v]&amp;nbsp;=&amp;nbsp;True &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(v,&amp;nbsp;end='&amp;nbsp;') &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;neighbor&amp;nbsp;in&amp;nbsp;graph[v]: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;visited[neighbor]: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;dfs(neighbor,&amp;nbsp;visited,&amp;nbsp;graph) &lt;br&gt;&lt;br&gt;#&amp;nbsp;BFS&amp;nbsp;함수&amp;nbsp;정의,&amp;nbsp;너비&amp;nbsp;우선&amp;nbsp;탐색 &lt;br&gt;def&amp;nbsp;bfs(start,&amp;nbsp;graph): &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited&amp;nbsp;=&amp;nbsp;[False]&amp;nbsp;*&amp;nbsp;(len(graph)) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;queue&amp;nbsp;=&amp;nbsp;deque([start]) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;visited[start]&amp;nbsp;=&amp;nbsp;True &lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;queue: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;v&amp;nbsp;=&amp;nbsp;queue.popleft() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(v,&amp;nbsp;end='&amp;nbsp;') &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;neighbor&amp;nbsp;in&amp;nbsp;graph[v]: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;visited[neighbor]: &lt;br&gt;&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;&amp;nbsp;&amp;nbsp;queue.append(neighbor) &lt;br&gt;&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;&amp;nbsp;&amp;nbsp;visited[neighbor]&amp;nbsp;=&amp;nbsp;True &lt;br&gt;&lt;br&gt;#&amp;nbsp;입력&amp;nbsp;받기 &lt;br&gt;N,&amp;nbsp;M,&amp;nbsp;V&amp;nbsp;=&amp;nbsp;map(int,&amp;nbsp;input().split())&amp;nbsp;&amp;nbsp;#&amp;nbsp;정점&amp;nbsp;수,&amp;nbsp;간선&amp;nbsp;수,&amp;nbsp;시작&amp;nbsp;정점 &lt;br&gt;graph&amp;nbsp;=&amp;nbsp;[[]&amp;nbsp;for&amp;nbsp;_&amp;nbsp;in&amp;nbsp;range(N&amp;nbsp;+&amp;nbsp;1)] &lt;br&gt;&lt;br&gt;#&amp;nbsp;간선&amp;nbsp;정보&amp;nbsp;입력&amp;nbsp;받기 &lt;br&gt;for&amp;nbsp;_&amp;nbsp;in&amp;nbsp;range(M): &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;u,&amp;nbsp;v&amp;nbsp;=&amp;nbsp;map(int,&amp;nbsp;input().split()) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#양방향&amp;nbsp;그래프이기&amp;nbsp;때문에 &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;graph[u].append(v) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;graph[v].append(u) &lt;br&gt;&lt;br&gt;#&amp;nbsp;각&amp;nbsp;정점의&amp;nbsp;인접&amp;nbsp;리스트를&amp;nbsp;정렬 &lt;br&gt;for&amp;nbsp;edges&amp;nbsp;in&amp;nbsp;graph: &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;edges.sort() &lt;br&gt;&lt;br&gt;#&amp;nbsp;DFS와&amp;nbsp;BFS&amp;nbsp;결과&amp;nbsp;출력 &lt;br&gt;visited_dfs&amp;nbsp;=&amp;nbsp;[False]&amp;nbsp;*&amp;nbsp;(N&amp;nbsp;+&amp;nbsp;1) &lt;br&gt;dfs(V,&amp;nbsp;visited_dfs,&amp;nbsp;graph) &lt;br&gt;print()&amp;nbsp;&amp;nbsp;#&amp;nbsp;줄바꿈 &lt;br&gt;bfs(V,&amp;nbsp;graph)&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[백준] 1260번: DFS와 BFS - 파이썬&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;  문제 그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방&quot; data-og-host=&quot;lazypazy.tistory.com&quot; data-og-source-url=&quot;https://lazypazy.tistory.com/132&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lQAdc/hyXwoIZBop/bbfgQG80kI0VWLKU16UeI0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cJo50K/hyXwj8JiMk/f8szh0YRA8kpW1IyLXMkLK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/CUDCx/hyXwmj7m4P/kriKEl423qKcjGPWIDYmKK/img.jpg?width=780&amp;amp;height=780&amp;amp;face=0_0_780_780&quot; data-og-url=&quot;https://lazypazy.tistory.com/132&quot;&gt;&lt;a href=&quot;https://lazypazy.tistory.com/132&quot; target=&quot;_blank&quot; data-source-url=&quot;https://lazypazy.tistory.com/132&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lQAdc/hyXwoIZBop/bbfgQG80kI0VWLKU16UeI0/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/cJo50K/hyXwj8JiMk/f8szh0YRA8kpW1IyLXMkLK/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/CUDCx/hyXwmj7m4P/kriKEl423qKcjGPWIDYmKK/img.jpg?width=780&amp;amp;height=780&amp;amp;face=0_0_780_780')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;[백준] 1260번: DFS와 BFS - 파이썬&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;  문제 그래프를 DFS로 탐색한 결과와 BFS로 탐색한 결과를 출력하는 프로그램을 작성하시오. 단, 방문할 수 있는 정점이 여러 개인 경우에는 정점 번호가 작은 것을 먼저 방문하고, 더 이상 방&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;lazypazy.tistory.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1920 수 찾기&lt;/b&gt;&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;N개의 정수 A[1], A[2], …, A[N]이 주어져 있을 때, 이 안에 X라는 정수가 존재하는지 알아내는 프로그램을 작성하시오.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;첫째 줄에 자연수 N(1 ≤ N ≤ 100,000)이 주어진다. 다음 줄에는 N개의 정수 A[1], A[2], …, A[N]이 주어진다. 다음 줄에는 M(1 ≤ M ≤ 100,000)이 주어진다. 다음 줄에는 M개의 수들이 주어지는데, 이 수들이 A안에 존재하는지 알아내면 된다. 모든 정수의 범위는 -2&lt;/span&gt;&lt;/span&gt;31&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;보다 크거나 같고 2&lt;/span&gt;&lt;/span&gt;31&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;보다 작다.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&amp;nbsp;&lt;br&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;M개의 줄에 답을 출력한다. 존재하면 1을, 존재하지 않으면 0을 출력한다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;[순차 탐색은 시간초과가 나온다.]&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 순차 탐색으로 구현
def sequential_search(n, target, array):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 각 원소를 하나씩 확인하며
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(n):
&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;&amp;nbsp;&amp;nbsp;if array[i] == target:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 1 # 찾으면 1 반환
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0 #찾지 못하면 0 반환
#N
N = int(input())
# 생성된 리스트
array = list(map(int, input().split()))
# 배열 길이가 N과 일치하는지 확인
if len(array) != N:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise ValueError(f&quot;Expected {N} elements for the main array, but got {len(array)} elements.&quot;)

#M
M = int(input())
# 확인할 리스트
checkArray = list(map(int, input().split()))
# 배열 길이가 M과 일치하는지 확인
if len(checkArray) != M:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise ValueError(f&quot;Expected {M} elements for the check array, but got {len(checkArray)} elements.&quot;)

# checkArray의 각 요소에 대해 sequential_search 실행
for target in checkArray:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(sequential_search(N, target, array))&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;[이진탐색으로 해야 시간초과가 나오지 않는다.]&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def binary_search(target, array):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;low, high = 0, len(array) - 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while low &amp;lt;= high:&amp;nbsp;&amp;nbsp;# 검색해야 할 레코드가 있는 경우
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;middle = (low + high) // 2&amp;nbsp;&amp;nbsp;# middle 계산
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if target == array[middle]:&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;return 1&amp;nbsp;&amp;nbsp;# 1 출력
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif target &amp;lt; array[middle]:&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;high = middle - 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&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;low = middle + 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return 0&amp;nbsp;&amp;nbsp;# 탐색 실패 -&amp;gt; 0 출력

# N 입력 및 배열 생성
N = int(input())
array = list(map(int, input().split()))
array.sort()&amp;nbsp;&amp;nbsp;# 이진 탐색을 위해 배열을 정렬

# M 입력 및 확인할 리스트 생성
M = int(input())
checkArray = list(map(int, input().split()))

# checkArray의 각 요소에 대해 이진 탐색 실행
for target in checkArray:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(binary_search(target, array))&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>문제풀이</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/67</guid>
      <comments>https://youryoung.tistory.com/67#entry67comment</comments>
      <pubDate>Tue, 12 Nov 2024 12:01:20 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조와알고리즘with파이썬] ch.7-4 이진 탐색 트리</title>
      <link>https://youryoung.tistory.com/66</link>
      <description>&lt;h3 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;리스트 안에 있는 특정 데이터를 찾기 위해 앞에서부터 데이터를 하나씩 차례대로 확인하는 방법&lt;br /&gt;&amp;rarr; 데이터의 개수가 N개일 때 최대 N번의 비교 연산이 필요하다. &lt;span style=&quot;color: #333333;&quot;&gt;&amp;rarr;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt; 시간복잡도는 O(N)&lt;/span&gt;&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: 25%; text-align: center;&quot;&gt;하늘&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;바다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;바나나&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;포도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;확인&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: 25%; text-align: center;&quot;&gt;하늘&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;바다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;바나나&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;포도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;확인&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: 25%; text-align: center;&quot;&gt;하늘&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;바다&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&lt;b&gt;바나나&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;포도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;확인, 성공&lt;/td&gt;
&lt;td style=&quot;width: 25%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 순차 탐색 구현
def sequential_search(n, target, array):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 각 원소를 하나씩 확인하며
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for i in range(n):
&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;&amp;nbsp;&amp;nbsp;if array[i] == target:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return i + 1 # 현재의 위치 반환(인덱스는 0부터 시작하므로)

# 탐색하고자 하는 리스트
fruit = [&quot;apple&quot;, &quot;banana&quot;, &quot;orange&quot;, &quot;grape&quot;, &quot;mango&quot;]
# 리스트의 길이
len_fruit = len(fruit)

print(sequential_search(len_fruit, &quot;orange&quot;, fruit))&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;참고코드&quot; href=&quot;https://velog.io/@changhee09/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%83%90%EC%83%89-%EC%88%9C%EC%B0%A8-%ED%83%90%EC%83%89-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span&gt;https://velog.io/@changhee09/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%83%90%EC%83%89-%EC%88%9C%EC%B0%A8-%ED%83%90%EC%83%89-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[알고리즘] 탐색 - 순차 탐색, 이진 탐색&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;알고리즘 - 탐색(순차 탐색, 이진 탐색)&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@changhee09/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%83%90%EC%83%89-%EC%88%9C%EC%B0%A8-%ED%83%90%EC%83%89-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b4oUiO/hyXwmc3pDb/K7nLW7puQGCVsFxsACCEpK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bhtxPU/hyXwovbNkD/WPR9E3Z72kk9s0wkDPts60/img.png?width=1152&amp;amp;height=300&amp;amp;face=0_0_1152_300,https://scrap.kakaocdn.net/dn/b2xa5Y/hyXwi2ZTaI/i3PmAmrlmKmN84BOam7i81/img.png?width=1152&amp;amp;height=300&amp;amp;face=0_0_1152_300&quot; data-og-url=&quot;https://velog.io/@changhee09/알고리즘-탐색-순차-탐색-이진-탐색&quot;&gt;&lt;a href=&quot;https://velog.io/@changhee09/알고리즘-탐색-순차-탐색-이진-탐색&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@changhee09/%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%ED%83%90%EC%83%89-%EC%88%9C%EC%B0%A8-%ED%83%90%EC%83%89-%EC%9D%B4%EC%A7%84-%ED%83%90%EC%83%89&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b4oUiO/hyXwmc3pDb/K7nLW7puQGCVsFxsACCEpK/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500,https://scrap.kakaocdn.net/dn/bhtxPU/hyXwovbNkD/WPR9E3Z72kk9s0wkDPts60/img.png?width=1152&amp;amp;height=300&amp;amp;face=0_0_1152_300,https://scrap.kakaocdn.net/dn/b2xa5Y/hyXwi2ZTaI/i3PmAmrlmKmN84BOam7i81/img.png?width=1152&amp;amp;height=300&amp;amp;face=0_0_1152_300');&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;[알고리즘] 탐색 - 순차 탐색, 이진 탐색&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;velog.io&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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 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;- 이미 정렬되어 있는 데이터에서 특정한 값을 찾아내는 알고리즘&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 시간복잡도: O(log n)&amp;nbsp; ∵ 한 번 확인할 때마다 확인해야 하는 원소의 개수가 절반씩 줄어든다.&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 시작점, 끝점, 중간점 &amp;rarr;&amp;nbsp; 찾으려는 데이터와 중간점의 값을 비교&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;- 삽입과 삭제가 빈번한 곳에서는 사용하기 어렵다.&lt;/span&gt;&lt;/span&gt;&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: 50%; text-align: center;&quot;&gt;찾으려는 데이터 &amp;lt; 중간점&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;왼쪽 탐색 (끝점을 중간점 이전으로 옮긴다.)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;찾으려는 데이터 &amp;gt; 중간점&lt;/td&gt;
&lt;td style=&quot;width: 50%; text-align: center;&quot;&gt;오른쪽 탐색 (시작점을 중간점 이후로 옮긴다.)&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;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;시작점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;중간점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;끝점&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;시작점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;중간점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;끝점&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;시작점,&lt;br /&gt;중간점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;끝점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;0&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;3&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;5&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;6&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;7&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;시작점,&lt;br /&gt;끝점,&lt;br /&gt;중간점&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 10%; height: 17px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[반복문 사용]&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def binary_search_iter(A, key, low, high):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (low &amp;lt;= high):&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;middle = (low + high) // 2&amp;nbsp;&amp;nbsp;# middle 계산
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if key == A[middle]:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 탐색 성공 O(1)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return middle&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif (key &amp;lt; A[middle]):&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 왼쪽 부분 리스트 (low ~ middle-1) 탐색
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;high = middle - 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 오른쪽 부분 리스트 (middle+1 ~ high) 탐색
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;low = middle + 1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return -1&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 탐색 실패&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[재귀 사용]&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;def binary_search(A, key, low, high):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if (low &amp;lt;= high):&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; # 검색해야 할 레코드가 있는 경우
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;middle = (low + high) // 2&amp;nbsp;&amp;nbsp;# middle 계산
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if key == A[middle]:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 탐색 성공 O(1)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return middle&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif (key &amp;lt; A[middle]):&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;&amp;nbsp;&amp;nbsp;return binary_search(A, key, low, middle - 1)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:&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;&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return binary_search(A, key, middle + 1, high)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return -1&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;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 탐색 실패&lt;/code&gt;&lt;/pre&gt;
&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;[자료구조와 알고리즘 with Python] Chapter 7 : Search&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Chapter 7: 탐색 (Search) 이번 Chapter에서는 기본적인 탐색 알고리즘들과 함께 이진 트리를 이용한 탐색 방법에 대해 알아보자. 01 &amp;quot;탐색이란?&amp;quot; 탐색이란? 데이터의 집합에서 원하는 조건을 만족하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nQmeh/hyXwtcrsYv/H1ySj9KGszOHRL6e4sCX40/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/xSIwK/hyXwi9MvZl/IkuDGawzCHk71y2Tuwsm6k/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/dgYyaH/hyXwoCckPX/OMfzKPgC8CJYVXu83JDob1/img.png?width=851&amp;amp;height=555&amp;amp;face=0_0_851_555&quot; data-og-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot;&gt;&lt;a href=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nQmeh/hyXwtcrsYv/H1ySj9KGszOHRL6e4sCX40/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/xSIwK/hyXwi9MvZl/IkuDGawzCHk71y2Tuwsm6k/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/dgYyaH/hyXwoCckPX/OMfzKPgC8CJYVXu83JDob1/img.png?width=851&amp;amp;height=555&amp;amp;face=0_0_851_555');&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;[자료구조와 알고리즘 with Python] Chapter 7 : Search&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Chapter 7: 탐색 (Search) 이번 Chapter에서는 기본적인 탐색 알고리즘들과 함께 이진 트리를 이용한 탐색 방법에 대해 알아보자. 01 &quot;탐색이란?&quot; 탐색이란? 데이터의 집합에서 원하는 조건을 만족하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&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;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;이진 탐색 트리 (Binary Search Tree)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 효율적인 데이터의 삽입과 삭제 가능&lt;br /&gt;: 왼쪽 자식노드 == 작은 값, 오른쪽 자식 노드 == 큰 값&lt;br /&gt;: 탐색을 위한 키(key) 와 나머지 데이터 부분(value)&lt;br /&gt;: 왼쪽과 오른쪽 서브트리도 이진 탐색 트리임&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.5: 이진 탐색 트리를 위한 노드 클래스
class BSTNode:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def __init__(self, key, value):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.key = key&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 키(key)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.value = value&amp;nbsp;&amp;nbsp;# 값(value)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.left = None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.right = None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[1. key를 이용한 탐색 (순환, 반복)]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sGaBc/btsKEOAPsm8/kvkEh0d6qGqe27JK3eZ1ck/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sGaBc/btsKEOAPsm8/kvkEh0d6qGqe27JK3eZ1ck/img.webp&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sGaBc/btsKEOAPsm8/kvkEh0d6qGqe27JK3eZ1ck/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsGaBc%2FbtsKEOAPsm8%2FkvkEh0d6qGqe27JK3eZ1ck%2Fimg.webp&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;400&quot; height=&quot;554&quot; data-origin-width=&quot;1000&quot; data-origin-height=&quot;1385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.6: 이진 탐색 트리의 탐색 연산(순환 구조)
def search_bst(n, key):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if n == None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif key == n.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 탐색 성공
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif key &amp;lt; n.key: #주어진 키 값이 루트 노드의 키값(n.key)보다 작은 경우
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return search_bst(n.left, key)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 왼쪽 서브트리에서 탐색
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else: #주어진 키 값이 루트 노드의 키값(n.key)보다 큰 경우
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return search_bst(n.right, key)&amp;nbsp;&amp;nbsp; # 오른쪽 서브트리에서 탐색&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.6.1: 이진 탐색 트리의 탐색 연산(반복 구조)
# key: 찾으려는 키값, n: 현재 노드
def search_bst_iter(n, key):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while n != None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if key == n.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 탐색 성공
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif key &amp;lt; n.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n = n.left&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;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;n = n.right&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 오른쪽 서브트리에서 탐색
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return None&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[2. value를 이용한 탐색 (전위 순회)]&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.7: 이진 탐색 트리의 값을 이용한 탐색(전위순회)
def search_value_bst(n, value):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if n == None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif value == n.value:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return n&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 탐색 성공
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res = search_value_bst(n.left, value)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if res is not None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return res&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 왼쪽 서브트리에서 탐색
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return search_value_bst(n.right, value)&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 오른쪽 서브트리에서 탐색&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[이진 탐색 트리 삽입]&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ls8MP/btsKGjzzKQY/F3A3X0ZKtVXdkqrKjkoET0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ls8MP/btsKGjzzKQY/F3A3X0ZKtVXdkqrKjkoET0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ls8MP/btsKGjzzKQY/F3A3X0ZKtVXdkqrKjkoET0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fls8MP%2FbtsKGjzzKQY%2FF3A3X0ZKtVXdkqrKjkoET0%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;588&quot; height=&quot;272&quot; data-origin-width=&quot;588&quot; data-origin-height=&quot;272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.8: 이진 탐색 트리의 삽입 연산
def insert_bst(root, node):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if root == None:&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# 공백 노드에 도달하면, 이 위치에 삽입 (탐색에 성공하지 않아야 한다.)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return node&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # node를 반환
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if node.key == root.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; # 삽입 실패, root를 반환

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# root의 서브 트리에 node 삽입
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if node.key &amp;lt; root.key: #루트 노드보다 작으면 왼쪽에
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.left = insert_bst(root.left, node)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else: #루트 노드보다 크면 오른쪽에
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.right = insert_bst(root.right, node)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br /&gt;&lt;b&gt;[이진 탐색 트리 삭제]&lt;/b&gt;&lt;br /&gt;노드 탐색 후 &amp;rarr;&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: 100%;&quot;&gt;1) 삭제하려는 노드가 단말노드인 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;2) 하나의 왼쪽이나 오른쪽 서브 트리 중 하나만 가지고 있는 경우 (자식 하나)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;3) 2개의 자신을 가진 경우&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&amp;rarr; 3) 삭제할 노드의 왼쪽 서브 트리에서 가장 큰 노드 or &lt;b&gt;삭&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;color: #212529;&quot;&gt;&lt;b&gt;제할 노드의 오른쪽 서브 트리에서 가장 작은 노드&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;&lt;b&gt; &lt;/b&gt;&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;pre class=&quot;python&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 코드 7.9: 이진 탐색 트리의 삭제 연산
def delete_bst(root, key):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if root == None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if key &amp;lt; root.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.left = delete_bst(root.left, key)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif key &amp;gt; root.key:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.right = delete_bst(root.right, key)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# key가 루트의 키와 같으면 root를 삭제
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# Case 1 (단말 노드) or Case 2 (오른쪽 자식만 있는 경우)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if root.left == None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root.right
&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;&amp;nbsp;&amp;nbsp;# Case 2 (왼쪽 자식만 있는 경우)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif root.right == None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root.left
&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;&amp;nbsp;&amp;nbsp;# Case 3 (두 자식이 모두 있는 경우)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;# succ: 후계자 노드
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;succ = root.right
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while succ.left != None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;succ = succ.left
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.key = succ.key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.value = succ.value
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;root.right = delete_bst(root.right, succ.key)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return root&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/u4SPx/btsKEOU9SLV/0nQdOWK9xlLDv9YQTnVmj0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/u4SPx/btsKEOU9SLV/0nQdOWK9xlLDv9YQTnVmj0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/u4SPx/btsKEOU9SLV/0nQdOWK9xlLDv9YQTnVmj0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fu4SPx%2FbtsKEOU9SLV%2F0nQdOWK9xlLDv9YQTnVmj0%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;599&quot; height=&quot;496&quot; data-origin-width=&quot;599&quot; data-origin-height=&quot;496&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;figure id=&quot;og_1731378662310&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[자료구조와 알고리즘 with Python] Chapter 7 : Search&quot; data-og-description=&quot;Chapter 7: 탐색 (Search) 이번 Chapter에서는 기본적인 탐색 알고리즘들과 함께 이진 트리를 이용한 탐색 방법에 대해 알아보자. 01 &amp;quot;탐색이란?&amp;quot; 탐색이란? 데이터의 집합에서 원하는 조건을 만족하는 &quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot; data-og-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/nQmeh/hyXwtcrsYv/H1ySj9KGszOHRL6e4sCX40/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/xSIwK/hyXwi9MvZl/IkuDGawzCHk71y2Tuwsm6k/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/dgYyaH/hyXwoCckPX/OMfzKPgC8CJYVXu83JDob1/img.png?width=851&amp;amp;height=555&amp;amp;face=0_0_851_555&quot;&gt;&lt;a href=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@mingming_eee/data-structure-with-python-7&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/nQmeh/hyXwtcrsYv/H1ySj9KGszOHRL6e4sCX40/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/xSIwK/hyXwi9MvZl/IkuDGawzCHk71y2Tuwsm6k/img.jpg?width=1858&amp;amp;height=1014&amp;amp;face=0_0_1858_1014,https://scrap.kakaocdn.net/dn/dgYyaH/hyXwoCckPX/OMfzKPgC8CJYVXu83JDob1/img.png?width=851&amp;amp;height=555&amp;amp;face=0_0_851_555');&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;[자료구조와 알고리즘 with Python] Chapter 7 : Search&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Chapter 7: 탐색 (Search) 이번 Chapter에서는 기본적인 탐색 알고리즘들과 함께 이진 트리를 이용한 탐색 방법에 대해 알아보자. 01 &quot;탐색이란?&quot; 탐색이란? 데이터의 집합에서 원하는 조건을 만족하는&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>책/자료구조와알고리즘with파이썬</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/66</guid>
      <comments>https://youryoung.tistory.com/66#entry66comment</comments>
      <pubDate>Tue, 12 Nov 2024 01:29:00 +0900</pubDate>
    </item>
    <item>
      <title>[자료구조와알고리즘with파이썬] ch.6 정렬</title>
      <link>https://youryoung.tistory.com/58</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;책에 나와있는 정렬 중에서도 가장 어려웠던 선택정렬에 대해서 설명해보고자 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선택정렬&lt;br /&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;lt;선택정렬 예시&amp;gt;&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: 20%; text-align: center;&quot;&gt;29&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;37&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;현재 위치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;가장 작은 값&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: 20%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;29&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;37&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;현재 위치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;가장 작은 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: 20%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;37&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;현재 위치, &lt;br /&gt;가장 작은 값&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&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: 20%; text-align: center;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;37&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;29&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;현재 위치&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center;&quot;&gt;가장 작은 값&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 34px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;10&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;13&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;14&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;29&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;37&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; text-align: center; height: 17px;&quot;&gt;종료&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;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2751 수 정렬하기2&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;자바에서 sort함수를 사용해 오름차순으로 정리&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730773837214&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        StringBuilder sb = new StringBuilder();
        int N = sc.nextInt();
        ArrayList&amp;lt;Integer&amp;gt; list = new ArrayList&amp;lt;&amp;gt;();
        
        for (int i = 0; i &amp;lt; N; i++) {
            list.add(sc.nextInt()); // 하나씩 list에 저장
        }
        
        Collections.sort(list); // 오름차순으로 정리
        
        for (int value : list) {
            sb.append(value).append('\n');
        }
        
        System.out.println(sb);
    }
}&lt;/code&gt;&lt;/pre&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: 100%;&quot;&gt;StringBuilder 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;a title=&quot;StringBuilder 사용&quot; href=&quot;https://da2uns2.tistory.com/entry/Java-StringBuilder-%EC%82%AC%EC%9A%A9%EB%B2%95%EA%B3%BC-%EC%A3%BC%EC%9A%94-%EB%A9%94%EC%86%8C%EB%93%9C&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://da2uns2.tistory.com/entry/Java-StringBuilder-%EC%82%AC%EC%9A%A9%EB%B2%95%EA%B3%BC-%EC%A3%BC%EC%9A%94-%EB%A9%94%EC%86%8C%EB%93%9C&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;ArrayList 사용&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;a href=&quot;https://psychoria.tistory.com/765&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://psychoria.tistory.com/765&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;Arrays.sort (ArrayList는 Collections.sort()를 사용해야 했음)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 100%;&quot;&gt;&lt;a href=&quot;https://codingnojam.tistory.com/38&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://codingnojam.tistory.com/38&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;파이썬에서 sort함수 사용해서 정렬&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730774029934&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input()) #N개의 수
numbers = [] #numbers 리스트 초기화

# N개의 숫자를 입력받아 리스트에 저장
for _ in range(N):
    numbers.append(int(input()))

# 리스트를 오름차순으로 정렬 (sort 사용)
numbers.sort()

# 정렬된 리스트 출력
for num in numbers:
    print(num)&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;파이썬에서 선택정렬 사용해서 정렬하기&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730774443308&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
numbers = []

# N개의 숫자를 입력받아 리스트에 저장
for _ in range(N):
    numbers.append(int(input()))

# !!!선택 정렬 알고리즘 적용!!!
for i in range(len(numbers)): #i는 0부터 1개씩 리스트 길이까지 인덱스 번호 증가
    min_index = i #현재 위치(현재 위치 전은 이미 정렬되었다고 본다.)
    #0번 인덱스에서 시작
    for j in range(i + 1, len(numbers)):
        if numbers[j] &amp;lt; numbers[min_index]:
            min_index = j
    # i번 인덱스 이후에 현재 위치의 값보다 작은 값이 있다면, 가장 작은 값과 현재 위치의 값을 교환
    numbers[i], numbers[min_index] = numbers[min_index], numbers[i]

# 정렬된 리스트 출력
for num in numbers:
    print(num)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cw6xmF/btsKxkyQf4T/WfgXtTBTVyiK7kYWVuIwa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cw6xmF/btsKxkyQf4T/WfgXtTBTVyiK7kYWVuIwa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cw6xmF/btsKxkyQf4T/WfgXtTBTVyiK7kYWVuIwa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcw6xmF%2FbtsKxkyQf4T%2FWfgXtTBTVyiK7kYWVuIwa1%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;944&quot; height=&quot;91&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;10989 수 정렬하기3&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;파이썬에서 선택정렬로 정렬하기&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FQiVl/btsKwiaZwGp/ze5nzLVfkPoNjr9MiqrmWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FQiVl/btsKwiaZwGp/ze5nzLVfkPoNjr9MiqrmWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FQiVl/btsKwiaZwGp/ze5nzLVfkPoNjr9MiqrmWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFQiVl%2FbtsKwiaZwGp%2Fze5nzLVfkPoNjr9MiqrmWk%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;500&quot; height=&quot;148&quot; data-origin-width=&quot;664&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1730775066536&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N = int(input())
numbers = []

# N개의 숫자를 입력받아 리스트에 저장
for _ in range(N):
    numbers.append(int(input()))

# !!!선택 정렬 알고리즘 적용!!!
for i in range(len(numbers)): #i는 0부터 1개씩 리스트 길이까지 인덱스 번호 증가
    min_index = i #현재 위치(현재 위치 전은 이미 정렬되었다고 본다.)
    #0번 인덱스에서 시작
    for j in range(i + 1, len(numbers)):
        if numbers[j] &amp;lt; numbers[min_index]:
            min_index = j
    # i번 인덱스 이후에 현재 위치의 값보다 작은 값이 있다면, 가장 작은 값과 현재 위치의 값을 교환
    numbers[i], numbers[min_index] = numbers[min_index], numbers[i]

# 정렬된 리스트 출력
for num in numbers:
    print(num)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cy2TaT/btsKv7Hl7Ik/Th34kG5Iqev68btEC0KSk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cy2TaT/btsKv7Hl7Ik/Th34kG5Iqev68btEC0KSk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cy2TaT/btsKv7Hl7Ik/Th34kG5Iqev68btEC0KSk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcy2TaT%2FbtsKv7Hl7Ik%2FTh34kG5Iqev68btEC0KSk0%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;500&quot; height=&quot;211&quot; data-origin-width=&quot;853&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;10814 나이순 정렬&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;자바에서 선택정렬로 정렬하기&amp;gt;&lt;/p&gt;
&lt;pre id=&quot;code_1730776018894&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.*;
import java.io.*;
public class Main{
    public static void main(String[]args){
        try{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int N = Integer.parseInt(br.readLine());
        String [][] info = new String[N][2];
        for(int i=0; i&amp;lt;N; i++){
            String[] input = br.readLine().split(&quot; &quot;);
            info[i][0] = input[0]; //나이
            info[i][1] = input[1]; //이름
        } //각 줄에 있는 내용을 저장하기
        
        for(int i=0; i&amp;lt;N;i++){ //i가 현재위치
            for(int j=i;j&amp;lt;N;j++){
                if(Integer.parseInt(info[j][0]) &amp;lt; Integer.parseInt(info[i][0])){
                    String[] temp = info[i];
                    info[i] = info[j];
                    info[j] = temp;
                }
            }
        } //선택정렬 사용
        StringBuilder sb = new StringBuilder();
        for(int i=0; i&amp;lt;N; i++){
            sb.append(info[i][0]).append(&quot; &quot;).append(info[i][1]).append(&quot;\n&quot;);
        }
        
        System.out.println(sb.toString());
        }
        
        catch(IOException e){
            System.out.println(&quot;입출력 오류가 발생했습니다: &quot; + e.getMessage());
            e.printStackTrace(); // 예외의 상세 정보 출력 (선택 사항)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속 시간초과가 나온다...&lt;/p&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_1730783227219&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.InputStreamReader;
import java.io.IOException;
import java.util.Arrays;
import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            int N = Integer.parseInt(br.readLine());
            String[][] info = new String[N][2];
            
            // 입력을 배열에 저장
            for (int i = 0; i &amp;lt; N; i++) {
                String[] input = br.readLine().split(&quot; &quot;);
                info[i][0] = input[0]; // 나이
                info[i][1] = input[1]; // 이름
            }
            
            // 익명 클래스로 Comparator 정의
            Arrays.sort(info, new Comparator&amp;lt;String[]&amp;gt;() {
                @Override
                public int compare(String[] a, String[] b) {
                    return Integer.parseInt(a[0]) - Integer.parseInt(b[0]);
                }
            });

            // 정렬된 결과 출력
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i &amp;lt; N; i++) {
                sb.append(info[i][0]).append(&quot; &quot;).append(info[i][1]).append(&quot;\n&quot;);
            }
            
            System.out.print(sb.toString());
        } catch (IOException e) {
            System.out.println(&quot;입출력 오류가 발생했습니다: &quot; + e.getMessage());
            e.printStackTrace();
        }
    }
}&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;Comparator를 사용해줬다. (Chat GPT가 도와줬다.)&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-origin-width=&quot;749&quot; data-origin-height=&quot;711&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/28UxS/btsKw2ZYT45/ud9x8iVjGQpAcGwvYqlUrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/28UxS/btsKw2ZYT45/ud9x8iVjGQpAcGwvYqlUrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/28UxS/btsKw2ZYT45/ud9x8iVjGQpAcGwvYqlUrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F28UxS%2FbtsKw2ZYT45%2Fud9x8iVjGQpAcGwvYqlUrK%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;600&quot; height=&quot;570&quot; data-origin-width=&quot;749&quot; data-origin-height=&quot;711&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;595&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1l8p2/btsKwzDKHlf/uVB4kRcsCcJknE9Bvsz4P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1l8p2/btsKwzDKHlf/uVB4kRcsCcJknE9Bvsz4P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1l8p2/btsKwzDKHlf/uVB4kRcsCcJknE9Bvsz4P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1l8p2%2FbtsKwzDKHlf%2FuVB4kRcsCcJknE9Bvsz4P0%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;600&quot; height=&quot;464&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;595&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;</description>
      <category>책/자료구조와알고리즘with파이썬</category>
      <author>젊은사람 등장</author>
      <guid isPermaLink="true">https://youryoung.tistory.com/58</guid>
      <comments>https://youryoung.tistory.com/58#entry58comment</comments>
      <pubDate>Tue, 5 Nov 2024 10:23:49 +0900</pubDate>
    </item>
  </channel>
</rss>