17  Simulation for Decision Making

Tujuan pembelajaran. Setelah menyelesaikan bab ini, kita akan mampu: 1. Menjelaskan mengapa optimasi deterministik (Bab 14–16) tidak cukup di dunia nyata. 2. Menerapkan simulation-optimization untuk mengevaluasi keputusan di bawah ketidakpastian. 3. Membedakan expected value optimization, robust optimization, dan stochastic programming. 4. Membangun scenario-based optimization menggunakan skenario dari simulasi Monte Carlo. 5. Mengevaluasi keputusan berdasarkan distribusi hasil—bukan hanya ekspektasi. 6. Menerapkan simulation-optimization pada dua kasus: alokasi retensi dan inventori retail. 7. Memahami trade-off antara expected performance dan robustness.

17.1 Mengapa Optimasi Determinstik Tidak Cukup

Di Bab 14–16, kita membangun model optimasi yang mengasumsikan parameter diketahui dengan pasti:

  • Probabilitas churn pelanggan = 0,72.
  • Permintaan toko = 600 unit.
  • Efektivitas intervensi = 30%.

Tetapi di dunia nyata, parameter ini tidak pasti. Probabilitas churn adalah estimasi dari model prediktif (Bab 9)—dengan standard error dan ketidakpastian model. Permintaan adalah forecast (Bab 10)—dengan rentang kepercayaan. Efektivitas intervensi adalah asumsi—yang mungkin berbeda di lapangan.

Optimasi deterministik mengabaikan ketidakpastian ini. Solusi yang dihasilkan optimal untuk satu skenario—tetapi bisa sangat buruk jika skenario aktual berbeda.

Important

Contoh konkret. Misalkan LP merekomendasikan mengintervensi 200 pelanggan berdasarkan asumsi probabilitas churn 0,72. Jika probabilitas sebenarnya hanya 0,50: - Jika optimasi deterministik: kita tetap mengintervensi 200 pelanggan— tetapi sebagian besar tidak akan churn, sehingga banyak biaya terbuang. - Jika keputusan robust: kita mengintervensi lebih sedikit, atau mengalokasikan anggaran ke diversifikasi—sehingga keputusan tetap baik di berbagai skenario.

Simulation-optimization menjembatani gap ini: ia menggunakan simulasi untuk menghasilkan berbagai skenario, dan optimasi untuk menemukan keputusan yang baik di semua skenario—bukan hanya optimal di satu skenario.

17.2 Tiga Pendekatan untuk Keputusan di Bawah Ketidakpastian

Ada tiga pendekatan utama, dengan tingkat kompleksitas dan asumsi yang berbeda:

Pendekatan Prinsip Kapan dipakai
Expected Value Optimization Optimalkan rata-rata dari distribusi hasil Ketika kita netral risiko dan keputusan berulang
Robust Optimization Optimalkan skenario terburuk (worst-case) Ketika kita menghindari risiko ekstrem
Stochastic Programming Optimalkan ekspektasi dengan recourse (tindakan korektif) Ketika ada keputusan bertahap

17.2.1 1. Expected Value Optimization

Prinsip: ganti setiap parameter tidak pasti dengan nilai ekspektasinya, lalu selesaikan model optimasi deterministik.

Kelebihan: sederhana—cukup gunakan rata-rata dari simulasi. Kekurangan: mengabaikan distribusi—solusi bisa sangat buruk di skenario ekstrem.

17.2.2 2. Robust Optimization

Prinsip: temukan keputusan yang meminimalkan kerugian maksimum di seluruh skenario—atau memaksimalkan hasil minimum.

Kelebihan: tahan terhadap skenario terburuk. Kekurangan: bisa terlalu konservatif—mengorbankan banyak expected value untuk menghindari risiko yang jarang terjadi.

17.2.3 3. Stochastic Programming

Prinsip: model keputusan dua tahap: - Tahap 1: keputusan sekarang, sebelum ketidakpastian terungkap. - Tahap 2: keputusan korektif, setelah ketidakpastian terungkap.

Kelebihan: paling realistis—mencerminkan keputusan nyata yang bertahap. Kekurangan: paling kompleks—membutuhkan formulasi matematis yang lebih canggih.

