Dolma Partisi

[English] [kod]
[Substack]

Geçenlerde dolma temalı yıllık bir akşam yemeği partisinin yirmi üçüncüsüne katıldım. Tahmin edebileceğiniz gibi bu etkinlik bazıları için bir gelenek haline gelmiş – ilki düzenlendiğinde ben sadece iki yaşındaydım. Kurallar gereği herkesin ev yapımı bir dolma getirmesi gerekiyordu, yalnız marketten alınan “ev yapımı” dolmalar sayılmıyor. Organizatörlerden gelen resmi bir duyuruda belirtildiği gibi, “ev yapımı dolmanız kendi ellerinizle yapılmış olmalı”.

Parti sırasında kayıt işlemlerine yardım ettim. Yeni gelenler dolmaları için benden bir etiket alıyorlardı. Her dolma için dört parametre vardı: “vegan”, “vejetaryen”, “kırmızı et yok” ve “süt ürünü yok”. Bu yazıyı yazma fikri bu etiketlerden çıktı.

Bu dört parametrenin $2^4 = 16$ kombinasyonunun içinde bazılarının mümkün olmadığını görmek zor değil. Bir dolma “vegan” ise zaten diğer üç sınıfın da içinde olmak zorunda, dolayısıyla hem kırmızı et içerip hem de vegan olan bir dolma olamaz. Eğer biri böyle bir dolma yapmayı başarırsa bu gerçekten büyük bir başarı olur. Peki hangi kombinasyonlar mümkün? Önce tüm olası kombinasyonları sıralayacağız, sonra da her kombinasyon için ya imkansız olduğunu göstereceğiz ya da o kombinasyonda olan bir dolmanın hangi malzemeleri içerebileceğini/içermesi gerektiğini listeleyeceğiz. Bunun için aşağıdaki Venn diyagramını çizdim:

Bu diyagramdaki renkleri ve harfleri açıklayalım:

İmkansız olan bölgeleri kesinleştirdiğimize göre Venn diyagramını biraz basitleştirelim:

Diyagramdaki sayılar, partide hangi kategoride kaç dolma olduğunu gösteriyor.

