İçeriğe geç
academia.sh

Ders 08 / 10

Tahmin ve Planlama

Belirsizlik altında iş kalemi süresinin tahmini: üç aşamada üretilen tahminlerin gerçekleşen akış süresiyle karşılaştırılması, hata payı ile yanlılığın ayrılması, belirsizlik konisinin aşama aşama daralması, tahmine göre kurulan planın kaç kalemde tuttuğu ve tek kalem tahmini ile toplam tahmini arasındaki farkın hataların birbirini götürüp götürmemesiyle açıklanması.

İçindekiler

Önceki iki derste her iş kaleminin dokunduğu modüller baştan biliniyordu. El değiştirme, bekleme ve yeniden iş bu bilgiden hesaplandı. Gerçekte bir kalem başlarken bu bilgi yoktur: kalemin ne kadar süreceği tahmin edilir, ve tahmin iş ilerledikçe değişir.

Mimari kararların etkisi daha önce tahmin edilmişti; burada tahmin edilen şey iş kaleminin süresidir ve ölçüsü turdur. Ders bir tahmin kümesini gerçekleşen sürelerle karşılaştırır ve dört sayı üretir: hata payı, hatanın yönü, belirsizlik konisinin aşama aşama daralması ve tahmine göre kurulan planın kaç kalemde tuttuğu. Sonra tek kalem tahmini ile toplam tahmininin neden farklı davrandığı sayılır. Through-line bölgesel kütüphane ağıdır ve kurgudur.

Tahmin Edilen ile Gerçekleşen

ET15 — önceki iki dersin akış odaklı düzeni ve on iki iş kalemi değişmeden kullanılır. ET16 — bir kalemin gerçekleşen süresi, önceki derslerin akış modelinden gelir: çalışma turu artı bekleme turu artı yeniden işin eklediği turlar. Gerekçe: tahminin karşılaştırılacağı “gerçek” bir yerden gelmelidir; burada o yer daha önce koşturulmuş ve sayıları basılmış modeldir, yani gerçekleşen süre bu ders için bağımsız bir veri değil, aynı modelin çıktısıdır.

ET17 — sapma bir üreteçten çekilir; üreteç doğrusal eşlemedir ve tohumu 20250401 olarak görünürdür. Gerekçe: tahmin hatasının rastgele bir bileşeni vardır ve koşum tekrarlanabilir olmalıdır.

ET18 — üç tahmin aşaması vardır. Aşama 1’de yalnız kalemin başlığı bilinir ve dokunulacak modül sayısı ±%80 sapmayla tahmin edilir. Aşama 2’de kalem incelenmiştir, dokunulan modüller tam bilinir, sapma ±%30’dur. Aşama 3’te ilk ekip işini bitirmiştir; çalışma turu ve kaç ekibe dağılacağı bilinir, her el değiştirme için bir tur bekleme eklenir, sapma ±%10’dur. Gerekçe: aşamalar belirsizliğin doğal olarak azaldığı üç ana durağı temsil eder; her aşamada tahmincinin ne bildiği ayrıca yazılıdır, çünkü koninin daralması bilginin artmasından gelir.

// tahmin.mjs — uc asamali tahmin, ayni kalemlerin gerceklesen akis suresiyle karsilastirilir (model)
import { writeFileSync } from "node:fs";

// ET15 — onceki iki dersin akis odakli duzeni ve on iki kalemi degismeden kullanilir
const BOLUNME = {
  deneyim: "subeUcu personelUcu ceptenErisim",
  odunc: "oduncAkisi oduncKurali rezervasyonAkisi depoErisimi",
  ucret: "ucretAkisi ucretKurali",
  uyelik: "uyelikAkisi uyelikKurali kimlikKoprusu",
  katalog: "katalogKurali katalogKoprusu",
  platform: "ortakBicim olayGunlugu bildirimKuyrugu",
};
const KANAL = "deneyim-odunc deneyim-ucret deneyim-uyelik odunc-ucret odunc-katalog " +
  "odunc-platform ucret-platform uyelik-platform katalog-platform";