Tip

Pendekatan mana yang tepat? Tergantung pada konteks keputusan:

Konteks Pendekatan yang disarankan
Keputusan berulang (mis. retensi harian) Expected value—rata-rata jangka panjang mendekati ekspektasi
Keputusan sekali seumur hidup (mis. merger) Robust—hindari hasil terburuk
Keputusan bertahap (mis. inventori bulanan) Stochastic programming—manfaatkan informasi baru

Untuk buku ini, kita akan fokus pada expected value dan robust optimization—karena keduanya paling mudah dipahami dan diimplementasikan dengan alat yang sudah kita miliki. Stochastic programming akan disinggung sebagai konsep, tetapi tidak didemonstrasikan secara mendalam.

17.3 Simulation-Optimization: Kerangka Kerja

Simulation-optimization adalah proses berulang yang menggabungkan simulasi dan optimasi:

Code
flowchart LR
    A[Model<br/>probabilistik] --> B[Bangkitkan<br/>skenario]
    B --> C[Optimasi<br/>per skenario]
    C --> D[Evaluasi<br/>out-of-sample]
    D --> E[Pilih keputusan<br/>terbaik]
    E -.->|Iterasi| B

flowchart LR
    A[Model<br/>probabilistik] --> B[Bangkitkan<br/>skenario]
    B --> C[Optimasi<br/>per skenario]
    C --> D[Evaluasi<br/>out-of-sample]
    D --> E[Pilih keputusan<br/>terbaik]
    E -.->|Iterasi| B

Langkah-langkah:

  1. Definisikan model probabilistik untuk parameter tidak pasti (dari Bab 12).
  2. Bangkitkan N skenario—setiap skenario adalah satu set parameter yang mungkin.
  3. Optimasi untuk setiap skenario—atau optimasi untuk aggregate (expected value).
  4. Evaluasi keputusan pada skenario baru (out-of-sample) untuk mengukur robustness.
  5. Pilih keputusan berdasarkan kriteria—expected value, worst-case, atau kombinasi.
Note

Mengapa perlu evaluasi out-of-sample? Karena keputusan yang optimal untuk skenario yang digunakan dalam optimasi belum tentu optimal untuk skenario baru—sama seperti overfitting di Bab 7. Kita perlu menguji keputusan pada skenario yang tidak dipakai dalam pelatihan.

17.4 Aplikasi 1: Alokasi Retensi di Bawah Ketidakpastian

Kita kembali ke masalah alokasi retensi dari Bab 14—tetapi kali ini dengan ketidakpastian dalam parameter:

  • Probabilitas churn per segmen tidak pasti—estimasi dari model dengan standard error.
  • Efektivitas intervensi tidak pasti—bisa lebih tinggi atau lebih rendah dari asumsi.
  • LTV pelanggan tidak pasti—bergantung pada perilaku masa depan.

17.4.1 Menyiapkan Model Probabilistik

Code
import sys
sys.path.insert(0, ".")
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")

from pulp import (
    LpProblem, LpVariable, LpMaximize, LpMinimize, LpStatus,
    lpSum, value, PULP_CBC_CMD, LpBinary, LpInteger, LpContinuous,
)

np.random.seed(42)
Code
# Parameter dasar (estimasi titik)
segments = pd.DataFrame({
    "segmen": ["Premium Baru", "Loyal Hemat", "Mainstream"],
    "n_pelanggan": [800, 2400, 3800],
    "p_churn": [0.55, 0.12, 0.28],
    "ltv": [1_200_000, 600_000, 900_000],
    "biaya_intervensi": [200_000, 80_000, 120_000],
    "efektivitas": [0.40, 0.25, 0.30],
})

# Ketidakpastian: standard error relatif
# (dalam praktik, ini berasal dari model Bab 9 atau data historis)
p_churn_se = [0.05, 0.02, 0.03]       # standard error probabilitas churn
efektivitas_se = [0.10, 0.05, 0.08]   # standard error efektivitas
ltv_se_pct = [0.20, 0.15, 0.18]       # standard error relatif LTV

BUDGET = 50_000_000
CAPACITY = 300

