W5 · DEBUG_KIT.md

자원분배 / KKT · 2601 TV화질최적화
W5_resource_alloc/DEBUG_KIT.md

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. 다시 빌드/실행 → 점수 변화 관찰