const KALEM = [
  ["gecikme ucreti orani", "ucretAkisi ucretKurali subeUcu ortakBicim"],
  ["rezervasyon iptali", "ceptenErisim rezervasyonAkisi oduncKurali bildirimKuyrugu"],
  ["uyelik hatirlatmasi", "uyelikAkisi uyelikKurali bildirimKuyrugu personelUcu"],
  ["katalog kaydinda alan", "katalogKoprusu katalogKurali ortakBicim ceptenErisim"],
  ["odunc suresi uzatma", "oduncAkisi oduncKurali subeUcu"],
  ["kimlikte ikinci adim", "kimlikKoprusu uyelikAkisi personelUcu ortakBicim"],
  ["ucret iadesi kaydi", "ucretAkisi depoErisimi olayGunlugu"],
  ["sube gecikme raporu", "personelUcu depoErisimi oduncAkisi"],
  ["bildirim metni bicimi", "bildirimKuyrugu ortakBicim"],
  ["rezervasyon sirasi", "ceptenErisim rezervasyonAkisi subeUcu"],
  ["odunc kurali istisnasi", "oduncKurali ucretKurali oduncAkisi ucretAkisi"],
  ["olay gunlugune alan", "olayGunlugu ortakBicim depoErisimi"],
];

const sahip = {}, E = Object.keys(BOLUNME), kom = Object.fromEntries(E.map((e) => [e, []]));
for (const [e, ms] of Object.entries(BOLUNME)) for (const m of ms.split(" ")) sahip[m] = e;
for (const k of KANAL.split(" ")) {
  const [x, y] = k.split("-");
  kom[x].push(y); kom[y].push(x);
}
const d = {};
for (const s of E) {
  d[s] = Object.fromEntries(E.map((e) => [e, -1]));
  d[s][s] = 0;
  for (let q = [s]; q.length; ) {
    const u = q.shift();
    for (const v of kom[u]) if (d[s][v] < 0) { d[s][v] = d[s][u] + 1; q.push(v); }
  }
}
const deg = {};
for (const [, ms] of KALEM) for (const m of ms.split(" "))
  (deg[m] ??= new Set([sahip[m]])).add(sahip[ms.split(" ")[0]]);

// ET16 — gerceklesen sure onceki derslerin akis modelinden gelir: calisma + bekleme + yeniden is
const BEKLE = [0, 1, 3, 5], YOK = 8;
const GERCEK = KALEM.map(([ad, dizi]) => {
  const ms = dizi.split(" "), sira = [...new Set(ms.map((m) => sahip[m]))];
  let bek = 0, enUzun = 0, kopuk = 0;
  for (let j = 1; j < sira.length; j++) {
    const u = d[sira[j - 1]][sira[j]], b = u < 0 ? YOK : BEKLE[u];
    bek += b; enUzun = Math.max(enUzun, b);
    if (u < 0 || u >= 2) kopuk += 1;
  }
  const neden = [];
  if (kopuk) neden.push("kisit");
  if (ms.some((m) => deg[m].size >= 3)) neden.push("sinir");
  if (sira.length >= 4) neden.push("bilgi");
  const cal = ms.length + neden.length * 2, bekT = bek + neden.length * enUzun;
  return { ad, modul: ms.length, ekip: sira.length, calisma: cal, bekleme: bekT,
    yenidenIs: neden.length, gercek: cal + bekT };
});

// ET17 — uretec: dogrusal esleme, tohum gorunur; her asamada bir tur cekilir
const TOHUM = 20250401;
function uretec(t) {
  return () => { t = (t * 1103515245 + 12345) % 2147483648; return t / 2147483648; };
}
const r = uretec(TOHUM);
const sapma = (genislik) => 1 + (r() * 2 - 1) * genislik;

