Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de2484754e | ||
|
|
1f64dc54e2 |
|
After Width: | Height: | Size: 570 KiB |
|
After Width: | Height: | Size: 96 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 196 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 158 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 150 KiB |
|
After Width: | Height: | Size: 138 KiB |
|
After Width: | Height: | Size: 391 KiB |
|
After Width: | Height: | Size: 454 KiB |
|
After Width: | Height: | Size: 367 KiB |
|
After Width: | Height: | Size: 523 KiB |
|
After Width: | Height: | Size: 448 KiB |
|
After Width: | Height: | Size: 520 KiB |
|
After Width: | Height: | Size: 469 KiB |
|
After Width: | Height: | Size: 401 KiB |
|
After Width: | Height: | Size: 393 KiB |
|
After Width: | Height: | Size: 408 KiB |
|
After Width: | Height: | Size: 400 KiB |
|
After Width: | Height: | Size: 424 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 76 KiB |
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 308 KiB |
|
After Width: | Height: | Size: 229 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 204 KiB |
|
After Width: | Height: | Size: 218 KiB |
|
After Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 297 KiB |
|
After Width: | Height: | Size: 323 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 167 KiB |
|
After Width: | Height: | Size: 553 KiB |
|
After Width: | Height: | Size: 137 KiB |
|
After Width: | Height: | Size: 1.0 MiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 110 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 1007 KiB |
|
After Width: | Height: | Size: 237 KiB |
@@ -0,0 +1,36 @@
|
|||||||
|
# 1. Hook
|
||||||
|
|
||||||
|
**예시**
|
||||||
|
> Big-O 시간 복잡도의 한계를 넘어,
|
||||||
|
알고리즘의 세부 작업 실행 횟수와 단위 실행 시간을 고려한
|
||||||
|
가장 정확한 Dijkstra 알고리즘 런타임 예측 모델
|
||||||
|
|
||||||
|
짧은 Hook
|
||||||
|
|
||||||
|
# 2. 프로젝트 소개
|
||||||
|
|
||||||
|
**예시**
|
||||||
|
> 네비게이션, 자율주행, 우주항공 등 많은 현대 기술들에게 최적 경로를 탐색하는 것은 중요한 일이며, Dijkstra 알고리즘은 최적 경로를 도출하는 가장 효율적인 알고리즘이다. \
|
||||||
|
그러나 자율주행과 같이 실시간성과 속도가 중요한 문제가 많아지자, 휴리스틱 기반의 A* 알고리즘들이 적용되었다. 이들은 속도는 빠르지만, 항상 최적해를 보장하지는 않한다는 트레이드오프가 존재한다. \
|
||||||
|
상황에 따라 적절한 알고리즘을 선택하면 될 듯 하지만, Big-O 시간복잡도는 실행 상황이 아닌 알고리즘만을 고려하기 때문에 Dijkstra의 실행시간이 충분한지 알기 어렵다. \
|
||||||
|
그래서 **"이 트레이드오프를 감이 아닌 숫자로 계산해서 상황에 따른 정확한 알고리즘을 선택할 수는 없을까?"**라는 질문에서 출발하여 입력 그래프와 기기 성능만으로 Dijkstra의 실제 실행시간을 예측하는 모델을 연구했다.
|
||||||
|
|
||||||
|
IB 소논문 활동에서 시작해 계속 발전시켜 현재는 외부 학술지에도 기제된 연구로, 목표는 그래프의 노드 수, 간선 수, 가중치의 분산, 기기 성능만으로 이집 힙이 적용된 Dijkstra의 실행시간을 예측하는 것이다.
|
||||||
|
|
||||||
|
|
||||||
|
- 제목
|
||||||
|
- 배경
|
||||||
|
- 문제 제시
|
||||||
|
- 개인적 연결 (내가 관심을 갖게 된 이유)
|
||||||
|
- 활동 부가 설명 (육하원칙으로)
|
||||||
|
|
||||||
|
# 3. 시행착오
|
||||||
|
|
||||||
|
- 제목
|
||||||
|
- 문제
|
||||||
|
- 해결
|
||||||
|
|
||||||
|
# 4. 결과
|
||||||
|
|
||||||
|
- 결과
|
||||||
|
- 연결
|
||||||
@@ -0,0 +1,557 @@
|
|||||||
|
#import "template.typ": *
|
||||||
|
|
||||||
|
|
||||||
|
// ==================================================
|
||||||
|
// Config Variables
|
||||||
|
// ==================================================
|
||||||
|
|
||||||
|
#show: base.with(
|
||||||
|
cur_domain: 1,
|
||||||
|
total_domain: 5,
|
||||||
|
domain_name: "Research",
|
||||||
|
domain_detail: "본질을 향한 여정",
|
||||||
|
date: "2026",
|
||||||
|
author: "Seungjun Lee",
|
||||||
|
main-hue: 60deg,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// ==================================================
|
||||||
|
// Main content
|
||||||
|
// ==================================================
|
||||||
|
|
||||||
|
#cover()
|
||||||
|
|
||||||
|
#counter(page).update(1)
|
||||||
|
|
||||||
|
#table_of_contents()
|
||||||
|
|
||||||
|
#domain_frame(
|
||||||
|
bg_img_path: "assets/research/bg.png",
|
||||||
|
font_color: color.oklch(95%, 0.01, 250deg),
|
||||||
|
chrome_color: color.oklch(80%, 0.01, 250deg),
|
||||||
|
title: [본질을 향한 여정],
|
||||||
|
)[
|
||||||
|
나는 세상을 하나의 질문으로 이해하고 싶었다. \
|
||||||
|
답을 알수록 새로운 질문이 늘어난다는 것도, \
|
||||||
|
그 질문들이 결국 하나로 이어지지 않는다는 것도 나중에 알았다.
|
||||||
|
|
||||||
|
생명 수업에서 인체를 배울 때 이 사실을 가장 선명하게 느꼈다. \
|
||||||
|
나는 인간이면서도 내 몸 안에서 무슨 일이 일어나는지 인지하지 못한다. \
|
||||||
|
신경계가, 세포가 어떻게 상호작용하는지 하나의 그림으로 설명하려 할수록 \
|
||||||
|
설명은 흐려졌다.
|
||||||
|
|
||||||
|
전체를 통째로 이해하려는 시도는 매번 그렇게 무너졌다. \
|
||||||
|
대신 남은 방법은 하나였다 — 쪼개서 이해하고, 그 이해들을 다시 쌓아 올리는 것. \
|
||||||
|
전체가 아니라 부분에서, 이론이 아니라 실제 관찰에서 다시 시작하는 것.
|
||||||
|
|
||||||
|
이후의 모든 연구는 이 하나의 발견에서 출발한다. \
|
||||||
|
그래프 알고리즘의 이론적 시간복잡도가 실제 실행 시간과 어긋나는 지점에서, \
|
||||||
|
사회 현상을 하나의 법칙이 아니라 여러 변수의 상호작용으로 다시 볼 때, \
|
||||||
|
그리고 인간의 선택이 이론적 모형을 벗어나는 순간에서 — \
|
||||||
|
나는 같은 방법을 반복했다. 쪼개고, 어긋난 지점을 찾고, 다시 쌓아 올렸다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_cover(
|
||||||
|
name: [Dijkstra Runtime Analysis],
|
||||||
|
hook: [
|
||||||
|
`Big-O` 시간 복잡도의 한계를 넘어, \
|
||||||
|
알고리즘의 세부 작업 실행 횟수와 단위 실행 시간을 고려한 \
|
||||||
|
가장 정확한 `Dijkstra` 알고리즘 런타임 예측 모델
|
||||||
|
],
|
||||||
|
)[]
|
||||||
|
|
||||||
|
#activity_intro(
|
||||||
|
title: "Big-O가 답하지 못하는 것",
|
||||||
|
)[
|
||||||
|
네비게이션, 자율주행, 우주항공 등 많은 현대 기술들에게 최적 경로를 탐색하는 것은 중요한 일이며,
|
||||||
|
Dijkstra 알고리즘은 최적 경로를 도출하는 가장 효율적인 알고리즘이다. \
|
||||||
|
그러나 자율주행과 같이 실시간성과 속도가 중요한 문제가 많아지자, 휴리스틱 기반의 A\* 알고리즘들이 적용되었다. 이들은 속도는 빠르지만, 항상 최적해를 보장하지는 않한다는 트레이드오프가 존재한다. \
|
||||||
|
상황에 따라 적절한 알고리즘을 선택하면 될 듯 하지만, Big-O 시간복잡도는 노드·간선 수 등 입력 크기에 따른 점근적 증가율만 나타낼 뿐, 상수항이나 그래프의 특성, 기기 성능 같은 요인은 반영하지 않기 때문에 Dijkstra의 실제 실행시간이 충분한지 알기 어렵다. \
|
||||||
|
그래서 *"이 트레이드오프를 감이 아닌 숫자로 계산해서 상황에 따른 정확한 알고리즘을 선택할 수는 없을까?"*라는 질문에서 출발하여 입력 그래프와 기기 성능만으로 Dijkstra의 실제 실행시간을 예측하는 모델을 연구했다.
|
||||||
|
|
||||||
|
IB 소논문 활동에서 시작해 계속 발전시켜 현재는 외부 학술지에도 기제된 연구로, 목표는 그래프의 노드 수, 간선 수, 가중치의 분산, 기기 성능만으로 이집 힙이 적용된 Dijkstra의 실행시간을 예측하는 것이다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#let RS = math.italic("RS")
|
||||||
|
#let RSR = math.italic("RSR")
|
||||||
|
|
||||||
|
#activity_process(
|
||||||
|
title: "통제된 환경",
|
||||||
|
)[
|
||||||
|
#problem()[
|
||||||
|
실행 시간 측정의 신뢰도와 타당도를 높이기 위해서는 일정한 환경을 유지하는 것이 중요하다.
|
||||||
|
그러나 일반적인 데스크탑이나 노트북 환경에서는 통제된 환경을 구현하기 어렵다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
운영중인 홈서버의 Proxmox VE에 특정한 스펙의 Debian CT를 만들어 환경을 통제한다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/pve.png"),
|
||||||
|
caption: [CPU: AMD Ryzen 7 7700 8-Core Processor (4cores, Host) \/ RAM: 8GB \/ \
|
||||||
|
OS: Debian GNU/Linux 13 (trixie) \/ Kernal: Linux 6.8.12-17-pve \/
|
||||||
|
Python: Python 3.12],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_process(
|
||||||
|
title: "Big-O의 한계를 넘어서다",
|
||||||
|
)[
|
||||||
|
#problem()[
|
||||||
|
`Big-O` 시간복잡도는 최고차항을 제외한 모든 항들을 무시하고, 계수마자 없앤다. \
|
||||||
|
그렇기 때문에 시간복잡도가 $O(n^2)$인 알고리즘에 $n$을 넣는다고 실행시간을 구할 수 없다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
Dijkstra 알고리즘을 의사코드를 기반으로 세부 작업들로 쪼개어 실행시간 식을 도출한다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#block_equation(description: [
|
||||||
|
$V$ : 노드 수, $E$ : 간선 수, $RS$ : 키 감소 실행 횟수
|
||||||
|
])[
|
||||||
|
$
|
||||||
|
"Runtime" & = "Heap build" + "Min-node find" + "Vertex Iterate" + "Decrease key" \
|
||||||
|
& = "V" dot k_1 log(V) + V dot k_2 log(V) + E dot k_3 + RS dot k_4 log(V) \
|
||||||
|
& = V dot k_12 log(V) + E dot k_3 + RS dot k_4 log(V)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
이진 힙이 적용된 Dijkstra 알고리즘은 크게 위의 4개의 동작으로 나눌 수 있다. \
|
||||||
|
각각의 시간 복잡도와 계수 그리고 실행 횟수의 곱을 모두 더하면 전체 실행 시간이 나온다. \
|
||||||
|
이때, 첫 두 항의 문자 부분이 같기 때문에 인수분해를 통해 두 식을 합친다.
|
||||||
|
이때 개별 항의 계수를 구하는 것이 목적이 아니기 때문에 두 계수를 합쳐 $k_12$라고 한다.
|
||||||
|
|
||||||
|
이 식을 통해 $V, E, RS, k_12, k_3, k_4$만 알면 Dijkstra의 실행시간을 구할 수 있게 된다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_process(
|
||||||
|
title: "존재하지 않는 데이터를 만들다",
|
||||||
|
)[
|
||||||
|
#problem()[
|
||||||
|
식을 학습시키려면 방대한 양의 실제 경로 데이터가 필요했다.
|
||||||
|
그러나 현실에서는 학습에 필요한 만큼의 실제 경로 데이터를 구할 수 없다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
실제 경로 데이터의 특성들(크기, 밀도, 가중치 분포 등)을 분석하여, 이를 토대로 가상의 경로 데이터를 제작한다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#figure(image("assets/research/dijkstra/hist_original.png", width: 100%), caption: [원데이터 분포])
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (auto, auto),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/hist_log.png"),
|
||||||
|
caption: [$log$ 데이터 Q-Q 플롯],
|
||||||
|
),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/Q-Q_log.png"),
|
||||||
|
caption: [$log$ 데이터 Q-Q 플롯],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
원데이터의 분포와 Q-Q 플롯을 통해 데이터가 양의 왜도를 지닌 것을 파악하여, 가중치에 $log$를 취한 후 다시 Q-Q 플롯을 그리니 정규성을 보였다.
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
table(
|
||||||
|
columns: (auto, auto),
|
||||||
|
[*그래프 밀도*], [$tilde.eq 1.02 dot 10^(-07)$],
|
||||||
|
[*가중치 평균*], [$tilde.eq 2950$],
|
||||||
|
[*표준편차 평균*], [$tilde.eq 4070$],
|
||||||
|
),
|
||||||
|
caption: [실제 경로 데이터 분석 값],
|
||||||
|
)
|
||||||
|
|
||||||
|
이를 바탕으로 그래프의 밀도와 가중치에 대한 추가적인 특성들을 분석하였다.
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
table(
|
||||||
|
columns: (auto, auto),
|
||||||
|
[*노드 수*], [${2000 dot 2^k | k = 0, 1, 2, 3}$],
|
||||||
|
[*그래프 밀도*], [${10^k, 3 dot 10^k | k = -7, -6, ..., -1}$],
|
||||||
|
[*표준편차*], [${1000, 1500, 2000, 3000, 4000, 6000, 8000, 12000, 16000}$],
|
||||||
|
[*개수*], [$10$],
|
||||||
|
[*가중치 평균*], [$3000$],
|
||||||
|
[*시작 노드*], [$0$],
|
||||||
|
[*랜덤 시드*], [$42$],
|
||||||
|
),
|
||||||
|
caption: [데이터 생성 매개변수 조합],
|
||||||
|
)
|
||||||
|
|
||||||
|
분석 값들을 바탕으로 적절한 노드 수, 그래프 밀도, 가중치 분산 3개 변수의 쌍으로 로그 정규분포를 띄는 가상 그래프를 864,000개 생성했다.
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/synthetic_data_code.png"),
|
||||||
|
caption: u_link(
|
||||||
|
"https://gitea.seung6lee.com/seung6lee/dijkstra-runtime-analysis/src/branch/main/codes/experiments/synthetic_data/graph_generators/outdegree_graph_generator.py",
|
||||||
|
)[outdegree 기반 랜덤 경로 데이터 생성 프로그램],
|
||||||
|
)
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/synthetic_data.png"),
|
||||||
|
caption: u_link(
|
||||||
|
"https://gitea.seung6lee.com/seung6lee/dijkstra-runtime-analysis/src/branch/main/codes/results/real_data/raw/dimacs_s42.csv",
|
||||||
|
)[가상 경로 데이터 864,000개],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_process(
|
||||||
|
title: "실행해야만 알 수 있는 값",
|
||||||
|
)[
|
||||||
|
#problem()[
|
||||||
|
모델을 학습시켜 가중치 $k$들을 알아낼 수 있다.
|
||||||
|
그리고 $V$와 $E$는 그래프에서 쉽계 계산할 수 있다.
|
||||||
|
그러나 $RS$는 Dijkstra를 실행하지 않으면 알 수 없다.
|
||||||
|
예측을 위해 실행이 선행되어야 하는 모순이 발생하는 것이다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
그래프의 특성만으로 Dijkstra 실행 없이 $RS$를 예측하는 방법을 찾는다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#stack(
|
||||||
|
(
|
||||||
|
[$bold(RS)$ 재정의],
|
||||||
|
[
|
||||||
|
$RS$를 그 자체가 아니라, 다른 변수들로 쪼개어서 정의한다.
|
||||||
|
|
||||||
|
#block_equation()[
|
||||||
|
$ RS = "Relax Attemps" times "Relax Success Ratio" = E times RSR $
|
||||||
|
]
|
||||||
|
|
||||||
|
시도 횟수는 $E$라는 것을 알기에, $RSR$만 예측하면 된다. \
|
||||||
|
$RSR$은 비율이기에 절대량이 아닌 밀도나 분산 등에 영향을 받을 것이라고 판단한다.
|
||||||
|
#fill_break()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[$bold(RSR) thin \& thin bold(sigma)$],
|
||||||
|
[
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/sigma_rsr_01.png", width: 60%),
|
||||||
|
caption: [$V = 2000, thin D = 0.01$ 에서의 $sigma "vs" RSR$],
|
||||||
|
)
|
||||||
|
각 $(V, D)$ 쌍에 대해 $(sigma, RSR)$ 그래프를 그린 결과,
|
||||||
|
대부분의 경우 완벽한 로그 함수 관계를 보인다.
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (1fr, 1fr),
|
||||||
|
gutter: 1em,
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/sigma_rsr_02.png", width: 100%),
|
||||||
|
caption: [$V = 2000, thin D = 3 dot 10^(-5)$ 에서의 $sigma "vs" RSR$],
|
||||||
|
),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/sigma_rsr_03.png", width: 100%),
|
||||||
|
caption: [$V = 2000, thin D = 0.0003$ 에서의 $sigma "vs" RSR$],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
그러나 $sigma$값에 관계없이 항상 $RSR=1$이거나, 경향성이 전혀 없는 경우도 나타난다.
|
||||||
|
|
||||||
|
#block_equation(description: [
|
||||||
|
$a, b$는 상수
|
||||||
|
])[
|
||||||
|
$ RSR = a dot log(sigma) + b $
|
||||||
|
]
|
||||||
|
|
||||||
|
$log$ 관계를 가정하고 위와 같이 $RSR$과 $sigma$의 관계를 식으로 나타내었다.
|
||||||
|
|
||||||
|
#fill_break()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[$bold(RSR) thin \& thin bold(D)$],
|
||||||
|
[
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/density_rsr.png", width: 60%),
|
||||||
|
caption: [$V = 10000, thin sigma = 0.3246$ 에서의 $D "vs" RSR$],
|
||||||
|
)
|
||||||
|
각 $(V, sigma)$ 쌍에 대해 $(D, RSR)$ 그래프를 그린 결과,
|
||||||
|
초반에 급격하게 감소하다 완만해지고 꼬리가 길게 남는 개형을 보인다. \
|
||||||
|
이는 멱법칙의 특성과 부합하여, $(log(D), log(RSR))$ 그래프를 그려본다.
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/density_rsr_loglog.png", width: 60%),
|
||||||
|
caption: [$V = 10000, thin sigma = 0.3246$ 에서의 $log(D) "vs" log(RSR)$],
|
||||||
|
)
|
||||||
|
특정 임계점을 기준으로 왼쪽은 항상 $RSR = 1$인 구간이고, 오른쪽은 두 변수가 완벽하게 멱법칙을 만족하는 구간이 나온다. \
|
||||||
|
이 임계점의 위치와 의미를 설명하려 했으나, 해당 지점의 $D$나 $log(D)$의 유의미한 특징을 발견하지 못하였다.
|
||||||
|
#fill_break()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[$bold(D)$ 분석],
|
||||||
|
[
|
||||||
|
$D$를 다른 변수로 대체해보았다. \
|
||||||
|
방향 그래프에서 $D = E / V(V - 1)$로 나타낼 수 있고,
|
||||||
|
평균 차수는 $macron(k) = E \/ V = (V - 1) D$로 나타낼 수 있다.
|
||||||
|
$D$대신 $macron(k)$를 대입해 그래프를 그려보았으며,
|
||||||
|
이때 $V$는 통제 변인으로 각 그래프에서 상수로 취급된다.
|
||||||
|
|
||||||
|
#grid(
|
||||||
|
columns: (1fr, 1fr),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/avgdeg_rsr_loglog.png"),
|
||||||
|
caption: [$V = 10000, thin sigma = 0.3246$ 에서의 $log(macron(k)) "vs" log(RSR)$],
|
||||||
|
),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/phase_histogram.png"),
|
||||||
|
caption: [$macron(k)$에 따른 경로 그래프 양상 분포],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
그 결과, 임계점이 항상 $macron(k) = 1$인 구간에서 발생한다.
|
||||||
|
이 지점은 각 노드에서 출발하는 간선이 최소 1개씩 있는 경우이다.
|
||||||
|
|
||||||
|
이 현상은 Erdős-Rényi 모델의 3가지 상과 연결된다는 것을 파악했다.
|
||||||
|
해당 모델은 $G(n,p)$ 그래프를 $n p$의 값에 따라 아임계, 임계, 초임계 구간으로 분류한다.\
|
||||||
|
$sigma$ 분석에서의 예외 상황들도 아임계 구간과 임계 구간으로 설명이 가능해졌다.
|
||||||
|
|
||||||
|
#block_equation(description: [
|
||||||
|
$c, d$는 상수
|
||||||
|
])[
|
||||||
|
$ RSR = c dot macron(k)^d quad (macron(k) > 1) $
|
||||||
|
]
|
||||||
|
|
||||||
|
$macron(k) > 1$인 조건에서 위와 같이 $RSR$과 $d$의 관계를 식으로 나타내었다.
|
||||||
|
#fill_break()
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[최종 식 도출],
|
||||||
|
[
|
||||||
|
$sigma$ 분석 과정에서 발견한 Erdős-Rényi 모델과의 연결성을 통해 $d$ 분석 과정에서의 한계를 극복하여, $sigma$와 $d$ 각각과 $RSR$간의 식을 확정하였다.
|
||||||
|
|
||||||
|
이때 $sigma$와 $d$는 그래프에서 서로 독립적이기 때문에 두 관계식을 곱하면 두 변수의 영향을 모두 반영하는 식을 도출할 수 있다.
|
||||||
|
|
||||||
|
#block_equation(description: [
|
||||||
|
$alpha, beta, gamma$는 상수
|
||||||
|
])[
|
||||||
|
$
|
||||||
|
RSR & = {a dot log(sigma) + b} times {c dot macron(k)^d} quad (macron(k) > 1) \
|
||||||
|
& = macron(k)^d times {a c dot log(sigma) + b c} \
|
||||||
|
& = macron(k)^alpha times {beta dot log(sigma) + gamma}
|
||||||
|
$
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[회귀 분석],
|
||||||
|
[
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/rsr_regression.png", width: 50%),
|
||||||
|
caption: [비선형 회귀 결과 (
|
||||||
|
$alpha = 0.0612 plus.minus 0.000202, thin
|
||||||
|
beta = 0.928 plus.minus 0.000171, thin
|
||||||
|
gamma = -0.711 plus.minus 0.000183$)],
|
||||||
|
)
|
||||||
|
|
||||||
|
제작한 864,000개의 데이터에 대해 비선형 회귀를 진행하였더니 $RSR$에 대해 $R^2 = 0.9947$에 달하는 적합에 성공하였다.
|
||||||
|
|
||||||
|
이를 통해 Dijkstra 실행 없이도 $99%$의 정확도로 $RS$를 예측할 수 있게 되었다.
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_process(title: "다중공선성")[
|
||||||
|
#problem()[
|
||||||
|
#block_equation(description: [
|
||||||
|
$V$ : 노드 수, $E$ : 간선 수, $RS$ : 키 감소 실행 횟수
|
||||||
|
])[
|
||||||
|
$
|
||||||
|
"Runtime" = V dot k_12 log(V) + E dot k_3 + RS dot k_4 log(V)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
이제 회귀 분석을 통해 $k_12, k_3, k_4$만 구하면 된다.
|
||||||
|
|
||||||
|
그러나 $k_12$와 $k_4$가 포함된 두 항이 유사하다.
|
||||||
|
공통적으로 $log(V)$가 있으며, $V$와 $RS$도 양의 상관관계를 가진다.
|
||||||
|
따라서 회귀분석 시행하면 다중공선성이 발생할 가능성이 높다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
모든 데이터를 한번에 회귀하는 것이 아니라, 고유한 $V$별로 부분 회귀를 하고 결과를 종합하는 방법을 고안했다.
|
||||||
|
|
||||||
|
#block_equation()[
|
||||||
|
$
|
||||||
|
"UC"_1(V) & = k_12 log(V) \
|
||||||
|
"UC"_2(V) & = k_4 log(V) \
|
||||||
|
"Runtime" & = V dot "UC"_1(V) + E dot k_3 + RS dot "UC"_2(V)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
$V = V_i$인 데이터로 회귀를 하면 $"UC"_1(V_i), "UC"_2(V_i), k_3$가 도출된다. \
|
||||||
|
이때 $"UC"_1(V)$과 $"UC"_2(V)$는 $V$에 대해 $log$ 관계를 가져야하고, $k_3$는 상수여야한다.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_process(title: "이론과 결과의 괴리")[
|
||||||
|
#callout(title: [$bold("UC"_1(V_i)), bold("UC"_2(V_i)), bold(k_3)$ 적합 결과])[
|
||||||
|
#grid(
|
||||||
|
columns: (1fr, 1fr),
|
||||||
|
gutter: 1em,
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/node_uc1.png", width: 100%),
|
||||||
|
caption: [$"UC"_1(V)$],
|
||||||
|
),
|
||||||
|
figure(
|
||||||
|
image("assets/research/dijkstra/node_uc2.png", width: 100%),
|
||||||
|
caption: [$"UC"_2(V)$],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/node_k3.png", width: 60%),
|
||||||
|
caption: [$"k"_3(V)$],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
#problem()[
|
||||||
|
$"UC"_2(V)$는 $V$와 정확한 $log$ 관계를 보였다.\
|
||||||
|
그러나 $"UC"_1(V)$는 어떠한 경향성도 보이지 않았으며, \
|
||||||
|
$k_3$는 고정되지 않고 우하향의 추세를 보였다.
|
||||||
|
]
|
||||||
|
|
||||||
|
#hl[]
|
||||||
|
|
||||||
|
#solution()[
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/contribution_vs_N.png", width: 100%),
|
||||||
|
caption: [세부 작업별 전체 실행시간 차지 비율],
|
||||||
|
)
|
||||||
|
|
||||||
|
$"UC"_1(V)$의 경향성은 파악이 불가능했으나,
|
||||||
|
전체 런타임에서 $V dot "UC"_1(V)$항이 차지하는 비율이 $V lt.eq.slant 20000$ 인 지점에서 평균 $4.2 %$ 이하라는 것을 고려해 평균을 대푯값으로 사용하기로 하였다.
|
||||||
|
|
||||||
|
$k_3$의 우하향 추세는 파이썬의 캐싱 효과로 인한 것이라고 추정하였다. \
|
||||||
|
실행시간은 양수여야한다는 점을 고려하여 지수 포화 함수로 모델링하였다.
|
||||||
|
|
||||||
|
$"UC"_2(V)$는 $log$ 함수 적합을 통해 성공적으로 $k_4$를 구했다.
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_result(title: "예측할 수 있게 된 것")[
|
||||||
|
#block_equation(
|
||||||
|
title: [키 감소 횟수],
|
||||||
|
description: [$V$ : 노드 수, $E$ : 간선 수, $sigma$ : 가중치 표준편차],
|
||||||
|
)[
|
||||||
|
$
|
||||||
|
RSR(macron(k), sigma) & = macron(k)^0.0612 (0.928 log sigma - 0.711), quad
|
||||||
|
macron(k)(V, E) & = E \/ V \
|
||||||
|
RS(V, E, sigma) & = E dot RSR(macron(k)(V, E), sigma)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
실행해야만 알 수 있던 $RS$를 세 변수만으로 계산할 수 있게 되었다.
|
||||||
|
|
||||||
|
#block_equation(
|
||||||
|
title: [단위 실행시간],
|
||||||
|
description: [단위 : 초],
|
||||||
|
)[
|
||||||
|
$
|
||||||
|
"UC"_1 & = 2.15 times 10^(-7) \
|
||||||
|
k_3(V) & = 1.59 times 10^(-7) e^(-5.67 times 10^(-5) V) + 4.37 times 10^(-8) \
|
||||||
|
"UC"_2(V) & = 1.06 times 10^(-6) log_2 V - 1.09 times 10^(-5)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
세 값은 모두 $V$의 함수이거나 상수가 되어 식에 남아 있던 미지수가 전부 사라진다.
|
||||||
|
|
||||||
|
#block_equation(
|
||||||
|
title: [최종 모델],
|
||||||
|
description: [입력 : $V$, $E$, $sigma$],
|
||||||
|
)[
|
||||||
|
$
|
||||||
|
"Runtime"(V, E, sigma) = V dot "UC"_1 + E dot k_3(V) + RS(V, E, sigma) dot "UC"_2(V)
|
||||||
|
$
|
||||||
|
]
|
||||||
|
|
||||||
|
최종 식의 세 변수 모두 실행 전에 그래프에서 직접 추출할 수 있는 값이기에, \
|
||||||
|
*Dijkstra를 한 번도 실행하지 않고 실행시간을 예측할 수 있다.*
|
||||||
|
]
|
||||||
|
|
||||||
|
#activity_result(title: "남은 오차")[
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/comparison_plot.png"),
|
||||||
|
caption: [실제 경로 데이터에 대한 실행시간 예측과 $RS$ 예측],
|
||||||
|
)
|
||||||
|
|
||||||
|
#figure(
|
||||||
|
image("assets/research/dijkstra/predicted_vs_real.png", width: 60%),
|
||||||
|
caption: [생성 그래프에 대한 예측값과 실측값],
|
||||||
|
)
|
||||||
|
|
||||||
|
내삽에 대해 실행 시간 예측에 대해서 $96.9%$를 설명에 성공했다. \
|
||||||
|
게다가 실제 경로 데이터(외삽)에서도 $RS$예측에 대해 일관된 정확도를 보였다. \
|
||||||
|
그러나 실전 상황에서 그래프가 커질수록 실행시간 예측은 과대평가하는 경항이 보였다.\
|
||||||
|
|
||||||
|
이 결과들에서 내 연구의 의의와 한계가 뚜렷하게 드러난다. \
|
||||||
|
$RS$는 그래프의 구조만으로 결정되므로, 그래프의 근본적인 특성까지 쪼개었더니 정확하게 설명이 가능해졌으나, 실행시간은 파이썬 아래의 캐싱이나 하드웨어적 특성까지 쪼개지 못해 예측과 실측값 차이가 벌어졌다.
|
||||||
|
|
||||||
|
#table(
|
||||||
|
columns: (auto, 1fr),
|
||||||
|
table.header([*문제 상황*], [*극복 방법*]),
|
||||||
|
[학습 데이터의 부재], [분포를 분석해 864,000개를 직접 생성],
|
||||||
|
[실행해야 알 수 있는 값], [$E times RSR$로 쪼개 미지수를 하나로 축소],
|
||||||
|
[다중공선성], [$V$별 개별 회귀 후 기여도 기준으로 정밀도 배분],
|
||||||
|
)
|
||||||
|
|
||||||
|
모든 문제 상황에서는 직면한 큰 문제를 작은 단위들로 쪼개고, 작은 문제들의 해결책들을 종합하여 전체 문제를 해결해나갔다.
|
||||||
|
실행시간 측정을 위한 통제된 환경은 직접 구축한 홈서버의 가상화 환경(PVE)를 통해 구현했다.
|
||||||
|
|
||||||
|
이제 남은 오차는 파이썬이라는 언어 아래의 저차원에 위치해 있다. \
|
||||||
|
카이스트에서 언어와 하드웨어 수준의 저차원 요소를 배워 이 모델의 정확도를 끌어올리고, 완성한 모델을 실제 임베디드 시스템에서 알고리즘 선택의 지표로 적용하려 한다.
|
||||||
|
|
||||||
|
#context ref_evidence()
|
||||||
|
]
|
||||||
|
|
||||||
|
#add_evidence(
|
||||||
|
activity_ref: ref(label("Dijkstra Runtime Analysis")),
|
||||||
|
(
|
||||||
|
[소스 코드 및 자료],
|
||||||
|
u_link(
|
||||||
|
"https://gitea.seung6lee.com/seung6lee/dijkstra-runtime-analysis",
|
||||||
|
)[https://gitea.seung6lee.com/seung6lee/dijkstra-runtime-analysis],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[International Journal of STEAM 기재],
|
||||||
|
[
|
||||||
|
#image("assets/research/dijkstra/short_paper_01.png", height: 70%)
|
||||||
|
#image("assets/research/dijkstra/short_paper_02.png")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[정리 노트],
|
||||||
|
{
|
||||||
|
for i in range(1, 15) {
|
||||||
|
let path = "assets/research/dijkstra/notes/note_" + pad_zeros(i, 2) + ".jpg"
|
||||||
|
image(path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
(
|
||||||
|
[논문 본문],
|
||||||
|
{
|
||||||
|
for i in range(1, 17) {
|
||||||
|
let path = "assets/research/dijkstra/paper/paper_" + pad_zeros(i, 2) + ".png"
|
||||||
|
image(path)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
#evidence()
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
#let font-display = ("Source Serif 4 Display", "Source Han Serif K")
|
#let font-display = ("Source Serif 4 Display", "Source Han Serif K")
|
||||||
#let font-subhead = ("Source Serif 4 Subhead", "Source Han Serif K")
|
#let font-subhead = ("Source Serif 4 Subhead", "Source Han Serif K")
|
||||||
#let font-caption = ("Source Serif 4 Caption", "Source Han Serif K")
|
#let font-caption = ("Source Serif 4 Caption", "Source Han Serif K")
|
||||||
// #let font-code =
|
#let font-code = ("Source Code Pro", "Pretendard")
|
||||||
|
|
||||||
#let color-surface = white
|
#let color-surface = white
|
||||||
#let color-ink = color.oklch(22%, 0.01, 250deg)
|
#let color-ink = color.oklch(22%, 0.01, 250deg)
|
||||||
@@ -19,6 +19,10 @@
|
|||||||
|
|
||||||
#let bleed = 1.5cm
|
#let bleed = 1.5cm
|
||||||
|
|
||||||
|
#let page-paper = "a4"
|
||||||
|
#let page-height = 297mm
|
||||||
|
#let page-margin = (x: 2.8cm, y: 4.2cm)
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// Metadata
|
// Metadata
|
||||||
// ==================================================
|
// ==================================================
|
||||||
@@ -48,12 +52,14 @@
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#let evidence-data = state("evidence-data", none)
|
||||||
|
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// Util functions
|
// Util functions
|
||||||
// ==================================================
|
// ==================================================
|
||||||
|
|
||||||
#let pad-zeros(num, width) = {
|
#let pad_zeros(num, width) = {
|
||||||
let s = str(num)
|
let s = str(num)
|
||||||
"0" * calc.max(0, width - s.len()) + s
|
"0" * calc.max(0, width - s.len()) + s
|
||||||
}
|
}
|
||||||
@@ -68,41 +74,73 @@
|
|||||||
accent-faint: color.oklch(70%, 0.02, hue),
|
accent-faint: color.oklch(70%, 0.02, hue),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#let fill_break() = context {
|
||||||
|
v(1fr)
|
||||||
|
}
|
||||||
|
|
||||||
#let heading_text(it) = {
|
#let heading_text(it) = {
|
||||||
if it.has("text") {
|
if type(it) == str {
|
||||||
|
it
|
||||||
|
} else if it == linebreak() {
|
||||||
|
" "
|
||||||
|
} else if it.has("text") {
|
||||||
it.text
|
it.text
|
||||||
} else if it.has("children") {
|
} else if it.has("children") {
|
||||||
it.children.map(heading_text).join("")
|
it.children.map(heading_text).join("")
|
||||||
} else if it == linebreak() {
|
|
||||||
" "
|
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must be called from inside a context block.
|
||||||
|
#let cur_activity() = {
|
||||||
|
let m = meta.get()
|
||||||
|
let level1s = query(heading.where(level: 1, outlined: true))
|
||||||
|
let n = counter(heading).get().at(0)
|
||||||
|
let cur = level1s.at(n - 1)
|
||||||
|
let prefix = str(m.cur_domain) + "." + str(n) + ". "
|
||||||
|
|
||||||
|
(
|
||||||
|
prefix: prefix,
|
||||||
|
name: heading_text(cur.body),
|
||||||
|
location: cur.location(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// Body components
|
// Body components
|
||||||
// ==================================================
|
// ==================================================
|
||||||
|
|
||||||
#let callout(bg_color: none, border_color: none, body) = context {
|
#let kicker(color: color-ink-helper, content) = {
|
||||||
|
set text(
|
||||||
|
font: font-body,
|
||||||
|
weight: 700,
|
||||||
|
size: 10pt,
|
||||||
|
tracking: 1pt,
|
||||||
|
fill: color,
|
||||||
|
)
|
||||||
|
|
||||||
|
block(below: 1.5em)[
|
||||||
|
#content
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let callout(title: "", bg_color: none, border_color: none, body) = context {
|
||||||
let cp = color-palette.get()
|
let cp = color-palette.get()
|
||||||
let bg_c = if bg_color == none { cp.callout-surface } else { bg_color }
|
let bg_c = if bg_color == none { cp.callout-surface } else { bg_color }
|
||||||
let border_c = if border_color == none { cp.callout-border } else { border_color }
|
let border_c = if border_color == none { cp.callout-border } else { border_color }
|
||||||
|
|
||||||
let x_pad = 12pt
|
|
||||||
let y_pad = 18pt
|
|
||||||
let gap = 24pt
|
|
||||||
|
|
||||||
block(
|
block(
|
||||||
width: 100%,
|
width: 100%,
|
||||||
above: gap + y_pad,
|
inset: (x: 12pt, y: 18pt),
|
||||||
below: gap + y_pad,
|
spacing: 1.6em,
|
||||||
outset: (x: x_pad, y: y_pad),
|
// radius: 4pt,
|
||||||
radius: 4pt,
|
|
||||||
fill: bg_c,
|
fill: bg_c,
|
||||||
stroke: (paint: border_c),
|
stroke: (paint: border_c),
|
||||||
)[
|
)[
|
||||||
|
#if title != "" {
|
||||||
|
kicker(color: cp.accent)[#title]
|
||||||
|
}
|
||||||
#body
|
#body
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -118,6 +156,7 @@
|
|||||||
let body-size = measure(
|
let body-size = measure(
|
||||||
content,
|
content,
|
||||||
width: body-width,
|
width: body-width,
|
||||||
|
height: size.height,
|
||||||
)
|
)
|
||||||
|
|
||||||
grid(
|
grid(
|
||||||
@@ -129,7 +168,7 @@
|
|||||||
stroke: (
|
stroke: (
|
||||||
paint: c,
|
paint: c,
|
||||||
thickness: line-width,
|
thickness: line-width,
|
||||||
cap: "round",
|
// cap: "round",
|
||||||
),
|
),
|
||||||
angle: 90deg,
|
angle: 90deg,
|
||||||
length: body-size.height + 15pt,
|
length: body-size.height + 15pt,
|
||||||
@@ -140,20 +179,6 @@
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#let kicker(color: color-ink-helper, content) = {
|
|
||||||
set text(
|
|
||||||
font: font-body,
|
|
||||||
weight: 700,
|
|
||||||
size: 10pt,
|
|
||||||
tracking: 1pt,
|
|
||||||
fill: color,
|
|
||||||
)
|
|
||||||
|
|
||||||
block(below: 1.5em)[
|
|
||||||
#content
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#let problem(content) = context {
|
#let problem(content) = context {
|
||||||
let c = color-palette.get().problem
|
let c = color-palette.get().problem
|
||||||
|
|
||||||
@@ -177,7 +202,9 @@
|
|||||||
#let hl(content) = context {
|
#let hl(content) = context {
|
||||||
let cp = color-palette.get()
|
let cp = color-palette.get()
|
||||||
|
|
||||||
// if content == "" {}
|
let body = box(
|
||||||
|
inset: (x: if content == [] { 0pt } else { 10pt }),
|
||||||
|
)[#content]
|
||||||
|
|
||||||
set text(
|
set text(
|
||||||
font: font-body,
|
font: font-body,
|
||||||
@@ -190,70 +217,74 @@
|
|||||||
grid(
|
grid(
|
||||||
columns: (1fr, auto, 1fr),
|
columns: (1fr, auto, 1fr),
|
||||||
align: horizon,
|
align: horizon,
|
||||||
gutter: 10pt,
|
line(length: 100%, stroke: (paint: cp.rule)), body, line(length: 100%, stroke: (paint: cp.rule)),
|
||||||
line(length: 100%, stroke: (paint: cp.rule)), content, line(length: 100%, stroke: (paint: cp.rule)),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#let block_equation(content) = {
|
#let block_equation(title: "", description: "", content) = context {
|
||||||
|
let cp = color-palette.get()
|
||||||
|
|
||||||
callout()[
|
callout()[
|
||||||
|
#set par(
|
||||||
|
spacing: 1em,
|
||||||
|
)
|
||||||
|
#if title != "" {
|
||||||
|
kicker(color: cp.accent)[#title]
|
||||||
|
}
|
||||||
|
|
||||||
#content
|
#content
|
||||||
|
|
||||||
|
#if description != "" {
|
||||||
|
set text(
|
||||||
|
size: 9pt,
|
||||||
|
fill: color-ink-helper,
|
||||||
|
)
|
||||||
|
align(right)[#description]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
#let stack(layers) = context {
|
#let u_link(url, content) = link(url)[
|
||||||
|
#underline(
|
||||||
|
content,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
#let stack(..layers) = context {
|
||||||
let cp = color-palette.get()
|
let cp = color-palette.get()
|
||||||
let radius = 12pt
|
let radius = 12pt
|
||||||
let gutter = 12pt
|
let gutter = 12pt
|
||||||
let gap = 24pt
|
let gap = 24pt
|
||||||
|
let indent = radius + gutter
|
||||||
|
|
||||||
for (idx, (title, body)) in layers.enumerate() {
|
for (idx, (title, body)) in layers.pos().enumerate() {
|
||||||
let is_last = idx == layers.len() - 1
|
let is_last = idx == layers.len() - 1
|
||||||
|
|
||||||
let content = {
|
|
||||||
v(radius - 12pt / 2)
|
|
||||||
text(
|
|
||||||
weight: 700,
|
|
||||||
)[
|
|
||||||
#title
|
|
||||||
#linebreak()
|
|
||||||
]
|
|
||||||
body
|
|
||||||
v(gap)
|
|
||||||
}
|
|
||||||
|
|
||||||
block(
|
block(
|
||||||
above: if idx == 0 { auto } else { 0pt },
|
above: if idx == 0 { auto } else { 0pt },
|
||||||
below: if is_last { auto } else { 0pt },
|
below: if is_last { auto } else { 0pt },
|
||||||
|
inset: (left: indent),
|
||||||
|
stroke: (left: (paint: cp.accent, thickness: 1pt)),
|
||||||
width: 100%,
|
width: 100%,
|
||||||
)[
|
)[
|
||||||
#layout(size => {
|
#place(
|
||||||
let content_width = size.width - radius * 2 - gutter
|
left + top,
|
||||||
let content_size = measure(content, width: content_width)
|
dx: -indent - radius,
|
||||||
|
)[
|
||||||
grid(
|
#circle(radius: radius, fill: cp.accent)[
|
||||||
columns: (radius * 2, 1fr),
|
|
||||||
gutter: gutter,
|
|
||||||
grid(
|
|
||||||
rows: (radius * 2, auto),
|
|
||||||
circle(radius: radius, fill: cp.accent)[
|
|
||||||
#set align(center + horizon)
|
#set align(center + horizon)
|
||||||
#set text(font: font-serif, fill: color-ink-inverse, size: 12pt, weight: 700)
|
#set text(font: font-serif, fill: color-ink-inverse, size: 12pt, weight: 700)
|
||||||
#{ idx + 1 }
|
#{ idx + 1 }
|
||||||
],
|
]
|
||||||
line(
|
]
|
||||||
start: (50%, 0pt),
|
|
||||||
end: (50%, content_size.height - radius * 2),
|
#v(radius - 12pt / 2)
|
||||||
stroke: (
|
#text(weight: 700)[
|
||||||
paint: cp.accent,
|
#title
|
||||||
thickness: 1pt,
|
#linebreak()
|
||||||
cap: "round",
|
]
|
||||||
),
|
#body
|
||||||
),
|
#v(gap)
|
||||||
),
|
|
||||||
content,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -285,6 +316,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#let ref_evidence() = {
|
||||||
|
let ca = cur_activity()
|
||||||
|
|
||||||
|
set ref(
|
||||||
|
supplement: "증빙 자료.",
|
||||||
|
)
|
||||||
|
|
||||||
|
align(bottom)[
|
||||||
|
#hl[]
|
||||||
|
*활동 증빙 자료:*
|
||||||
|
#underline[#ref(label("Evidence) " + ca.name))]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#let add_evidence(activity_ref: none, ..content) = context {
|
||||||
|
let name = str(activity_ref.target)
|
||||||
|
let data = (
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
content.pos().flatten(),
|
||||||
|
),
|
||||||
|
).to-dict()
|
||||||
|
|
||||||
|
evidence-data.update(old => {
|
||||||
|
if old == none {
|
||||||
|
data
|
||||||
|
} else {
|
||||||
|
old + data
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#let header_template(dark: false, font_color: none, show_activity: false) = place(
|
#let header_template(dark: false, font_color: none, show_activity: false) = place(
|
||||||
top + left,
|
top + left,
|
||||||
dy: 1cm,
|
dy: 1cm,
|
||||||
@@ -304,13 +367,10 @@
|
|||||||
link((page: 1, x: 0pt, y: 0pt))[#m.cur_domain. #m.domain_name]
|
link((page: 1, x: 0pt, y: 0pt))[#m.cur_domain. #m.domain_name]
|
||||||
h(1fr)
|
h(1fr)
|
||||||
if show_activity {
|
if show_activity {
|
||||||
let level1s = query(heading.where(level: 1, outlined: true))
|
let ca = cur_activity()
|
||||||
let n = counter(heading).get().at(0)
|
|
||||||
let cur = level1s.at(n - 1)
|
|
||||||
let prefix = str(m.cur_domain) + "." + str(n) + ". "
|
|
||||||
link(
|
link(
|
||||||
cur.location(),
|
ca.location,
|
||||||
prefix + cur.body,
|
ca.prefix + ca.name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +416,7 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
#text(font: font-caption)[
|
#text(font: font-caption)[
|
||||||
#pad-zeros(m.cur_domain, 2) / #pad-zeros(m.total_domain, 2)
|
#pad_zeros(m.cur_domain, 2) / #pad_zeros(m.total_domain, 2)
|
||||||
]
|
]
|
||||||
|
|
||||||
#v(12pt)
|
#v(12pt)
|
||||||
@@ -421,6 +481,7 @@
|
|||||||
#heading(level: 1)[
|
#heading(level: 1)[
|
||||||
#name
|
#name
|
||||||
]
|
]
|
||||||
|
#label(heading_text(name))
|
||||||
|
|
||||||
#block_quote()[
|
#block_quote()[
|
||||||
#hook
|
#hook
|
||||||
@@ -434,7 +495,7 @@
|
|||||||
header: header_template(show_activity: true),
|
header: header_template(show_activity: true),
|
||||||
footer: footer_template(),
|
footer: footer_template(),
|
||||||
)[
|
)[
|
||||||
#kicker("프로젝트 설명")
|
#kicker("프로젝트 소개")
|
||||||
|
|
||||||
#heading(level: 2)[
|
#heading(level: 2)[
|
||||||
#title
|
#title
|
||||||
@@ -446,6 +507,7 @@
|
|||||||
// Activity process
|
// Activity process
|
||||||
#let activity_process(title: "", content) = page(
|
#let activity_process(title: "", content) = page(
|
||||||
header: header_template(show_activity: true),
|
header: header_template(show_activity: true),
|
||||||
|
footer: footer_template(),
|
||||||
)[
|
)[
|
||||||
#kicker("시행착오")
|
#kicker("시행착오")
|
||||||
|
|
||||||
@@ -459,8 +521,9 @@
|
|||||||
// Activity result
|
// Activity result
|
||||||
#let activity_result(title: "", content) = page(
|
#let activity_result(title: "", content) = page(
|
||||||
header: header_template(show_activity: true),
|
header: header_template(show_activity: true),
|
||||||
|
footer: footer_template(),
|
||||||
)[
|
)[
|
||||||
#kicker("결론")
|
#kicker("결과")
|
||||||
|
|
||||||
#heading(level: 2)[
|
#heading(level: 2)[
|
||||||
#title
|
#title
|
||||||
@@ -469,6 +532,51 @@
|
|||||||
#content
|
#content
|
||||||
]
|
]
|
||||||
|
|
||||||
|
#let evidence() = context {
|
||||||
|
let cp = color-palette.get()
|
||||||
|
let ed = evidence-data.get()
|
||||||
|
|
||||||
|
page(
|
||||||
|
header: header_template(),
|
||||||
|
footer: footer_template(),
|
||||||
|
)[
|
||||||
|
#set heading(numbering: (..nums) => {
|
||||||
|
numbering("A", nums.at(1))
|
||||||
|
})
|
||||||
|
#show heading.where(level: 2): it => {
|
||||||
|
let num = counter(heading).display(
|
||||||
|
(..nums) => numbering("A", nums.at(1)),
|
||||||
|
)
|
||||||
|
|
||||||
|
block[
|
||||||
|
#num. #it.body
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#show table.cell.where(x: 0): set text(
|
||||||
|
weight: 700,
|
||||||
|
fill: cp.accent,
|
||||||
|
)
|
||||||
|
|
||||||
|
#heading(level: 1, numbering: none)[
|
||||||
|
증빙 자료
|
||||||
|
]
|
||||||
|
|
||||||
|
#let cnt = ed.len()
|
||||||
|
#for (name, data) in ed [
|
||||||
|
#heading(level: 2, supplement: "evidence")[#name]
|
||||||
|
#label("Evidence) " + name)
|
||||||
|
|
||||||
|
#table(
|
||||||
|
columns: (10em, 1fr),
|
||||||
|
..data,
|
||||||
|
)
|
||||||
|
|
||||||
|
#{ cnt -= 1 }
|
||||||
|
#if cnt > 0 { pagebreak() }
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
// ==================================================
|
// ==================================================
|
||||||
// Style Settings
|
// Style Settings
|
||||||
@@ -509,8 +617,8 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
set page(
|
set page(
|
||||||
paper: "a4",
|
paper: page-paper,
|
||||||
margin: (x: 2.8cm, y: 4.2cm),
|
margin: page-margin,
|
||||||
fill: color-surface,
|
fill: color-surface,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -524,25 +632,58 @@
|
|||||||
|
|
||||||
set par(
|
set par(
|
||||||
leading: 1.2em,
|
leading: 1.2em,
|
||||||
spacing: 2.4em,
|
spacing: 1.8em,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
show raw: set text(font: font-code)
|
||||||
|
|
||||||
show strong: set text(fill: cp.accent)
|
show strong: set text(fill: cp.accent)
|
||||||
|
|
||||||
|
// underline config
|
||||||
|
set underline(
|
||||||
|
offset: 2pt,
|
||||||
|
stroke: (paint: cp.accent, thickness: 1pt),
|
||||||
|
)
|
||||||
|
|
||||||
// list config
|
// list config
|
||||||
show list: set par(leading: 0.8em)
|
show list: set par(leading: 0.8em)
|
||||||
set list(
|
set list(
|
||||||
marker: ([--], [•]),
|
marker: ([--], [•]),
|
||||||
indent: 0.6em,
|
indent: 0.6em,
|
||||||
body-indent: 0.8em,
|
body-indent: 0.8em,
|
||||||
spacing: 1.2em,
|
spacing: 0.8em,
|
||||||
)
|
)
|
||||||
|
|
||||||
show enum: set par(leading: 0.8em)
|
show enum: set par(leading: 0.8em)
|
||||||
set enum(
|
set enum(
|
||||||
indent: 0.6em,
|
indent: 0.6em,
|
||||||
body-indent: 0.8em,
|
body-indent: 0.8em,
|
||||||
spacing: 1.2em,
|
spacing: 0.8em,
|
||||||
|
)
|
||||||
|
|
||||||
|
// image config
|
||||||
|
show image: set align(center)
|
||||||
|
show image: set par(
|
||||||
|
spacing: 1.6em,
|
||||||
|
)
|
||||||
|
|
||||||
|
// figure config
|
||||||
|
show figure: set text(
|
||||||
|
fill: color-ink-helper,
|
||||||
|
size: 10pt,
|
||||||
|
)
|
||||||
|
|
||||||
|
show figure.caption: it => {
|
||||||
|
"[ " + it.body + " ]"
|
||||||
|
}
|
||||||
|
|
||||||
|
// table config
|
||||||
|
show table: set text(size: 10pt)
|
||||||
|
|
||||||
|
set table(
|
||||||
|
inset: 0.8em,
|
||||||
|
align: left + top,
|
||||||
|
stroke: (paint: cp.rule),
|
||||||
)
|
)
|
||||||
|
|
||||||
// heading config
|
// heading config
|
||||||
@@ -569,7 +710,7 @@
|
|||||||
|
|
||||||
// outline config
|
// outline config
|
||||||
set outline(
|
set outline(
|
||||||
indent: 1em,
|
indent: 2em,
|
||||||
)
|
)
|
||||||
|
|
||||||
show outline: set par(leading: 1em)
|
show outline: set par(leading: 1em)
|
||||||
@@ -600,14 +741,16 @@
|
|||||||
set text(
|
set text(
|
||||||
font: font-body,
|
font: font-body,
|
||||||
size: 12pt,
|
size: 12pt,
|
||||||
weight: 700,
|
weight: 400,
|
||||||
fill: color-ink-secondary,
|
fill: color-ink-secondary,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if it.element.supplement.text != "evidence" {
|
||||||
block(above: 20pt)[
|
block(above: 20pt)[
|
||||||
#it
|
#it
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// render content
|
// render content
|
||||||
doc
|
doc
|
||||||
|
|||||||