initial commit

This commit is contained in:
2026-05-26 09:25:11 -05:00
commit ebbeac2231
15 changed files with 3478 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
// PAIRS: indices into VARS array [0=안정성, 1=수익_가능성, 2=자금_효율성, 3=사업_계획_완성도, 4=사회적_가치]
const AHP_PAIRS = [
[0,1],[0,2],[0,3],[0,4],
[1,2],[1,3],[1,4],
[2,3],[2,4],
[3,4]
];
function buildMatrix(pairwiseResults) {
const n = 5;
const matrix = Array.from({length: n}, () => Array(n).fill(1));
AHP_PAIRS.forEach(([i, j], idx) => {
const val = pairwiseResults[idx];
if (val >= 1) {
matrix[i][j] = val;
matrix[j][i] = 1 / val;
} else {
matrix[j][i] = Math.abs(val);
matrix[i][j] = 1 / Math.abs(val);
}
});
return matrix;
}
function powerMethod(matrix, maxIter = 1000, threshold = 1e-6) {
const n = matrix.length;
let v = Array(n).fill(1 / n);
for (let iter = 0; iter < maxIter; iter++) {
const Av = Array(n).fill(0);
for (let i = 0; i < n; i++)
for (let j = 0; j < n; j++)
Av[i] += matrix[i][j] * v[j];
const sum = Av.reduce((a, b) => a + b, 0);
const vNew = Av.map(x => x / sum);
const maxDiff = Math.max(...vNew.map((x, k) => Math.abs(x - v[k])));
v = vNew;
if (maxDiff < threshold) break;
}
return v;
}
function calcConsistency(matrix, weights) {
const n = matrix.length;
const RI = [0, 0, 0.58, 0.90, 1.12, 1.24, 1.32, 1.41, 1.45];
const Aw = Array(n).fill(0);
for (let i = 0; i < n; i++)
for (let j = 0; j < n; j++)
Aw[i] += matrix[i][j] * weights[j];
const lambdaMax = Aw.reduce((s, v, i) => s + v / weights[i], 0) / n;
const CI = (lambdaMax - n) / (n - 1);
const CR = CI / RI[n];
return { lambdaMax, CI, CR };
}
function calcModelScore(caseData, weights) {
return VARS.reduce((sum, varKey, i) => sum + caseData[varKey] * weights[i], 0);
}
// Slider positions -4 to +4 map to AHP odd values 1,3,5,7,9
// pos=0 → 1 (equal)
// pos<0 → left more important, AHP value = 2*|pos|+1 (1,3,5,7,9)
// pos>0 → right more important, stored as negative: -(2*pos+1)
function sliderToStored(sliderPos) {
if (sliderPos === 0) return 1;
if (sliderPos < 0) return 2 * Math.abs(sliderPos) + 1;
return -(2 * sliderPos + 1);
}
function storedToSlider(val) {
if (val === 1) return 0;
if (val > 1) return -((val - 1) / 2); // left was important
return (Math.abs(val) - 1) / 2; // right was important
}
function ahpValueToScale(absVal) {
// absVal is 2-9
const levels = { 2: '2', 3: '3', 4: '4', 5: '5', 6: '6', 7: '7', 8: '8', 9: '9' };
return absVal;
}
function runAHP(pairwiseRaw) {
const matrix = buildMatrix(pairwiseRaw);
const weights = powerMethod(matrix);
const { lambdaMax, CI, CR } = calcConsistency(matrix, weights);
return { weights, lambdaMax, CI, CR, matrix };
}