// ET18 — uc tahmin asamasi: istek geldiginde, kalem incelendiginde, ilk ekip isini bitirdiginde
const T = GERCEK.map((g) => {
  const a1 = Math.max(1, Math.round(g.modul * sapma(0.8)));    // yalnizca basligi gorur
  const a2 = Math.round(g.modul * sapma(0.3));                 // dokunulan modulleri bilir
  const a3 = Math.round((g.calisma + (g.ekip - 1)) * sapma(0.1)); // ekip sayisini da bilir
  return { ...g, a1, a2, a3 };
});
writeFileSync("tahmin.json", JSON.stringify(T));

console.log(`${"is kalemi".padEnd(24)}${"modul".padStart(6)}${"ekip".padStart(5)}` +
  `${"asama1".padStart(8)}${"asama2".padStart(8)}${"asama3".padStart(8)}${"gercek".padStart(8)}` +
  `${"  = calisma + bekleme"}`);
for (const t of T)
  console.log(`${t.ad.padEnd(24)}${String(t.modul).padStart(6)}${String(t.ekip).padStart(5)}` +
    `${String(t.a1).padStart(8)}${String(t.a2).padStart(8)}${String(t.a3).padStart(8)}` +
    `${String(t.gercek).padStart(8)}    ${String(t.calisma).padStart(2)} + ${String(t.bekleme).padStart(2)}` +
    `${t.yenidenIs ? `  (${t.yenidenIs} yeniden is)` : ""}`);
console.log(`\ntohum ${TOHUM}; toplam gercek ${T.reduce((s, t) => s + t.gercek, 0)} tur ` +
  `(${T.reduce((s, t) => s + t.calisma, 0)} calisma + ${T.reduce((s, t) => s + t.bekleme, 0)} bekleme)`);
is kalemi                modul ekip  asama1  asama2  asama3  gercek  = calisma + bekleme
gecikme ucreti orani         4    3       5       4      11      18     8 + 10  (2 yeniden is)
rezervasyon iptali           4    3       1       3       7       9     6 +  3  (1 yeniden is)
uyelik hatirlatmasi          4    3       6       5      10      18     8 + 10  (2 yeniden is)
katalog kaydinda alan        4    3       4       3       9      18     8 + 10  (2 yeniden is)
odunc suresi uzatma          3    2       1       2       6       7     5 +  2  (1 yeniden is)
kimlikte ikinci adim         4    3       3       3      10      18     8 + 10  (2 yeniden is)
ucret iadesi kaydi           3    3       4       4       6       8     5 +  3  (1 yeniden is)
sube gecikme raporu          3    2       4       2       6       7     5 +  2  (1 yeniden is)
bildirim metni bicimi        2    1       3       3       4       4     4 +  0  (1 yeniden is)
rezervasyon sirasi           3    2       5       3       6       7     5 +  2  (1 yeniden is)
odunc kurali istisnasi       4    2       6       5       5       5     4 +  1
olay gunlugune alan          3    2       5       4       6       7     5 +  2  (1 yeniden is)

tohum 20250401; toplam gercek 126 tur (71 calisma + 55 bekleme)

Hata Payı ve Koninin Daralması

ET19 — bir kalemde plan “tuttu” sayılır: tahmin gerçeğin %20 bandı içinde kalıyorsa. ET20 — kalemler dokundukları ekip sayısına göre iki kümeye ayrılır: bir–iki ekibe dokunanlar ve üç ve daha çok ekibe dokunanlar. Gerekçe: bant olmadan “tuttu” sözcüğü sayıya çevrilemez; ikinci ayrım ise ekip sınırının tahmin üzerindeki payını yalıtır.

// koni.mjs — hata payi, belirsizlik konisinin daralmasi, tek kalem tahmini ile toplam tahmini
import { readFileSync } from "node:fs";
const T = JSON.parse(readFileSync("tahmin.json", "utf8"));

