İçeriğe geç
academia.sh

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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat