Ders 18 / 21
Bütçe ve Uyarılar
Beklenmedik harcamanın ne zaman fark edildiği ölçülür: kurgu bir harcama eğrisine yerleştirilen beş artış üzerinde eşik uyarısı ikisini tamamen kaçırır ve ilk tetiklendiğinde aylık bütçenin %86,4'ü çoktan harcanmıştır (kalan 204 birim), tahmin uyarısı beşini de yakalar ama doksan günün 63'ünde açık kalır. Eşik ailesinin en iyi ayarı 821 birim kaçırırken tahmin ailesi 12,5 birimde kalır; yanlış alarm farkı ise 21 olaysız günde 5'e karşı 6'dır. Faturanın gecikmesi doğrudan fark etme gününe eklenir: üç günlük gecikme ortalama gecikmeye 1,8 gün ve kaçırılan tutara 210 birim ekler, sekiz günlük gecikmede bir artış tamamen kaçar.
İçindekiler
Önceki iki ders faturayı kalemlere ayırdı ve bir sahiplik denemesi yaptı; ikisi de fatura geldikten sonra yapılır. Ay kapandığında hangi ekibin ne kadar harcadığı bilinir ve harcama çoktan yapılmıştır.
Bu dersin sorusu ay içindedir: harcama beklenmedik biçimde yükseldiğinde bunun kaçıncı gün fark edildiği ve yükselişin hiç fark edilmeden bitip bitmediği sayılır.
Harcama Eğrisi ve İki Uyarı
Örnek kurgu bölgesel ölçüm ağıdır: beş ekip, üç ortam, kiralanmış makine, ağ, depo, işlev ve
kenar katmanı. Eğri, bütçe ve olaylar kurgudur; para birimi de kurgudur ve br diye anılır,
gerçek bir fiyat listesine karşılık gelmez.
BM13: aylık bütçe 1.500 birimdir ve üç ay izlenir. BM14: eğri bir tabandan, haftalık bir desenden, yavaş bir büyümeden ve gürültüden oluşur; büyüme yüzünden olaysız taban da bütçeye yaklaşır. BM15: eğriye beş artış yerleştirilir; dördü başladığı ayda biter. BM16: eşik uyarısı ay birikiminin bütçeye oranına bakar, tahmin uyarısı son k günün ortalamasıyla ay sonunu öngörür. BM17: koşulun doğru olduğu her gün bir uyarı günüdür; artış sürerken gelen uyarı fark etme, artışsız günde gelen uyarı yanlış alarmdır. BM18: fatura gecikmeli gelir — g. günün kararı g−gecikme verisiyle alınır, yani koşul tam olarak gecikme kadar kayar.
Eğri, bütçe ve uyarılar süreç içi bir modeldir; gerçek bir harcama kaydı yoktur.
// maliyet/harcama.mjs — gunluk harcama egrisi modeli: gercek bir harcama kaydi yok. Egri bir // tabandan, haftalik desenden, yavas buyumeden ve elle yerlestirilmis *olaylardan* olusur; // gurultu bir uretecten gelir, tohum gorunurdur, para birimi kurgudur ve `br` diye anilir. // Olaysiz taban ayrica hesaplanir: bir olayin *fazla* harcamasi ikisinin farkidir. export const TOHUM = 3391; export const AY = 3, AY_GUN = 30, GUN = AY * AY_GUN; export const BUTCE = 1500; // aylik butce (br, kurgu) export const TABAN = 43, BUYUME = 0.12; // gunluk taban ve gunluk yavas buyume export const HAFTA = [1.08, 1.10, 1.06, 1.04, 0.98, 0.72, 0.68]; // olay: ad, baslangic gunu (1..90), sure (gun), gunluk fazla br export const OLAY = [ ["gelistirme ortami acik kaldi", 9, 22, 12.5], ["gecici veri kopyasi", 38, 6, 41.0], ["yanlis boyut secimi", 47, 40, 9.0], ["kenar dugumu cogaltma", 63, 25, 7.5], ["yeniden isleme dongusu", 71, 4, 63.0], ]; export const aktif = (g) => OLAY.filter(([, b, s]) => g >= b && g < b + s); export const seri = (olayli = true) => { let s = TOHUM >>> 0; const r = () => { s = (Math.imul(s, 1664525) + 1013904223) >>> 0; return s / 4294967296; }; const v = [0]; for (let g = 1; g <= GUN; g++) { const gurultu = 0.94 + r() * 0.12; let x = (TABAN + BUYUME * (g - 1)) * HAFTA[(g - 1) % 7] * gurultu; if (olayli) for (const [, , , f] of aktif(g)) x += f; v.push(Math.round(x * 100) / 100); } return v; }; export const ay = (g) => Math.floor((g - 1) / AY_GUN); export const ayGunu = (g) => ((g - 1) % AY_GUN) + 1; export const topla = (v) => v.reduce((a, b) => a + b, 0);
// maliyet/uyari.mjs — iki uyari turu ayni egri uzerinde kosar: esik uyarisi (butcenin yuzdesi) // ve tahmin uyarisi (ay sonu ongorusu). Sayilan sey uyarinin dogrulugu degil *fark etme // gunudur*: olay basladiktan kac gun sonra uyari geliyor, o gunlerde ne kadar fazla harcaniyor. // Fatura gecikmesi bir kisittir: g. gunun karari g-GECIKME verisiyle alinir, yani kosul tam // olarak GECIKME gun kayar. Kosulun dogru oldugu her gun bir *uyari gunudur*. import { OLAY, BUTCE, AY, AY_GUN, GUN, TOHUM, aktif, seri, ay, ayGunu, topla } from "./harcama.mjs"; const yaz = (g, ...s) => console.log(s.map((v, i) => (g[i] < 0 ? String(v).padEnd(-g[i]) : String(v).padStart(g[i]))).join("")); const t0 = process.hrtime.bigint(); const E = seri(), T = seri(false); const fazla = (g) => E[g] - T[g]; const ayTop = (v, a) => topla(v.slice(a * AY_GUN + 1, (a + 1) * AY_GUN + 1)); const birikim = (a, g) => topla(E.slice(a * AY_GUN + 1, a * AY_GUN + g + 1)); console.log(`tohum ${TOHUM}: ${GUN} gun, aylik butce ${BUTCE} br (kurgu para birimi)`); console.log("\n1. harcama egrisi: butce, gerceklesen, asim"); const A = [5, 10, 14, 11, 10, 13]; yaz(A, "ay", "butce", "gerceklesen", "asim", "olaysiz", "olay fazlasi"); for (let a = 0; a < AY; a++) { const ger = ayTop(E, a), tab = ayTop(T, a); yaz(A, a + 1, BUTCE, ger.toFixed(1), (ger - BUTCE).toFixed(1), tab.toFixed(1), (ger - tab).toFixed(1)); } const kisa = OLAY.filter(([, b, s2]) => ay(b) === ay(b + s2 - 1)).length; console.log(` ${OLAY.length} olay yerlestirildi (yukaridaki modelde); ${kisa} tanesi ` + `basladigi ayda biter`); // bir uyari turunu egri uzerinde kostur; donen sey tetiklenen gunlerin listesi const kostur = (tur, p, gecikme) => { const gunler = []; for (let g = 1; g <= GUN; g++) { const v = g - gecikme; // faturadan okunabilen son gun if (v < 1) continue; const a = ay(v), d = ayGunu(v); if (ay(g) !== a) continue; // ay siniri: karar ayin kendi verisiyle const bir = birikim(a, d); let tetik = false; if (tur === "esik") tetik = bir / BUTCE >= p; else { if (d < p) continue; const ort = topla(E.slice(a * AY_GUN + d - p + 1, a * AY_GUN + d + 1)) / p; tetik = bir + ort * (AY_GUN - d) > BUTCE; } if (tetik) gunler.push(g); } return gunler; }; // uyari gunlerini olaylara esle: fark etme gunu, gecikme, gecikmede harcanan fazla const olc = (gunler) => { const bulunan = OLAY.map(([, b, s]) => { const g = gunler.find((x) => x >= b && x < b + s); return g === undefined ? null : g; }); const yanlis = gunler.filter((g) => aktif(g).length === 0).length; const kacan = OLAY.filter((_, i) => bulunan[i] === null).length; const gecikmeler = bulunan.map((g, i) => (g === null ? null : g - OLAY[i][1])); const kacirilan = topla(OLAY.map(([, b, s], i) => { const son = bulunan[i] === null ? b + s : bulunan[i]; let x = 0; for (let g = b; g < son; g++) x += fazla(g); return x; })); const gec = gecikmeler.filter((x) => x !== null); return { bulunan, gecikmeler, yanlis, kacan, kacirilan, toplam: gunler.length, ortGecikme: gec.length ? topla(gec) / gec.length : NaN }; }; console.log("\n2. iki uyari turu, ayni egri (esik %85, tahmin penceresi 7 gun, gecikme 0)"); const es = kostur("esik", 0.85, 0), ta = kostur("tahmin", 7, 0); const oE = olc(es), oT = olc(ta); const C = [-30, 8, 11, 10, 13, 10]; yaz(C, "olay", "baslar", "esik gunu", "gecikme", "tahmin gunu", "gecikme"); for (let i = 0; i < OLAY.length; i++) { const g = (x) => (x === null ? "kacti" : x); yaz(C, OLAY[i][0], OLAY[i][1], g(oE.bulunan[i]), g(oE.gecikmeler[i]), g(oT.bulunan[i]), g(oT.gecikmeler[i])); } // uyari geldigi gun butcenin ne kadari harcanmis: her ayin ilk uyari gunu const kalan = (gunler) => { const ilk = []; for (let a = 0; a < AY; a++) { const g = gunler.find((x) => ay(x) === a); if (g !== undefined) ilk.push(birikim(a, ayGunu(g)) / BUTCE); } return topla(ilk) / ilk.length; }; for (const [ad, o, g] of [["esik", oE, es], ["tahmin", oT, ta]]) { console.log(` ${ad.padEnd(7)} ${o.toplam} uyari gunu, ${OLAY.length - o.kacan} olay ` + `yakalandi, ${o.kacan} kacti, ${o.yanlis} yanlis alarm gunu`); console.log(` ${" ".repeat(7)} gecikmede ${o.kacirilan.toFixed(1)} br fazla; ilk uyari ` + `gunu butcenin %${(100 * kalan(g)).toFixed(1)}'i harcanmis, kalan ` + `${(BUTCE * (1 - kalan(g))).toFixed(0)} br`); } console.log("\n3. parametre taramasi: uyari ne kadar erken, kac yanlis alarm"); const D = [-9, 9, 12, 10, 8, 14, 8, 14]; yaz(D, "tur", "ayar", "uyari gunu", "yakalanan", "kacan", "ort. gecikme", "yanlis", "kacirilan br"); const ayarlar = [["esik", 0.60], ["esik", 0.85], ["esik", 1.00], ["tahmin", 3], ["tahmin", 7], ["tahmin", 14]]; const skor = []; for (const [tur, p] of ayarlar) { const o = olc(kostur(tur, p, 0)); skor.push([tur, p, o]); yaz(D, tur, tur === "esik" ? `%${(100 * p).toFixed(0)}` : `${p} gun`, o.toplam, OLAY.length - o.kacan, o.kacan, isNaN(o.ortGecikme) ? "-" : o.ortGecikme.toFixed(1), o.yanlis, o.kacirilan.toFixed(1)); } const enIyi = skor.reduce((a, b) => (b[2].kacirilan < a[2].kacirilan ? b : a)); console.log(` en az kaciran ayar: ${enIyi[0]} ${enIyi[1]}, ` + `${enIyi[2].kacirilan.toFixed(1)} br, ${enIyi[2].yanlis} yanlis alarm gunu`); const olaysiz = [...Array(GUN)].map((_, i) => i + 1).filter((g) => aktif(g).length === 0).length; console.log(` olaysiz gun ${olaysiz}/${GUN}: yanlis alarm ancak bu gunlerde olabilir`); console.log(` ${enIyi[0]} ${enIyi[1]} uyariyi ${GUN} gunun ${enIyi[2].toplam} gununde ` + `(%${(100 * enIyi[2].toplam / GUN).toFixed(0)}) acik tutuyor`); console.log("\n4. faturanin gecikmesi bir kisit: karar g-gecikme verisiyle alinir"); const F = [9, 12, 8, 14, 14, 13]; yaz(F, "gecikme", "uyari gunu", "kacan", "ort. gecikme", "kacirilan br", "butceye pay"); for (const gk of [0, 1, 3, 5, 8]) { const o = olc(kostur(enIyi[0], enIyi[1], gk)); yaz(F, `${gk} gun`, o.toplam, o.kacan, isNaN(o.ortGecikme) ? "-" : o.ortGecikme.toFixed(1), o.kacirilan.toFixed(1), `%${(100 * o.kacirilan / (AY * BUTCE)).toFixed(1)}`); } const g0 = olc(kostur(enIyi[0], enIyi[1], 0)), g3 = olc(kostur(enIyi[0], enIyi[1], 3)); console.log(` 3 gunluk gecikme fark etme gunune ${(g3.ortGecikme - g0.ortGecikme).toFixed(1)} ` + `gun ekliyor ve kacirilan tutari ${(g3.kacirilan - g0.kacirilan).toFixed(1)} br buyutuyor`); const ms = Number(process.hrtime.bigint() - t0) / 1e6; // ham olcum basilmaz, bant denetlenir console.log(`\ndort tablo 2000 ms bandinda: ${ms < 2000 ? "evet" : "hayir"}`);
tohum 3391: 90 gun, aylik butce 1500 br (kurgu para birimi)
1. harcama egrisi: butce, gerceklesen, asim
ay butce gerceklesen asim olaysiz olay fazlasi
1 1500 1553.7 53.7 1278.7 275.0
2 1500 1752.8 252.8 1380.8 372.0
3 1500 2137.0 637.0 1463.5 673.5
5 olay yerlestirildi (yukaridaki modelde); 4 tanesi basladigi ayda biter
2. iki uyari turu, ayni egri (esik %85, tahmin penceresi 7 gun, gecikme 0)
olay baslar esik gunu gecikme tahmin gunu gecikme
gelistirme ortami acik kaldi 9 25 16 15 6
gecici veri kopyasi 38 kacti kacti 38 0
yanlis boyut secimi 47 52 5 47 0
kenar dugumu cogaltma 63 77 14 67 4
yeniden isleme dongusu 71 kacti kacti 71 0
esik 29 uyari gunu, 3 olay yakalandi, 2 kacti, 3 yanlis alarm gunu
gecikmede 1292.0 br fazla; ilk uyari gunu butcenin %86.4'i harcanmis, kalan 204 br
tahmin 63 uyari gunu, 5 olay yakalandi, 0 kacti, 6 yanlis alarm gunu
gecikmede 141.0 br fazla; ilk uyari gunu butcenin %34.6'i harcanmis, kalan 982 br
3. parametre taramasi: uyari ne kadar erken, kac yanlis alarm
tur ayar uyari gunu yakalanan kacan ort. gecikme yanlis kacirilan br
esik %60 46 4 1 5.5 5 821.0
esik %85 29 3 2 11.7 3 1292.0
esik %100 17 3 2 15.7 3 1440.0
tahmin 3 gun 68 5 0 0.2 6 12.5
tahmin 7 gun 63 5 0 2.0 6 141.0
tahmin 14 gun 47 4 1 5.8 6 967.5
en az kaciran ayar: tahmin 3, 12.5 br, 6 yanlis alarm gunu
olaysiz gun 21/90: yanlis alarm ancak bu gunlerde olabilir
tahmin 3 uyariyi 90 gunun 68 gununde (%76) acik tutuyor
4. faturanin gecikmesi bir kisit: karar g-gecikme verisiyle alinir
gecikme uyari gunu kacan ort. gecikme kacirilan br butceye pay
0 gun 68 0 0.2 12.5 %0.3
1 gun 65 0 0.8 82.5 %1.8
3 gun 59 0 2.0 222.5 %4.9
5 gun 53 0 3.2 362.5 %8.1
8 gun 44 1 4.3 490.5 %10.9
3 gunluk gecikme fark etme gunune 1.8 gun ekliyor ve kacirilan tutari 210.0 br buyutuyor
dort tablo 2000 ms bandinda: evet
Sayılar ölçüm sınıfındadır; girdileri yukarıdaki varsayımlardır.
Eşik Uyarısı Bütçeyi Bekliyor
Birinci tablo üç ayın da aşıldığını gösterir: 53,7 — 252,8 — 637,0 birim. Kaynağı yalnız artışlar değildir; olaysız taban 1.278,7’den 1.463,5’e tırmanır ve üçüncü ayda bütçenin %97,6’sını doldurur.
İkinci tablo eşik uyarısını %85’te koşturur. İki artış tamamen kaçar: geçici veri kopyası (6 gün, günlük 41 birim) ve yeniden işleme döngüsü (4 gün, günlük 63 birim). İkisi de kısa ve büyüktür, ama birikimi eşiğin üstüne taşımadan biter. Uzun ve küçük olanlar 16 ve 14 gün sonra fark edilir; toplam kaçırılan 1.292 birim.
Nedeni ölçünün kendisindedir: eşik uyarısı artışa değil birikime bakar. İlk tetiklendiği gün bütçenin ortalama %86,4’ü harcanmıştır, kalan 204 birimdir — bu bir uyarı değil, bir kalan bildirimidir. Eşik %60’a indirildiğinde bile kaçırılan 821 birimdir ve kısa artışlardan biri hâlâ kaçar.
Tahmin Uyarısı Erken ama Sürekli
Tahmin uyarısı yedi günlük pencerede beş artışın beşini de yakalar. İki kısa artışı başladığı gün (38 ve 71) fark eder, çünkü son yedi günün ortalaması bir günde sıçrar ve ay sonu öngörüsü bütçeyi aşar. Kaçırılan tutar 1.292’den 141 birime iner ve ilk uyarı geldiğinde bütçenin yalnız %34,6’sı harcanmıştır — kalan 982 birim.
Üçüncü tablo bedelini gösterir. Pencere üç güne indirildiğinde kaçırılan tutar 12,5 birime düşer, ama uyarı 90 günün 68’inde (%76) açık kalır. Yanlış alarm bunu açıklamaz: 21 artışsız günün 6’sında yanlış alarm vardır, eşik %60’ta 5’inde. İki ailenin ayrıldığı yer yanlış alarm değil doğru alarmın süresidir; tahmin uyarısı artış sürdükçe bağırır ve ayırt edici olmaktan çıkar, eşik uyarısı sessizdir ama geç konuşur.
Pencere on dörde çıkarıldığında tahmin uyarısı eşiğe benzer: bir artış kaçar, kaçırılan 967,5 birime çıkar.
Faturanın Gecikmesi Bir Tavan Koyuyor
Dördüncü tablo tek bir kısıtı ekler: fatura verisi gecikmeli gelir. Uyarı koşulu tam olarak gecikme kadar kaydığı için fark etme günü de aynı miktarda kayar. Ortalama gecikme 0,2 günden 1 günde 0,8’e, 3 günde 2,0’a, 8 günde 4,3’e çıkar.
Kaçırılan tutar buna doğrusal eşlik eder: 12,5 — 82,5 — 222,5 — 362,5 — 490,5 birim. Sekiz günlük gecikmede dört günlük yeniden işleme döngüsü veri görünmeden biter ve tamamen kaçar; üç aylık bütçenin %10,9’u fark edilmeden harcanır.
Bu bir tavandır: uyarı ayarı ne kadar iyileştirilirse iyileştirilsin, fark etme günü fatura gecikmesinin altına inemez. Gecikme ayrı bir kalem de üretmez — bütçeden düşen tutar aynıdır, yalnız uyarı gelmeden önce harcanmıştır.
Özet
- Üç ayın üçü de aşılır (53,7 — 252,8 — 637,0 birim); aşımın bir kısmı artıştan değil taban büyümesinden gelir — olaysız taban üçüncü ayda bütçenin %97,6’sını doldurur.
- Eşik uyarısı birikime bakar, artışa değil: %85 eşiğinde iki kısa artış tamamen kaçar ve ilk tetiklendiğinde bütçenin %86,4’ü harcanmıştır — kalan 204 birim. Kaçırılan toplam 1.292 birim.
- Tahmin uyarısı yedi günlük pencerede beş artışın beşini yakalar ve kaçırılanı 141 birime indirir; ilk uyarı geldiğinde bütçeden 982 birim kalmıştır.
- Erken fark etmenin bedeli yanlış alarm değil süredir: üç günlük pencere kaçırılanı 12,5 birime indirirken uyarıyı 90 günün 68’inde (%76) açık tutar; yanlış alarm 21 artışsız günün 6’sındadır, eşik %60’ta 5’indedir.
- Fatura gecikmesi fark etme gününe doğrudan eklenir: 3 günlük gecikme ortalamaya 1,8 gün ve kaçırılana 210 birim ekler; 8 günlük gecikmede dört günlük bir artış hiç görülmez ve üç aylık bütçenin %10,9’u fark edilmeden harcanır.
Sonraki Adım
Uyarı, harcamanın beklenmedik biçimde yükseldiğini söyler; beklenen harcama hakkında hiçbir şey söylemez. Taban her ay bütçeye biraz daha yaklaşırken hiçbir uyarı tetiklenmez, çünkü ortada bir olay yoktur — yalnızca gereğinden büyük kurulmuş kaynaklar vardır. Sıradaki ders o tabana bakar: kullanım geçmişinden küçültme adayları çıkarılır ve her adayın tasarrufu ile riski birlikte sayılır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.