17.4.2 Membangkitkan Skenario

Code
N_SCENARIOS = 500

def generate_scenario():
    """Bangkitkan satu skenario parameter dari distribusi."""
    p = np.clip(
        np.random.normal(segments["p_churn"], p_churn_se), 0.01, 0.99
    )
    eff = np.clip(
        np.random.normal(segments["efektivitas"], efektivitas_se), 0.01, 0.90
    )
    ltv = segments["ltv"] * (1 + np.random.normal(0, ltv_se_pct))
    return p, eff, ltv

scenarios = [generate_scenario() for _ in range(N_SCENARIOS)]

# Ringkasan skenario
p_all = np.array([s[0] for s in scenarios])
eff_all = np.array([s[1] for s in scenarios])
ltv_all = np.array([s[2] for s in scenarios])

print(f"Skenario yang dibangkitkan: {N_SCENARIOS}")
print(f"\nProbabilitas churn — rata-rata ± std per segmen:")
for i, seg in enumerate(segments["segmen"]):
    print(f"  {seg:15s}: {p_all[:,i].mean():.3f} ± {p_all[:,i].std():.3f}")
print(f"\nEfektivitas — rata-rata ± std per segmen:")
for i, seg in enumerate(segments["segmen"]):
    print(f"  {seg:15s}: {eff_all[:,i].mean():.3f} ± {eff_all[:,i].std():.3f}")
Skenario yang dibangkitkan: 500

Probabilitas churn — rata-rata ± std per segmen:
  Premium Baru   : 0.550 ± 0.049
  Loyal Hemat    : 0.119 ± 0.020
  Mainstream     : 0.281 ± 0.030

Efektivitas — rata-rata ± std per segmen:
  Premium Baru   : 0.400 ± 0.098
  Loyal Hemat    : 0.248 ± 0.051
  Mainstream     : 0.302 ± 0.080

17.4.3 Strategi 1: Expected Value Optimization

Ini adalah pendekatan paling sederhana: ganti parameter dengan rata-rata dari simulasi, lalu selesaikan LP.

Code
def solve_allocation(p_churn, efektivitas, ltv, budget=BUDGET, capacity=CAPACITY):
    """Selesaikan alokasi retensi untuk parameter tertentu."""
    model = LpProblem("alokasi", LpMaximize)
    x = LpVariable.dicts("x", segments["segmen"].tolist(), lowBound=0, cat=LpContinuous)

    # Net benefit per pelanggan (parameter skenario)
    net_benefit = [
        p_churn[i] * efektivitas[i] * ltv[i] - segments.loc[i, "biaya_intervensi"]
        for i in range(len(segments))
    ]

    model += lpSum(net_benefit[i] * x[seg] for i, seg in enumerate(segments["segmen"])), "Obj"
    model += lpSum(segments.loc[i, "biaya_intervensi"] * x[seg]
                   for i, seg in enumerate(segments["segmen"])) <= budget, "Budget"
    model += lpSum(x[seg] for seg in segments["segmen"]) <= capacity, "Capacity"
    for i, seg in enumerate(segments["segmen"]):
        model += x[seg] <= segments.loc[i, "n_pelanggan"], f"Bound_{i}"

    model.solve(PULP_CBC_CMD(msg=False))
    return {seg: value(x[seg]) for seg in segments["segmen"]}

# Expected value optimization
p_mean = p_all.mean(axis=0)
eff_mean = eff_all.mean(axis=0)
ltv_mean = ltv_all.mean(axis=0)

allocation_ev = solve_allocation(p_mean, eff_mean, ltv_mean)
print("Strategi Expected Value — alokasi:")
for seg, n in allocation_ev.items():
    print(f"  {seg:15s}: {n:6.1f} pelanggan")
Strategi Expected Value — alokasi:
  Premium Baru   :  250.0 pelanggan
  Loyal Hemat    :    0.0 pelanggan
  Mainstream     :    0.0 pelanggan

17.4.4 Strategi 2: Robust Optimization

Robust optimization mencari keputusan yang meminimalkan kerugian maksimum di seluruh skenario. Implementasinya dengan skenario terburuk (worst-case) atau probabilitas rendah (mis. persentil 5%).