const AS = ["a1", "a2", "a3"];
const AD = { a1: "asama 1 (istek)", a2: "asama 2 (inceleme)", a3: "asama 3 (ilk ekip bitti)" };
const top = (f) => T.reduce((s, t) => s + f(t), 0);
const yuzde = (x) => `%${(100 * x).toFixed(1)}`;
// ET19 — plan bir kalemde "tuttu" sayilir: tahmin gercegin %20 bandi icinde kaliyorsa
const BANT = 0.2;

console.log(`${"asama".padEnd(26)}${"ort. mutlak hata".padStart(18)}${"ort. yanlilik".padStart(15)}` +
  `${"koni: tahmin/gercek".padStart(21)}${"genislik".padStart(10)}${"plan tuttu".padStart(12)}`);
for (const a of AS) {
  const oran = T.map((t) => t[a] / t.gercek).sort((x, y) => x - y);
  const mape = top((t) => Math.abs(t[a] - t.gercek) / t.gercek) / T.length;
  const yan = top((t) => (t[a] - t.gercek) / t.gercek) / T.length;
  const tut = T.filter((t) => Math.abs(t[a] - t.gercek) / t.gercek <= BANT).length;
  console.log(`${AD[a].padEnd(26)}${yuzde(mape).padStart(18)}${yuzde(yan).padStart(15)}` +
    `${`${oran[0].toFixed(2)} - ${oran[oran.length - 1].toFixed(2)}`.padStart(21)}` +
    `${(oran[oran.length - 1] - oran[0]).toFixed(2).padStart(10)}${`${tut}/${T.length}`.padStart(12)}`);
}

console.log(`\n${"asama".padEnd(26)}${"toplam tahmin".padStart(15)}${"toplam gercek".padStart(15)}` +
  `${"toplamin hatasi".padStart(17)}${"tek kalem ort. hatasi".padStart(23)}`);
for (const a of AS) {
  const tt = top((t) => t[a]), tg = top((t) => t.gercek);
  const mape = top((t) => Math.abs(t[a] - t.gercek) / t.gercek) / T.length;
  console.log(`${AD[a].padEnd(26)}${String(tt).padStart(15)}${String(tg).padStart(15)}` +
    `${yuzde((tt - tg) / tg).padStart(17)}${yuzde(mape).padStart(23)}`);
}

// hatanin nereden geldigi: asama 2 yalnizca calismayi tahmin eder
const a2 = top((t) => t.a2), gercek = top((t) => t.gercek);
console.log(`\nasama 2 toplami ${a2} tur; gercek ${gercek} tur; fark ${gercek - a2} tur`);
console.log(`  farkin bilesenleri: bekleme ${top((t) => t.bekleme)} tur, ` +
  `yeniden isin ekledigi calisma ${top((t) => 2 * t.yenidenIs)} tur, ` +
  `dokunulan modul ${top((t) => t.modul)} tur (asama 2 bunu tahmin eder)`);

// ET20 — kalemler dokunduklari ekip sayisina gore ayrilir
console.log(`\n${"kalem kumesi".padEnd(26)}${"kalem".padStart(7)}${"ort. gercek".padStart(13)}` +
  `${"bekleme payi".padStart(14)}${"asama 2 hatasi".padStart(16)}${"asama 3 hatasi".padStart(16)}`);
for (const [ad, f] of [["1-2 ekibe dokunan", (t) => t.ekip <= 2], ["3+ ekibe dokunan", (t) => t.ekip >= 3]]) {
  const k = T.filter(f), s = (g) => k.reduce((x, t) => x + g(t), 0);
  console.log(`${ad.padEnd(26)}${String(k.length).padStart(7)}` +
    `${(s((t) => t.gercek) / k.length).toFixed(1).padStart(13)}` +
    `${yuzde(s((t) => t.bekleme) / s((t) => t.gercek)).padStart(14)}` +
    `${yuzde(s((t) => Math.abs(t.a2 - t.gercek) / t.gercek) / k.length).padStart(16)}` +
    `${yuzde(s((t) => Math.abs(t.a3 - t.gercek) / t.gercek) / k.length).padStart(16)}`);
}
asama                       ort. mutlak hata  ort. yanlilik  koni: tahmin/gercek  genislik  plan tuttu
asama 1 (istek)                        %55.8         %-52.5          0.11 - 1.20      1.09        1/12
asama 2 (inceleme)                     %58.4         %-58.4          0.17 - 1.00      0.83        1/12
asama 3 (ilk ekip bitti)               %23.5         %-23.5          0.50 - 1.00      0.50        6/12