İki kategorinin ("$0$” ve “$\underline{0}$” ile işaretli) dolma partisinde olmadığını görüyoruz. Bunların hala mümkün olduğunu göstermek için bu kategorilere girecek dolma örnekleri vermemiz gerekiyor:

Şu ana kadar yaptıklarımızı özetleyelim. $2^4 = 16$ kombinasyon arasında sadece $7$‘sinin mümkün olduğunu bulduk. Sonra her kategoride kaç dolma olduğunu saydık. 23. dolma partisinde görülmeyen kategoriler için de o kategorilere girecek dolma örnekleri verdik. Bu son kısmı gelecekteki dolma partileri için bir öneri olarak görmeyin, gördürmeyin.

Şimdi yukarıdaki Venn diyagramlarını çizerken dikkatimi çeken bir durumu ele alalım.

Daha dengeli bir dağılım için yeni kategoriler

Dolmaların kategorilere dağılımının dengesiz olduğunu gözlemliyoruz. Bazı kategorilerde çok dolma var, bazı kategorilerde ise hiç dolma yok. Bu bize şu soruyu sorduruyor:

“Dolmaları aralarında daha eşit dağıtacak başka kategoriler var mı?”

Önce birkaç detayı netleştirmemiz gerek. Parti sırasında iki aşamalı bir süreç vardı: Önce dört parametre tanımlandı, sonra da bu dört parametrenin her olası kombinasyonuna “kategori” denildi. Burada ise kategorileri doğrudan tanımlayacağız.

Yiyeceklerimizi malzeme varlığı/yokluğu ile tanımlanmış ikili vektörler olarak modelleyelim:

Sığır eti Tavuk eti Domates Zeytinyağı Süt Tereyağı Bal
Yiyecek 1 1 0 1 0 1 0 1
Yiyecek 2 0 1 1 0 0 1 1
Yiyecek 3 1 1 0 1 0 0 1
Yiyecek 4 0 0 1 1 1 0 0
Yiyecek 5 1 0 0 1 0 1 0

Yani “kategori” sözcüğünü, hangi malzemelere izin verildiğini/verilmediğini belirten bir Boolean ifade olarak tanımlayabiliriz. Venn diyagramındaki bölgeler nasıl kesişmiyorsa kategorilerin de kesişmesine izin vermeyeceğiz. Bu kural nedeniyle “vegan”, “vejetaryen”, “kırmızı et yok” ve “süt ürünü yok"u kategori değil, parametre olarak adlandırdık önceki paragraflarda.

Yukarıdaki tabloyu esas alırsak partideki dört parametrenin tanımları şöyle oluyor:

Elimizdeki veride başka malzemeler olsaydı tabi ki farklı tanımlara ihtiyacımız olurdu. Örneğin yiyeceklerden bazılarında yumurta olsaydı “vegan"ın tanımına "yumurta" YOK ifadesini eklememiz gerekirdi.

Dolmaları sınıflandırmak için kullandığımız dört parametrenin mantıklı olduğununun altını çizeyim. Bunlar bazı insanların gerçek hayatta uyduğu kurallar. Dolmaları birbirleri arasında daha dengeli dağıtan kategoriler ararken bu kategorilerin günlük hayatta anlamlı olup olmadığını umursamayacağız.

Doğrusal programlama kullanacağız. Önce algoritmamızın girdilerini netleştirelim. Dolmaları kaç kategoriye böleceğimiz bize söyleniyor. Ayrıca her dolmanın hangi malzemeleri içerdiğini biliyoruz, dolayısıyla partide (tüm dolmaları toplarsak) hangi malzemelerin kullanıldığını da biliyoruz. Bunları biraz daha matematiksel olarak ifade edelim:

Algoritmanın görevi:

Değişkenler:

Amaç fonksiyonu: $$ \begin{aligned} \min(S_{\text{max}} - S_{\text{min}}) \end{aligned} $$

Kısıtlamalar:

Algoritmanın kodu

Bu problem için Python temelli bir kütüphane olan PuLP kullanılarak yazılmış bir kodu burada bulabilirsiniz. Kodun içinde boyutları bir dolma partisini temsil etmeye yetecek boyutlarda (30 yemek, 30 malzeme) rastgele bir matris de var.

Algoritmaya kodun içindeki 30’a 30’luk matristeki yiyecekleri 5 kategoriye böldürdüğünüzde bu sonucu veriyor:

Result - Optimal solution found

Objective value:                0.00000000
Enumerated nodes:               2121
Total iterations:               113678
Time (CPU seconds):             21.71
Time (Wallclock seconds):       21.77

Option for printingOptions changed from normal to all
Total time (CPU seconds):       21.73   (Wallclock seconds):       21.80

Objective (S_max - S_min): 0.0

Category 0:
  Food 0
  Food 3
  Food 4
  Food 13
  Food 16
  Food 18
  Rule:
    NOT Ingredient 0
    NOT Ingredient 1
    HAS Ingredient 3
    HAS Ingredient 27

Category 1:
  Food 1
  Food 6
  Food 7
  Food 11
  Food 21
  Food 24
  Rule:
    HAS Ingredient 8
    HAS Ingredient 20
    HAS Ingredient 21
    NOT Ingredient 23

Category 2:
  Food 2
  Food 17
  Food 19
  Food 20
  Food 26
  Food 27
  Rule:
    HAS Ingredient 0
    HAS Ingredient 7
    HAS Ingredient 18
    HAS Ingredient 23

Category 3:
  Food 5
  Food 9
  Food 12
  Food 22
  Food 28
  Food 29
  Rule:
    HAS Ingredient 1
    HAS Ingredient 3
    NOT Ingredient 7

Category 4:
  Food 8
  Food 10
  Food 14
  Food 15
  Food 23
  Food 25
  Rule:
    NOT Ingredient 3
    NOT Ingredient 18

Sonuç

Programın 30’a 30’luk matris için çalışma süresi dizüstü bilgisayarımda yaklaşık 22 saniyeydi. Bu süre pek uzun değil ama parametreleri biraz bile arttırmak çok daha uzun çalışma sürelerine yol açıyor (doğrusal programlama için bu çok şaşırtıcı değil). Bu tip problemlerde buluşsal yöntemler mantıklı olabiliyor.

Kaynakça