Code
# Robust: optimasi untuk skenario "buruk" — p_churn rendah, efektivitas rendah, LTV rendah
# (konservatif: kita asumsikan skenario yang merugikan)

# Ambil persentil 10% untuk setiap parameter (skenario buruk)
p_bad = np.percentile(p_all, 10, axis=0)
eff_bad = np.percentile(eff_all, 10, axis=0)
ltv_bad = np.percentile(ltv_all, 10, axis=0)

allocation_robust = solve_allocation(p_bad, eff_bad, ltv_bad)
print("Strategi Robust (skenario buruk) — alokasi:")
for seg, n in allocation_robust.items():
    print(f"  {seg:15s}: {n:6.1f} pelanggan")
Strategi Robust (skenario buruk) — alokasi:
  Premium Baru   :    0.0 pelanggan
  Loyal Hemat    :    0.0 pelanggan
  Mainstream     :    0.0 pelanggan
Warning

Robust optimization dengan skenario terburuk bisa terlalu konservatif. Jika kita mengoptimalkan untuk skenario persentil 10%, kita mengabaikan 90% kemungkinan—dan mungkin mengorbankan banyak expected value.

Alternatif yang lebih seimbang: minimax regret—meminimalkan penyesalan maksimum (selisih antara hasil aktual dan hasil terbaik yang mungkin di skenario itu). Kita akan membahas ini di latihan.

17.4.5 Evaluasi Out-of-Sample: Membandingkan Strategi

Sekarang kita uji kedua strategi pada skenario baru yang tidak dipakai dalam optimasi:

Code
# Bangkitkan skenario uji baru
np.random.seed(123)
N_TEST = 200
test_scenarios = [generate_scenario() for _ in range(N_TEST)]

def evaluate_allocation(allocation, scenarios):
    """Evaluasi alokasi pada sekumpulan skenario."""
    results = []
    for p, eff, ltv in scenarios:
        total_benefit = 0
        total_cost = 0
        for i, seg in enumerate(segments["segmen"]):
            n = allocation[seg]
            net_benefit_i = p[i] * eff[i] * ltv[i] - segments.loc[i, "biaya_intervensi"]
            total_benefit += n * net_benefit_i
            total_cost += n * segments.loc[i, "biaya_intervensi"]
        results.append(total_benefit)
    return np.array(results)

results_ev = evaluate_allocation(allocation_ev, test_scenarios)
results_robust = evaluate_allocation(allocation_robust, test_scenarios)

# Bandingkan distribusi hasil
comparison = pd.DataFrame({
    "Strategi": ["Expected Value", "Robust (P10)"],
    "Mean": [results_ev.mean(), results_robust.mean()],
    "Std": [results_ev.std(), results_robust.std()],
    "P5 (worst)": [np.percentile(results_ev, 5), np.percentile(results_robust, 5)],
    "P95 (best)": [np.percentile(results_ev, 95), np.percentile(results_robust, 95)],
    "Prob(rugi)": [(results_ev < 0).mean(), (results_robust < 0).mean()],
}).round(2)
comparison
Strategi Mean Std P5 (worst) P95 (best) Prob(rugi)
0 Expected Value 15994079.77 20814419.37 -16821893.59 51150696.51 0.25
1 Robust (P10) 0.00 0.00 0.00 0.00 0.00
Code
fig, ax = plt.subplots(figsize=(9, 4.5))
ax.hist(results_ev / 1e6, bins=30, alpha=0.6, label="Expected Value",
        color="#4C72B0", edgecolor="white")
ax.hist(results_robust / 1e6, bins=30, alpha=0.6, label="Robust (P10)",
        color="#DD8452", edgecolor="white")
ax.axvline(results_ev.mean() / 1e6, color="#4C72B0", linestyle="--", linewidth=2)
ax.axvline(results_robust.mean() / 1e6, color="#DD8452", linestyle="--", linewidth=2)
ax.set_xlabel("Total Net Benefit (Rp juta)")
ax.set_ylabel("Frekuensi")
ax.set_title("Distribusi Hasil pada Skenario Uji — EV vs Robust")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Figure 17.1: Distribusi hasil untuk dua strategi pada skenario uji. Expected value optimization memiliki mean lebih tinggi tetapi variansi lebih besar; robust memiliki distribusi lebih sempit.
Important