asama                       toplam tahmin  toplam gercek  toplamin hatasi  tek kalem ort. hatasi
asama 1 (istek)                        47            126           %-62.7                  %55.8
asama 2 (inceleme)                     41            126           %-67.5                  %58.4
asama 3 (ilk ekip bitti)               86            126           %-31.7                  %23.5

asama 2 toplami 41 tur; gercek 126 tur; fark 85 tur
  farkin bilesenleri: bekleme 55 tur, yeniden isin ekledigi calisma 30 tur, dokunulan modul 41 tur (asama 2 bunu tahmin eder)

kalem kumesi                kalem  ort. gercek  bekleme payi  asama 2 hatasi  asama 3 hatasi
1-2 ekibe dokunan               6          6.2         %24.3           %44.6            %9.5
3+ ekibe dokunan                6         14.8         %51.7           %72.2           %37.5

Koni Daralırken Hata Neden Durdu

İlk tablonun iki sütunu ters yönlere gider. Koni gerçekten daralır: tahmin/gerçek oranının genişliği 1.09’dan 0.83’e, oradan 0.50’ye iner. Ama ortalama mutlak hata aynı yolu izlemez — aşama 1’de %55.8, aşama 2’de %58.4’tür. Belirsizlik azalırken hata azalmadı.

Nedeni yanlılık sütununda yazılıdır. Üç aşamanın üçünde de yanlılık negatiftir: %-52.5, %-58.4, %-23.5. Yani tahminler yalnız dağınık değil, hepsi aynı yöne yanlıştır — her aşamada süre olduğundan az tahmin edilir. Aşama 1’den aşama 2’ye geçerken tahmincinin öğrendiği şey dokunulan modüllerin tam listesidir; bu bilgi sapmayı ±%80’den ±%30’a indirir ama yanlılığa dokunmaz, çünkü yanlılık modül sayısını yanlış bilmekten gelmiyordu.

Nereden geldiğini ikinci çıktı bloğunun ortasındaki satır adlandırır. Aşama 2’nin toplam tahmini 41 tur, gerçekleşen toplam 126 turdur; aradaki 85 turun 55’i bekleme, 30’u yeniden işin eklediği çalışmadır. Aşama 2 dokunulan modül sayısını (41 tur) neredeyse tam bilir — ve tahmin ettiği tek şey odur. Tahmin çalışmayı tahmin eder; gerçekleşen süre akış süresidir. İkisinin arasındaki fark önceki iki dersin ölçtüğü şeyin ta kendisidir: el değiştirmede beklenen turlar ve yanlış sınır yüzünden geri dönen kalemler.

Aşama 3 hatayı %23.5’e indirir ve planın tuttuğu kalem sayısını 1’den 6’ya çıkarır. Yaptığı tek şey bekleme kavramını tahmine sokmaktır — her el değiştirme için bir tur. Bu kaba düzeltme bile hatanın yarısından fazlasını kapatır; kalan %23.5 ise beklemenin bir turdan uzun olduğu geçişlerden ve yeniden işten gelir.

Tek Kalem Tahmini ile Toplam Tahmini

İkinci tablo yaygın bir beklentiyi sınar: tek tek kalemlerde yapılan hataların toplamda birbirini götürmesi. Götürmez. Aşama 2’de tek kalem ortalama mutlak hatası %58.4, toplamın hatası %-67.5’tir — toplam, tek kalemden daha kötüdür. Aynı ilişki üç aşamada da sürer.

