fix(havuz): B1 — tek dilime yığılan havuz 24'ün altına kırpılmasın [KARAR]
24 eşiği kırpmadan önce sayılıyor, sonra her dilim 20'ye iniyordu; havuzu tek dilimde toplanan adayın listesi 20–22 satırda kalıyordu (SAY 800 devlet sağlık: 617 güvenli program varken 22 satır). Kırpma artık dilim başına 20'yi korur ama toplam 24'ün altına düşecekse eksiği, adayı kalan dilimlerden tamamlar. Dağıtım kuralı dilimHedefiHesapla ile aynı (dengeli → güvenli → hayal) ve tek yardımcıda toplandı; 5/13/6 iskeleti ve hedef hesabı değişmedi. Kanıt (liste satırı, önce → sonra): a02 22 → 24 (1/1/22), a01 21 → 24, a11/a12/a17/b05 20 → 24. Diğer 33 profilde havuz birebir aynı. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,35 @@ export type DilimHedef = Record<Dilim, number>;
|
|||||||
// Listenin ideal iskeleti: 5 hayal / 13 dengeli / 6 garanti = 24 tercih.
|
// Listenin ideal iskeleti: 5 hayal / 13 dengeli / 6 garanti = 24 tercih.
|
||||||
export const IDEAL_HEDEF: DilimHedef = { hayal: 5, dengeli: 13, garanti: 6 };
|
export const IDEAL_HEDEF: DilimHedef = { hayal: 5, dengeli: 13, garanti: 6 };
|
||||||
|
|
||||||
|
const DILIM_ONCELIGI: Dilim[] = ["dengeli", "garanti", "hayal"];
|
||||||
|
|
||||||
|
function dilimSay(havuz: AdayProgram[]): DilimHedef {
|
||||||
|
const mevcut: DilimHedef = { hayal: 0, dengeli: 0, garanti: 0 };
|
||||||
|
for (const p of havuz) mevcut[p.dilim]++;
|
||||||
|
return mevcut;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `pay` toplamı `toplam`ın altındaysa eksiği, adayı kalan dilimlere birer
|
||||||
|
* birer dağıtır (öncelik: dengeli → garanti → hayal — omurga önce, sigorta
|
||||||
|
* sonra, en riskli dilim en son). `pay`'i yerinde günceller.
|
||||||
|
*/
|
||||||
|
function eksigiDagit(pay: DilimHedef, mevcut: DilimHedef, toplam: number) {
|
||||||
|
let eksik = toplam - pay.hayal - pay.dengeli - pay.garanti;
|
||||||
|
while (eksik > 0) {
|
||||||
|
let dagitildi = false;
|
||||||
|
for (const d of DILIM_ONCELIGI) {
|
||||||
|
if (eksik <= 0) break;
|
||||||
|
if (mevcut[d] > pay[d]) {
|
||||||
|
pay[d]++;
|
||||||
|
eksik--;
|
||||||
|
dagitildi = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dagitildi) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Havuzun gerçek dilim dağılımına uyarlanmış liste hedefi. İdeal 5/13/6'dan
|
* Havuzun gerçek dilim dağılımına uyarlanmış liste hedefi. İdeal 5/13/6'dan
|
||||||
* başlar; bir dilimde yeterli aday yoksa eksik, kapasitesi kalan dilimlere
|
* başlar; bir dilimde yeterli aday yoksa eksik, kapasitesi kalan dilimlere
|
||||||
@@ -84,30 +113,13 @@ export const IDEAL_HEDEF: DilimHedef = { hayal: 5, dengeli: 13, garanti: 6 };
|
|||||||
* Deterministiktir: aynı havuz her zaman aynı hedefi üretir.
|
* Deterministiktir: aynı havuz her zaman aynı hedefi üretir.
|
||||||
*/
|
*/
|
||||||
export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
|
export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
|
||||||
const mevcut: DilimHedef = { hayal: 0, dengeli: 0, garanti: 0 };
|
const mevcut = dilimSay(havuz);
|
||||||
for (const p of havuz) mevcut[p.dilim]++;
|
|
||||||
|
|
||||||
const toplamHedef = Math.min(HEDEF, havuz.length);
|
|
||||||
const hedef: DilimHedef = {
|
const hedef: DilimHedef = {
|
||||||
hayal: Math.min(IDEAL_HEDEF.hayal, mevcut.hayal),
|
hayal: Math.min(IDEAL_HEDEF.hayal, mevcut.hayal),
|
||||||
dengeli: Math.min(IDEAL_HEDEF.dengeli, mevcut.dengeli),
|
dengeli: Math.min(IDEAL_HEDEF.dengeli, mevcut.dengeli),
|
||||||
garanti: Math.min(IDEAL_HEDEF.garanti, mevcut.garanti),
|
garanti: Math.min(IDEAL_HEDEF.garanti, mevcut.garanti),
|
||||||
};
|
};
|
||||||
|
eksigiDagit(hedef, mevcut, Math.min(HEDEF, havuz.length));
|
||||||
const oncelik: Dilim[] = ["dengeli", "garanti", "hayal"];
|
|
||||||
let eksik = toplamHedef - hedef.hayal - hedef.dengeli - hedef.garanti;
|
|
||||||
while (eksik > 0) {
|
|
||||||
let dagitildi = false;
|
|
||||||
for (const d of oncelik) {
|
|
||||||
if (eksik <= 0) break;
|
|
||||||
if (mevcut[d] > hedef[d]) {
|
|
||||||
hedef[d]++;
|
|
||||||
eksik--;
|
|
||||||
dagitildi = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!dagitildi) break;
|
|
||||||
}
|
|
||||||
return hedef;
|
return hedef;
|
||||||
}
|
}
|
||||||
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama dilimin tamamını
|
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama dilimin tamamını
|
||||||
@@ -116,10 +128,22 @@ export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
|
|||||||
// modellerde prefill + isabet maliyeti yüksek (ölçüm: 6 Ağu 2026).
|
// modellerde prefill + isabet maliyeti yüksek (ölçüm: 6 Ağu 2026).
|
||||||
const PROMPT_LIMIT_DILIM = 20;
|
const PROMPT_LIMIT_DILIM = 20;
|
||||||
|
|
||||||
/** Dilim sırasını koruyarak her dilimden en fazla n aday bırakır. */
|
/**
|
||||||
|
* Dilim sırasını koruyarak her dilimden en fazla n aday bırakır. Havuz tek
|
||||||
|
* dilime yığılmışsa (ör. çok iyi derece: neredeyse hepsi güvenli) n'e kırpmak
|
||||||
|
* listeyi 24'ün altına düşürürdü; o durumda eksik, adayı kalan dilimlerden
|
||||||
|
* tamamlanır — yeterli program varken liste 24'ten kısa kalmaz.
|
||||||
|
*/
|
||||||
function dilimBasinaKirp(havuz: AdayProgram[], n: number): AdayProgram[] {
|
function dilimBasinaKirp(havuz: AdayProgram[], n: number): AdayProgram[] {
|
||||||
const sayac: Record<Dilim, number> = { hayal: 0, dengeli: 0, garanti: 0 };
|
const mevcut = dilimSay(havuz);
|
||||||
return havuz.filter((p) => ++sayac[p.dilim] <= n);
|
const pay: DilimHedef = {
|
||||||
|
hayal: Math.min(n, mevcut.hayal),
|
||||||
|
dengeli: Math.min(n, mevcut.dengeli),
|
||||||
|
garanti: Math.min(n, mevcut.garanti),
|
||||||
|
};
|
||||||
|
eksigiDagit(pay, mevcut, Math.min(HEDEF, havuz.length));
|
||||||
|
const sayac: DilimHedef = { hayal: 0, dengeli: 0, garanti: 0 };
|
||||||
|
return havuz.filter((p) => ++sayac[p.dilim] <= pay[p.dilim]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function dilimEkle(r: {
|
function dilimEkle(r: {
|
||||||
|
|||||||
Reference in New Issue
Block a user