Trade-off klasik: expected value vs robustness.

Aspek Expected Value Robust
Mean Lebih tinggi Lebih rendah (mengorbankan EV)
Std Lebih besar Lebih kecil
Worst-case Bisa sangat buruk Lebih baik
Prob(rugi) Lebih tinggi Lebih rendah

Tidak ada strategi yang “benar” secara universal. Pilihan tergantung pada preferensi risiko manajemen: - Jika perusahaan netral risiko (dapat menanggung fluktuasi), pilih expected value. - Jika perusahaan menghindari risiko (tidak bisa menanggung kerugian besar), pilih robust.

Dalam praktik, banyak organisasi menggunakan kombinasi: optimalkan expected value dengan kendala pada worst-case (mis. “expected benefit harus ≥ X, dan worst-case ≥ Y”).

17.5 Aplikasi 2: Inventori Retail di Bawah Ketidakpastian

Kita kembali ke masalah inventori dari Bab 12—tetapi kali ini dengan optimasi. Kita ingin mencari level stok optimal yang mempertimbangkan ketidakpastian permintaan.

17.5.1 Model

  • Keputusan: level stok untuk setiap produk.
  • Ketidakpastian: permintaan harian.
  • Tujuan: maksimalkan expected profit (revenue − stockout cost − holding cost).

17.5.2 Pendekatan: Optimasi untuk Setiap Skenario

Code
from decanal.utils import load_retail

df_retail = load_retail()
df_retail["Date"] = pd.to_datetime(df_retail["Date"])

# Fokus pada produk dengan volume tertinggi
produk_id = df_retail.groupby("Product ID")["Units Sold"].mean().idxmax()
subset = df_retail[df_retail["Product ID"] == produk_id]

# Parameter biaya (asumsi)
harga_jual = subset["Price"].mean()
biaya_stockout = harga_jual * 0.5  # margin yang hilang
biaya_holding = harga_jual * 0.02  # biaya penyimpanan per hari

demand_data = subset["Units Sold"].values

print(f"Produk: {produk_id}")
print(f"Rata-rata permintaan harian: {demand_data.mean():.1f} unit")
print(f"Std permintaan             : {demand_data.std():.1f} unit")
print(f"Harga jual                 : Rp {harga_jual:,.0f}")
print(f"Biaya stockout per unit    : Rp {biaya_stockout:,.0f}")
print(f"Biaya holding per unit/hari: Rp {biaya_holding:,.0f}")
Produk: P0016
Rata-rata permintaan harian: 139.1 unit
Std permintaan             : 109.4 unit
Harga jual                 : Rp 55
Biaya stockout per unit    : Rp 27
Biaya holding per unit/hari: Rp 1
Code
def simulate_inventory(stock_level, demand_samples):
    """Simulasi total profit untuk level stok tertentu."""
    stockouts = np.maximum(0, demand_samples - stock_level)
    holdings = np.maximum(0, stock_level - demand_samples)
    sales = np.minimum(demand_samples, stock_level)

    revenue = sales * harga_jual
    stockout_cost = stockouts * biaya_stockout
    holding_cost = holdings * biaya_holding

    return revenue - stockout_cost - holding_cost

# Bangkitkan skenario permintaan (bootstrap dari data historis)
np.random.seed(42)
N_SCEN = 1000
N_DAYS = 30
demand_scenarios = np.random.choice(demand_data, size=(N_SCEN, N_DAYS), replace=True)

# Uji berbagai level stok
stock_levels = np.arange(10, 60, 2)
results = []

for level in stock_levels:
    profits = [simulate_inventory(level, ds).sum() for ds in demand_scenarios]
    profits = np.array(profits)
    results.append({
        "stock_level": level,
        "mean_profit": profits.mean(),
        "std_profit": profits.std(),
        "p5_profit": np.percentile(profits, 5),
        "p95_profit": np.percentile(profits, 95),
    })