Hataların birbirini götürmesi ancak hataların iki yöne dağıldığı durumda olur. Burada on iki kalemin hiçbirinde aşama 2 tahmini gerçeği aşmaz; koni sütunundaki en yüksek oran 1.00’dır. Bir yöne yanlı hatalar toplandığında büyüklükleri de toplanır. Bir tahmin kümesinin toplamı, ancak kümedeki hatalar yansızsa tek kalem hatasından daha güvenilirdir; yanlılık varsa toplam tahmin tek kalem tahmininden daha az güvenilirdir.

Son tablo yanlılığın nereden geldiğini gösterir. Bir–iki ekibe dokunan altı kalemin ortalama gerçekleşen süresi 6.2 tur ve bunun %24.3’ü beklemedir; aşama 2 hatası %44.6, aşama 3 hatası %9.5’tir. Üç ve daha çok ekibe dokunan altı kalemde ise ortalama süre 14.8 tur, bekleme payı %51.7, aşama 2 hatası %72.2 ve aşama 3 hatası %37.5’tir. Aynı tahmin yöntemi, ekip sınırını çok geçen kalemlerde yaklaşık iki kat daha kötü çalışır. Tahminin kötüleştiği yer, önceki iki dersin akış süresinin uzadığı yerle aynıdır — modül sınırı ile ekip sınırının örtüşmediği kalemler.

Özet

  • On iki kalemin gerçekleşen süresi önceki derslerin akış modelinden alındı (toplam 126 tur = 71 çalışma + 55 bekleme) ve üç aşamada üretilen tahminlerle karşılaştırıldı; üreteç tohumu 20250401.
  • Belirsizlik konisi aşama aşama daralır (tahmin/gerçek oranının genişliği 1.09 → 0.83 → 0.50) ama ortalama mutlak hata aynı yolu izlemez: %55.8 → %58.4 → %23.5. Aşama 1’den 2’ye geçişte koni daraldığı halde hata azalmadı.
  • Üç aşamada da yanlılık negatiftir (%-52.5, %-58.4, %-23.5): tahminler dağınık değil, hepsi aynı yöne yanlıştır. Aşama 2’nin 41 turluk toplam tahmini ile 126 turluk gerçek arasındaki 85 turun 55’i bekleme, 30’u yeniden işin eklediği çalışmadır.
  • Beklemeyi kaba biçimde (el değiştirme başına bir tur) tahmine sokmak hatayı %23.5’e indirir ve %20 bandında tutan kalem sayısını 1/12’den 6/12’ye çıkarır.
  • Hatalar birbirini götürmez: aşama 2’de tek kalem ortalama hatası %58.4 iken toplamın hatası %-67.5’tir. Tek yöne yanlı hatalar toplandığında büyüklükleri de toplanır.
  • Ekip sınırını çok geçen kalemlerde tahmin yaklaşık iki kat kötüleşir: 1–2 ekibe dokunan kalemlerde aşama 2 hatası %44.6 ve bekleme payı %24.3 iken, 3+ ekibe dokunanlarda %72.2 ve %51.7’dir.

Sonraki Adım

Bu dersin bütün sayıları bir varsayıma dayanıyordu: iş kalemi kümesi çeyrek boyunca sabittir. On iki kalem baştan sayıldı, tahmin edildi, koşturuldu. Gerçekte kalem kümesi durmaz — çeyrek ortasında yeni bir istek gelir, var olan bir kalemin kapsamı büyür, bir başkası iptal edilir. Bu durumda tahminin hata payı yalnız yukarıdaki nedenlerle değil, tahmin edilen işin kendisi değiştiği için de büyür. Sonraki ders bu değişimi ele alır: bir kalemin kapsamı büyüdüğünde akışta ne olur, değişiklik hangi noktadan sonra kabul edilirse yeniden iş kaçınılmaz olur ve değişimi denetleyen bir düzenin kendisi akışa kaç tur ekler.

İ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