W5 · debug_snippet.cpp

자원분배 / KKT · 2601 TV화질최적화
W5_resource_alloc/debug_snippet.cpp
// ============================================================================
// 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