results_df = pd.DataFrame(results)
results_df["mean_profit_juta"] = results_df["mean_profit"] / 1e6
results_df["p5_profit_juta"] = results_df["p5_profit"] / 1e6

# Level optimal berdasarkan mean
optimal_mean = results_df.loc[results_df["mean_profit"].idxmax()]
# Level optimal berdasarkan P5 (robust)
optimal_p5 = results_df.loc[results_df["p5_profit"].idxmax()]

print(f"Level stok optimal (mean)  : {optimal_mean['stock_level']:.0f} unit")
print(f"  → Mean profit: Rp {optimal_mean['mean_profit_juta']:.2f} juta")
print(f"  → P5 profit  : Rp {optimal_mean['p5_profit_juta']:.2f} juta")
print()
print(f"Level stok optimal (P5)    : {optimal_p5['stock_level']:.0f} unit")
print(f"  → Mean profit: Rp {optimal_p5['mean_profit_juta']:.2f} juta")
print(f"  → P5 profit  : Rp {optimal_p5['p5_profit_juta']:.2f} juta")
Level stok optimal (mean)  : 58 unit
  → Mean profit: Rp 0.01 juta
  → P5 profit  : Rp -0.01 juta

Level stok optimal (P5)    : 58 unit
  → Mean profit: Rp 0.01 juta
  → P5 profit  : Rp -0.01 juta
Code
fig, ax = plt.subplots(figsize=(9, 4.5))
ax.plot(results_df["stock_level"], results_df["mean_profit_juta"],
        marker="o", linewidth=2, color="#4C72B0", label="Mean profit")
ax.plot(results_df["stock_level"], results_df["p5_profit_juta"],
        marker="s", linewidth=2, color="#DD8452", label="P5 profit (worst)")

ax.axvline(optimal_mean["stock_level"], color="#4C72B0", linestyle="--",
           alpha=0.5, label=f"Optimal (mean): {optimal_mean['stock_level']:.0f}")
ax.axvline(optimal_p5["stock_level"], color="#DD8452", linestyle=":",
           alpha=0.5, label=f"Optimal (P5): {optimal_p5['stock_level']:.0f}")

ax.set_xlabel("Level stok (unit)")
ax.set_ylabel("Profit bulanan (Rp juta)")
ax.set_title("Optimasi Inventori di Bawah Ketidakpastian")
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Figure 17.2: Mean profit dan P5 profit sebagai fungsi level stok. Level optimal berdasarkan mean berbeda dari level optimal berdasarkan P5—mencerminkan trade-off risiko.

Temuan: Level stok optimal berdasarkan mean berbeda dari level stok optimal berdasarkan P5 (worst-case). Level stok yang lebih tinggi: - Meningkatkan P5 profit (mengurangi risiko stockout). - Menurunkan mean profit (karena holding cost lebih tinggi).

Trade-off ini adalah inti dari keputusan di bawah ketidakpastian: berapa banyak expected value yang bersedia kita korbankan untuk mengurangi risiko?


17.5.3 Value-at-Risk untuk Keputusan Inventori

Selain mean dan P5, kita bisa mengevaluasi keputusan dengan Value-at-Risk (VaR)—seberapa besar kerugian yang mungkin terjadi pada tingkat kepercayaan tertentu.

Code
def compute_var(profits, confidence=0.95):
    """VaR 95%: kerugian yang tidak akan terlampaui dengan probabilitas 95%."""
    return np.percentile(profits, (1 - confidence) * 100)

# Bandingkan VaR untuk tiga level stok
levels_to_compare = [20, 30, 40]

comparison_var = []
for level in levels_to_compare:
    profits = np.array([simulate_inventory(level, ds).sum() for ds in demand_scenarios])
    comparison_var.append({
        "stock_level": level,
        "mean_profit (Rp juta)": profits.mean() / 1e6,
        "std (Rp juta)": profits.std() / 1e6,
        "VaR 95% (Rp juta)": compute_var(profits) / 1e6,
        "prob_rugi (%)": (profits < 0).mean() * 100,
    })

