W5 · DEBUG_KIT.md
자원분배 / KKT · 2601 TV화질최적화
DEBUG_KIT — 2601 TV화질최적화 개선 힌트 출력 설계
목표: "이 한 줄을 찍으면 다음 시도에서 뭘 바꿔야 할지 보인다"
debug_snippet.cpp에 캡슐화된 함수로 제공. user.cpp에#include후 1줄 호출.
1. 무엇을 찍어야 하는가? — 6개 핵심 지표
1.1 SCORE 분해 (1회성, 루프 종료 후)
=== SCORE BREAKDOWN ===
Total Score : 176540
Sum Satisfaction: 179834.21 (avg 179.83/step)
Sum Power : 379790.45 (avg 379.79/step, budget 380.00)
└ signal power: 379627.90 (val^2 term)
└ switch cost : 162.55 (|Δval| term)
Power Slack : 9.55 total (0.0096/step) ← 얼마나 예산을 남겼는지
Implied Score : sat - 0.01*power = 176034.75
Local-search + : 505.25 ← 2-swap/SA로 추가 획득
- 읽는 법:
Power Slack이 크면 → greedy fill을 더 돌려야 함.Local-search +가 0이면 → swap이 효과 없는 상태(이미 최적).
1.2 예산 위반 카운터 (실시간)
[OVERBUDGET] t=347 power=380.0123 → 강제 trim 발생
[OVERBUDGET] count: 0 ← 0이 아니면 BUG
- 읽는 법: count>0이면 안전마진이 부족하거나 KKT λ가 너무 작음.
1.3 자원 사용률 (매 100스텝)
[t=100] power=379.94/380 (99.98%) sat=180.31 switch=0.18
[t=200] power=379.87/380 (99.97%) sat=179.55 switch=0.15
- 읽는 법: power%가 99% 미만으로 떨어지는 스텝이 많으면 → 그 스텝의 α 분포가 극단적(한 feature가 압도). 정규화 또는 cap 완화 검토(불가능하지만 진단용).
1.4 수렴 곡선 (루프 종료 후)
KKT-only score : 176034 (greedy fill 없이)
+greedy fill : 176510 (+476)
+2-swap : 176540 (+30)
+SA(2000/step) : 176540 (+0) ← 이미 수렴
- 읽는 법: 어느 단계에서 점수가 정체하는지 보고, 다음 시도에서 그 단계의 알고리즘을 강화(예: greedy fill이 +50이면 +500이 되도록 더 많은 패스).
1.5 입력 통계 (초기 1회)
=== INPUT STATS ===
Features: 20
Users: 800
Σpower: 1010 (min=13 max=80)
Σquality: 22.4 (min=0.10 max=2.50)
Median range: [127, 2487]
User dist: mean=1290 std=680 [min=105, max=2497]
Spot dist sequence: 623, 1845, 412, ... (changes every 10 steps)
- 읽는 법: Σpower가 크면(>1200) val 평균이 작아짐(예산 타이트). quality 편차가 크면 α 편차도 큼 → 소수 feature에 몰아주기 효과적.
1.6 제약 위반 카운터 (실시간)
[VIOLATION] out-of-range val: 0 times
[VIOLATION] screen_control calls: 1000/1000
[VIOLATION] power>380: 0 times
- 읽는 법: 모두 0이어야 합격. 하나라도 >0이면 −1 확정.
2. debug_snippet.cpp 사용법
// user.cpp 상단에 추가
#include "debug_snippet.cpp" // 또는 내용 직접 복붙
void process(){
// ...
dbg_init(); // 초기화 (TC 시작 시 1회)
dbg_input_stats(fp, fq, fm, ud0);
for(int t=0; t<1000; t++){
// ... val 계산 ...
dbg_step(t, val, prev, alpha, powerOf(val,prev));
screen_control(val);
}
dbg_summary(); // SCORE 분해 + 수렴 곡선 출력
}
출력은 모두 stderr로 → stdout(채점용)과 분리.
3. "다음 시도에서 뭘 바꿔야 할지" 의사결정표
| 관찰 | 시사점 | 다음 액션 |
|---|---|---|
| Power Slack > 100 | 예산을 너무 남김 | greedy fill 패스 증가, KKT λ 정밀도 ↑ |
| Power Slack < 1 | float 위험 | 안전 마진 0.5 추가 (val을 1 낮춤) |
| OVERBUDGET count > 0 | trim이 발생 → sat 손실 | λ upper bound ↑, 반올림 후 trim을 더 보수적으로 |
| switch cost > 1000 | val이 너무 자주 바뀜 | lookahead(윈도 3~5) 또는 val 평활화 |
| Local-search + < 10 | 이미 최적 근처 | SA 또는 다른 초기 λ 시도 |
| sat std(스텝 간) > 50 | α 변동 큼 | 특정 feature가 지배 → cap이 한계 |
| 총점 < 100,000 | KKT 미작동 | λ 범위 점검, fp/α 부호 점검 |
4. 실전 디버그 흐름 (시험장 5분 루틴)
1. dbg_summary() 1회 출력 → SCORE 분해 확인
2. Power Slack 확인:
- > 50 → greedy fill 패스 2배
- < 1 → 안전 마진 0.5 추가
3. OVERBUDGET count 확인:
- > 0 → λ upper 1e3 → 1e5, trim 로직 재검토
4. switch cost 확인:
- > 500 → lookahead 고려 (하지만 이 문제에선 보통 의미 없음)
5. 다시 빌드/실행 → 점수 변화 관찰