W5 · debug_snippet.cpp
자원분배 / KKT · 2601 TV화질최적화
// ============================================================================
// debug_snippet.cpp — 2601 TV화질최적화 개선 힌트 출력 키트
// ============================================================================
// 사용법: user.cpp 상단에 이 파일 전체를 복붙(또는 #include).
// process() 시작에 dbg_init(), 매 스텝에 dbg_step(),
// 루프 종료 후 dbg_summary() 호출.
// 모든 출력은 stderr (stdout과 분리 → 채점 영향 없음).
// ============================================================================
#ifndef DBG_2601_H
#define DBG_2601_H
#include <cstdio>
#include <cmath>
#include <cstring>
namespace dbg2601 {
constexpr int DBG_TL = 1000;
constexpr int DBG_MF = 20;
constexpr int DBG_MU = 800;
constexpr double DBG_BUDGET = 380.0;
// ─── 상태 ───
static double sumSat_ = 0.0;
static double sumPower_ = 0.0;
static double sumSwitch_ = 0.0;
static double minPower_ = 1e18;
static double maxPower_ = 0.0;
static int overbudgetCnt_= 0;
static int outrangeCnt_ = 0;
static int stepCnt_ = 0;
// 단계별 점수 기록 (수렴 곡선용)
static double stageKkt_ = 0.0;
static double stageGreedy_ = 0.0;
static double stageSwap_ = 0.0;
static bool stageRecorded_ = false;
// ─── 초기화 ───
inline void init() {
sumSat_ = sumPower_ = sumSwitch_ = 0.0;
minPower_ = 1e18; maxPower_ = 0.0;
overbudgetCnt_ = outrangeCnt_ = stepCnt_ = 0;
stageKkt_ = stageGreedy_ = stageSwap_ = 0.0;
stageRecorded_ = false;
}
// ─── 입력 통계 (TC 시작 후 1회) ───
inline void inputStats(const int* fp, const double* fq, const int* fm, const int* ud) {
int pmin=1e9, pmax=0, psum=0;
double qmin=1e9, qmax=0, qsum=0;
int mmin=1e9, mmax=0;
for (int i = 0; i < DBG_MF; i++) {
if (fp[i] < pmin) pmin = fp[i];
if (fp[i] > pmax) pmax = fp[i];
psum += fp[i];
if (fq[i] < qmin) qmin = fq[i];
if (fq[i] > qmax) qmax = fq[i];
qsum += fq[i];
if (fm[i] < mmin) mmin = fm[i];
if (fm[i] > mmax) mmax = fm[i];
}
double umean=0, umin=1e9, umax=0;
for (int i = 0; i < DBG_MU; i++) {
umean += ud[i];
if (ud[i]<umin) umin=ud[i];
if (ud[i]>umax) umax=ud[i];
}
umean /= DBG_MU;
double uvar=0;
for (int i = 0; i < DBG_MU; i++) uvar += (ud[i]-umean)*(ud[i]-umean);
double ustd = sqrt(uvar/DBG_MU);
fprintf(stderr, "\n=== INPUT STATS ===\n");
fprintf(stderr, "Features: %d, Users: %d\n", DBG_MF, DBG_MU);
fprintf(stderr, "power: sum=%d min=%d max=%d\n", psum, pmin, pmax);
fprintf(stderr, "quality: sum=%.2f min=%.2f max=%.2f\n", qsum, qmin, qmax);
fprintf(stderr, "median range: [%d, %d]\n", mmin, mmax);
fprintf(stderr, "user dist: mean=%.0f std=%.0f [min=%d, max=%d]\n", umean, ustd, (int)umin, (int)umax);
if (psum > 1200) fprintf(stderr, " [HINT] Σpower 큼 → val 평균 작아짐, 예산 타이트\n");
if (qmax - qmin > 2.0) fprintf(stderr, " [HINT] quality 편차 큼 → 소수 feature 집중 효과적\n");
}
// ─── 매 스텝 호출 ───
// val: 이번 스텝에 제출할 값, prev: 이전 스텝 값, alpha: sat 선형계수, power: 이번 power
inline void step(int t, const int* val, const int* prev, const double* alpha, double power) {
// 위반 카운트
if (power > DBG_BUDGET) overbudgetCnt_++;
for (int i = 0; i < DBG_MF; i++) {
if (val[i] < 0 || val[i] > 100) { outrangeCnt_++; break; }
}
// sat / power / switch 누적
double sat = 0.0;
for (int i = 0; i < DBG_MF; i++) sat += alpha[i] * val[i];
double sw = 0.0;
for (int i = 0; i < DBG_MF; i++) { int d = val[i]-prev[i]; if (d<0) d=-d; sw += d*0.01; }
sumSat_ += sat;
sumPower_ += power;
sumSwitch_ += sw;
if (power < minPower_) minPower_ = power;
if (power > maxPower_) maxPower_ = power;
stepCnt_++;
// 100스텝마다 자원 사용률
if (t % 100 == 0 || t == DBG_TL-1) {
fprintf(stderr, "[t=%4d] power=%.2f/380 (%.2f%%) sat=%.3f switch=%.3f\n",
t, power, power/DBG_BUDGET*100.0, sat, sw);
}
// 예산 위반 시 즉시 경고
if (power > DBG_BUDGET) {
fprintf(stderr, " [OVERBUDGET] t=%d power=%.4f\n", t, power);
}
}
// ─── 수렴 곡선 기록 (선택) ───
// 각 최적화 단계(KKT/greedy/swap) 후 점수를 기록해서 어디서 점수가 오르는지 추적.
inline void recordStage(const char* stageName, double satAtStage, double powerAtStage) {
double s = satAtStage - 0.01 * powerAtStage;
if (strcmp(stageName,"kkt")==0) stageKkt_ = s;
else if (strcmp(stageName,"greedy")==0) stageGreedy_ = s;
else if (strcmp(stageName,"swap")==0) stageSwap_ = s;
}
// ─── 요약 출력 (루프 종료 후 1회) ───
inline void summary(long long finalScore) {
double implied = sumSat_ - 0.01 * sumPower_;
double slack = (DBG_BUDGET * stepCnt_) - sumPower_;
fprintf(stderr, "\n=== SCORE BREAKDOWN ===\n");
fprintf(stderr, "Total Score : %lld\n", finalScore);
fprintf(stderr, "Sum Satisfaction: %.2f (avg %.4f/step)\n", sumSat_, sumSat_/stepCnt_);
fprintf(stderr, "Sum Power : %.2f (avg %.4f/step, budget %.2f)\n",
sumPower_, sumPower_/stepCnt_, DBG_BUDGET);
fprintf(stderr, " └ switch cost : %.2f (avg %.4f/step)\n", sumSwitch_, sumSwitch_/stepCnt_);
fprintf(stderr, "Power Slack : %.2f total (%.4f/step)\n", slack, slack/stepCnt_);
fprintf(stderr, "Implied Score : sat - 0.01*power = %.2f\n", implied);
fprintf(stderr, "Local-search + : %.2f\n", (double)finalScore - implied);
fprintf(stderr, "Power range : [%.2f, %.2f]\n", minPower_, maxPower_);
fprintf(stderr, "\n=== CONSTRAINT CHECK ===\n");
fprintf(stderr, "screen_control calls: %d/1000 %s\n", stepCnt_, stepCnt_==1000?"OK":"FAIL");
fprintf(stderr, "power>380 violations: %d %s\n", overbudgetCnt_, overbudgetCnt_==0?"OK":"BUG");
fprintf(stderr, "out-of-range val : %d %s\n", outrangeCnt_, outrangeCnt_==0?"OK":"BUG");
if (stageRecorded_ || stageKkt_>0 || stageGreedy_>0 || stageSwap_>0) {
fprintf(stderr, "\n=== CONVERGENCE CURVE (1-step sample) ===\n");
fprintf(stderr, "KKT-only score : %.2f\n", stageKkt_);
fprintf(stderr, "+greedy fill : %.2f (%+.2f)\n", stageGreedy_, stageGreedy_-stageKkt_);
fprintf(stderr, "+2-swap : %.2f (%+.2f)\n", stageSwap_, stageSwap_-stageGreedy_);
}
// 자동 진단 힌트
fprintf(stderr, "\n=== DIAGNOSTIC HINTS ===\n");
if (slack > 50) fprintf(stderr, " [HINT] Power Slack > 50 → greedy fill 패스 증가, KKT λ 정밀도 ↑\n");
if (slack < 1) fprintf(stderr, " [HINT] Power Slack < 1 → float 위험, 안전 마진 0.5 추가 (val 1 감소)\n");
if (overbudgetCnt_ > 0) fprintf(stderr, " [HINT] OVERBUDGET > 0 → λ upper ↑, trim 로직 보수화\n");
if (sumSwitch_ > 1000) fprintf(stderr, " [HINT] switch cost > 1000 → val 평활화 또는 short lookahead\n");
if (finalScore - implied < 10) fprintf(stderr, " [HINT] local-search + < 10 → 이미 최적 근처, SA 또는 다른 초기 λ 시도\n");
}
} // namespace dbg2601
// ─── C-스타일 짧은 래퍼 (원하면 사용) ───
inline void dbg_init() { dbg2601::init(); }
inline void dbg_input_stats(const int* fp, const double* fq, const int* fm, const int* ud) {
dbg2601::inputStats(fp, fq, fm, ud);
}
inline void dbg_step(int t, const int* val, const int* prev, const double* alpha, double power) {
dbg2601::step(t, val, prev, alpha, power);
}
inline void dbg_summary(long long finalScore) { dbg2601::summary(finalScore); }
#endif // DBG_2601_H