pd.DataFrame(comparison_var).round(2)
stock_level mean_profit (Rp juta) std (Rp juta) VaR 95% (Rp juta) prob_rugi (%)
0 20 -0.07 0.02 -0.09 100.0
1 30 -0.05 0.02 -0.07 99.9
2 40 -0.03 0.01 -0.05 96.0
Tip

Cara membaca VaR untuk inventori:

  • VaR 95% = −Rp 2 juta berarti: “Ada 5% kemungkinan profit bulanan kurang dari −Rp 2 juta (rugi lebih dari Rp 2 juta).”
  • Prob(rugi) = 8% berarti: “Dalam 8% skenario, kita mengalami kerugian.”

Kedua metrik ini memberi gambaran risiko yang lebih lengkap daripada mean saja. Keputusan akhir harus mempertimbangkan keduanya—bukan hanya expected profit.

17.6 Scenario-Based Optimization: Pendekatan Lebih Kaya

Expected value optimization menggunakan satu set parameter rata-rata—yang menyembunyikan distribusi. Scenario-based optimization menggunakan banyak skenario secara langsung, dan mencari keputusan yang memaksimalkan rata-rata hasil di seluruh skenario.

17.6.1 Formulasi

Alih-alih satu set parameter, kita memiliki N skenario—masing-masing dengan parameter berbeda. Kita ingin memilih alokasi \(x\) yang memaksimalkan:

\[\max \, \frac{1}{N} \sum_{s=1}^{N} \text{benefit}(x, \text{skenario}_s)\]

Ini adalah expected value atas distribusi skenario—bukan hanya rata-rata parameter. Untuk fungsi linear, kedua pendekatan ini ekuivalen—tetapi untuk fungsi non-linear, mereka bisa berbeda.

Note

Untuk model alokasi retensi kita, kedua pendekatan ekuivalen karena fungsi tujuan linear terhadap parameter. Tetapi untuk model yang lebih kompleks—misalnya dengan kendala probabilistik (mis. “probabilitas stockout ≤ 5%”)—scenario-based optimization menjadi jauh lebih kuat.

Contoh kendala probabilistik: “Kita ingin probabilitas kehabisan stok ≤ 5%.” Ini tidak dapat diekspresikan dalam model deterministik—tetapi dapat diekspresikan dalam scenario-based optimization: “Dalam setidaknya 95% skenario, stok harus cukup.”

17.7 Latihan Terbimbing

Latihan 17.1 — Expected value vs robust. Untuk model alokasi retensi, bandingkan tiga strategi: (a) expected value, (b) robust P10, (c) robust P25. Evaluasi pada 200 skenario uji. Strategi mana yang memiliki mean tertinggi? Mana yang memiliki P5 tertinggi? Mana yang kita rekomendasikan untuk perusahaan yang baru saja mengalami kerugian besar?

Latihan 17.2 — Minimax regret. Implementasikan minimax regret untuk model alokasi retensi: untuk setiap skenario, hitung regret (selisih antara hasil strategi dan hasil terbaik yang mungkin di skenario itu), lalu pilih strategi yang meminimalkan regret maksimum. Bandingkan hasilnya dengan expected value dan robust.

Latihan 17.3 — Inventori multi-produk. Kembangkan model inventori untuk dua produk dengan permintaan yang berkorelasi (korelasi 0,3). Bangkitkan skenario permintaan bersama, dan optimalkan level stok untuk kedua produk secara bersamaan dengan kendala total kapasitas gudang (mis. 80 unit). Bagaimana korelasi mempengaruhi level stok optimal?

Latihan 17.4 — Value of information. Dalam model alokasi retensi, hitung Expected Value of Perfect Information (EVPI) dengan simulasi: berapa selisih antara expected benefit jika kita tahu parameter sebenarnya (perfect information) versus tidak tahu (expected value optimization)? Bandingkan dengan EVPI dari Bab 13.

Latihan 17.5 — Kendala probabilistik. Modifikasi model inventori dengan menambahkan kendala probabilistik: “Probabilitas stockout ≤ 5%.” Implementasikan dengan scenario-based optimization: cari level stok minimum yang memenuhi kendala ini di 95% skenario. Bandingkan dengan level stok optimal berdasarkan mean.

Petunjuk Latihan 17.2. Minimax regret membutuhkan matriks regret—baris = strategi, kolom = skenario. Untuk setiap skenario, regret = hasil terbaik di skenario itu − hasil strategi. Kemudian untuk setiap strategi, ambil regret maksimum di seluruh skenario, dan pilih strategi dengan regret maksimum terkecil.

17.8 Kotak “Jembatan”: Dari Simulasi ke Integrasi

Important

Jembatan. Bab ini melengkapi alat keputusan Bagian III:

Bab Alat Kapan dipakai
13 Decision framework Keputusan satu pelanggan
14 Linear programming Alokasi sumber daya, hubungan linear
15 Integer/binary/nonlinear Keputusan diskrit, logika
16 Network & multi-objective Aliran jaringan, trade-off tujuan
17 Simulation-optimization Ketidakpastian parameter

Kita sekarang dapat mengoptimalkan di dunia yang tidak pasti—bukan hanya di dunia yang pasti. Kita dapat: - Mengevaluasi keputusan berdasarkan distribusi hasil, bukan hanya ekspektasi. - Memilih strategi yang tahan terhadap berbagai skenario. - Mengukur risiko dengan VaR, P5, dan probabilitas kerugian.

Tetapi semua ini masih merupakan bagian-bagian terpisah. Bab 18 akan mengintegrasikan semuanya dalam satu proyek end-to-end:

  • Dari data mentah hingga rekomendasi kebijakan.
  • Menggabungkan prediksi (Bab 7–12) dengan optimasi (Bab 14–17).
  • Menghasilkan dashboard dan laporan eksekutif untuk keputusan nyata.

Bab 18 adalah puncak buku ini—di mana semua alat yang telah kita bangun bekerja bersama untuk menyelesaikan masalah bisnis yang sesungguhnya.

17.9 Rangkuman

Dalam bab ini kita telah:

  • Memahami mengapa optimasi deterministik tidak cukup di dunia nyata—parameter tidak pasti, dan solusi optimal untuk satu skenario bisa buruk di skenario lain.
  • Membedakan tiga pendekatan untuk keputusan di bawah ketidakpastian: expected value, robust, dan stochastic programming.
  • Membangun simulation-optimization dengan membangkitkan skenario dari distribusi parameter, optimasi per skenario, dan evaluasi out-of-sample.
  • Menerapkan expected value optimization pada alokasi retensi—mengganti parameter dengan rata-rata dari simulasi.
  • Menerapkan robust optimization dengan skenario buruk (P10)—menghasilkan keputusan yang lebih tahan terhadap risiko.
  • Membandingkan strategi pada skenario uji baru—menemukan trade-off klasik antara mean dan worst-case.
  • Menerapkan simulation-optimization pada inventori retail—menemukan level stok optimal yang mempertimbangkan ketidakpastian permintaan.
  • Mengukur risiko dengan VaR, P5, dan probabilitas kerugian—melengkapi gambaran expected value dengan dimensi risiko.
  • Memahami keterbatasan expected value optimization: ia mengabaikan distribusi, dan bisa menghasilkan keputusan yang rapuh.

17.10 Jembatan ke Bab 18

Kita telah menyelesaikan seluruh alat untuk decision analytics:

Bagian I mengajarkan kita melihat dan memahami data. Bagian II mengajarkan kita memprediksi. Bagian III mengajarkan kita mengoptimalkan dan memutuskan.

Sekarang saatnya menggabungkan semuanya dalam satu proyek end-to-end. Bab 18—Integrasi & Proyek Akhir—akan:

  • Mengambil satu masalah bisnis dari awal hingga akhir.
  • Menggunakan model prediktif dari Bagian II untuk menghasilkan probabilitas.
  • Menggunakan optimasi dari Bagian III untuk memilih tindakan.
  • Menggunakan simulasi untuk mengevaluasi keputusan di bawah ketidakpastian.
  • Menghasilkan rekomendasi kebijakan yang dapat dieksekusi.
  • Menyajikan hasil dalam dashboard eksekutif dan laporan.

Ini adalah puncak buku—dan kesempatan untuk melihat bagaimana semua bagian bekerja bersama.