Problemden Algoritmaya ( Ana Konu Giriş )
Algoritma geliştirmek, sadece hazır çözümleri uygulamak değil;
bir problemi analiz ederek onu mantıksal adımlara dönüştürme sürecidir.
Bu bölümde, metinsel problemleri nasıl parçalayacağını, doğru yaklaşımı nasıl seçeceğini
ve kendi algoritmalarını nasıl tasarlayacağını öğreneceksin.
Problemden Algoritmaya: Düşünmenin Mühendisliği Analiz → Parçalama → Çözüm Tasarımı
Çoğu geliştirici için algoritma, hazır çözümleri bilmek ve uygulamak gibi görünür. Eğitim sürecinde genellikle sıralama, arama veya klasik veri yapıları öğretilir ve bu algoritmaların nasıl yazıldığı gösterilir.
Ancak gerçek dünyada karşılaşılan problemler, nadiren birebir bu hazır kalıplara uyar. Çoğu problem, farklı kavramların birleşiminden oluşur ve doğrudan bir çözümle eşleşmez.
Bu noktada asıl zorluk, bilinen bir algoritmayı yazmak değil; problemi analiz ederek onu algoritmaya dönüştürmektir. Yani geliştirici, hazır bir çözümü uygulayan değil, çözümü tasarlayan kişiye dönüşmelidir.
Bir problemi çözebilmek için önce onu anlamak, sonra parçalara ayırmak ve en sonunda bu parçaları mantıksal bir akışa dönüştürmek gerekir. Bu süreç, yazılım geliştirmede en kritik zihinsel becerilerden biridir.
Problemi Görmek: İlk KatmanHer algoritma bir problemle başlar, ancak çoğu zaman problem açık ve net bir şekilde tanımlanmaz. Kullanıcı ihtiyaçları, sistem gereksinimleri veya metinsel açıklamalar genellikle belirsizlik içerir.
İyi bir geliştirici, verilen metnin arkasındaki yapıyı görür: veri nedir, hangi bilgiler giriş olarak alınır, hangi sonuç üretilmelidir ve hangi kısıtlar vardır.
Bu aşamada sorulması gereken kritik sorular şunlardır: veri tipi nedir, problem deterministik mi, sınır durumlar (edge cases) neler olabilir ve çözümün doğruluğu nasıl test edilir.
Yanlış anlaşılan bir problem, en iyi algoritma ile bile yanlış sonuç üretir. Bu nedenle algoritma tasarım süreci, her zaman doğru problem tanımı ile başlamak zorundadır.
Parçalama: Karmaşıklığı YönetmekKarmaşık problemler doğrudan çözülemez; çünkü tek parça halinde ele alındıklarında yönetilemez hale gelirler.
Bu nedenle problem, daha küçük ve bağımsız alt problemlere ayrılır. Her alt parça ayrı ayrı çözülebilir hale geldiğinde, bütün sistem daha anlaşılır ve kontrol edilebilir olur.
Bu yaklaşım sadece teorik bir yöntem değildir; modern yazılım mühendisliğinde kullanılan birçok teknik (modüler yapı, fonksiyonel parçalama, mikro servis mimarisi) bu prensibe dayanır.
Aslında tüm modern algoritmaların temelinde, problemi böl ve yönet yaklaşımı yer alır. Bu yaklaşım olmadan karmaşık sistemlerin kontrol edilmesi neredeyse imkansızdır.
Algoritma Tasarımı: Karar MekanizmasıBir algoritma, sabit ve doğrusal adımlar listesi değildir; veriyle birlikte değişen dinamik bir karar sistemidir.
Algoritmalar, koşullar ve durumlara göre farklı yollar izler. Bu yapı, if-else, döngüler ve kontrol mekanizmaları ile temsil edilir.
Her koşul, algoritmanın farklı bir yola gitmesine neden olur ve bu yapı çözümün esnekliğini belirler. Aynı algoritma, farklı girdilerde tamamen farklı davranışlar sergileyebilir.
Bu yüzden algoritma kurmak, sadece adımları sıralamak değil; doğru kararları doğru sırada verebilme becerisidir. Bu beceri, deneyim ve analiz yeteneği ile gelişir.
Amaç: Algoritma Yazmak Değil, Algoritma KurmakBu bölümde amaç, sana belirli algoritmaları ezberletmek değildir. Çünkü ezberlenen bilgi, sadece bilinen problemler için işe yarar.
Amaç, her yeni problem karşısında kendi çözümünü tasarlayabilecek bir düşünme sistemi kazandırmaktır. Bu sistem, analiz, parçalama ve karar verme süreçlerinin birleşiminden oluşur.
Gerçek yazılım geliştirme sürecinde, hazır çözümlerden çok, yeni çözümler üretme yeteneği belirleyicidir.
Bu nedenle burada öğreneceğin şey, tek bir algoritma değil; her probleme uygulanabilecek bir düşünme modelidir.
Uygulamaya Geçiş: Problemi Algoritmaya Dönüştürmek Teoriden Pratiğe İlk Adım
Şimdiye kadar algoritma kurmanın mantığını, problem analizini ve parçalama yaklaşımını öğrendin. Bu bilgiler, algoritmik düşünmenin teorik temelini oluşturur.
Ancak gerçek gelişim, bu bilgileri kullanmaya başladığın anda ortaya çıkar. Çünkü algoritma öğrenmek, pasif bir süreç değil; aktif bir üretim sürecidir.
Bu noktadan sonra amaç, anlatılanları okumak değil; kendi algoritmanı üretmeye başlamaktır. Yani artık çözümü görmek değil, çözümü kuran kişi olacaksın.
Her problemde senden beklenen şey; problemi doğru analiz etmek, onu daha küçük parçalara ayırmak ve bu parçaları mantıksal bir akış haline getirmektir.
Bu süreçte hata yapman normaldir. Asıl öğrenme, doğru cevabı görmekten değil; yanlış yaklaşımları fark etmekten gelir.
Nasıl İlerlemelisin?Aşağıda verilen problemleri çözerken doğrudan koda geçme. Kod yazmak, algoritma sürecinin son adımıdır; ilk adımı değil.
Önce problemi dikkatlice oku ve ne istendiğini netleştir. Ardından çözüm için gerekli adımları zihninde veya kağıt üzerinde oluştur.
Kendine şu soruları sor: Bu problem ne istiyor, hangi veriler var, hangi işlemler yapılmalı ve hangi durumlar sonucu değiştirebilir?
Çözüm adımların netleştiğinde, ancak o zaman kodlamaya geç. Böylece yazdığın kod, rastgele değil; bilinçli bir tasarımın sonucu olur.
Unutma: iyi algoritmalar editörde değil, zihinde başlar. Kod sadece bu düşüncenin dışa yansımasıdır.
Tekrar Eden Sayıyı Bul
Elinde bir sayı listesi bulunmaktadır. Bu liste içerisinde bazı sayılar birden fazla kez tekrar edebilir.
Senin görevin, bu listeyi analiz ederek tekrar eden sayıyı veya sayıları tespit etmektir. Ancak burada önemli olan sadece sonucu bulmak değil; bu sonuca nasıl ulaştığını mantıksal olarak kurgulamaktır.
Problemi çözerken tüm olasılıkları düşünmeli, gereksiz işlemlerden kaçınmalı ve çözümünü mümkün olduğunca verimli hale getirmeye çalışmalısın.
// Problem: Bir listede tekrar eden sayıyı bul
function tekrarEdeniBul(list) {
// 1. Önce boş veya hatalı veri kontrolü yapalım
if (!list || list.length === 0) {
return null;
}
// 2. İlk akla gelen çözüm:
// Her elemanı diğerleriyle karşılaştırmak (O(n²))
// Ama bu çok yavaş olur → daha iyi bir yol bulmalıyız
// 3. Daha önce gördüğümüz sayıları tutabileceğimiz bir yapı kullanalım
const gorulenler = new Set();
// 4. Listeyi baştan sona dolaşıyoruz
for (let i = 0; i < list.length; i++) {
const sayi = list[i];
// 5. Eğer bu sayıyı daha önce gördüysek → tekrar eden!
if (gorulenler.has(sayi)) {
return sayi;
}
// 6. İlk kez görüyorsak kaydet
gorulenler.add(sayi);
}
// 7. Hiç tekrar eden yoksa
return null;
}
// Test edelim
const sayilar = [1, 3, 5, 3, 7];
const sonuc = tekrarEdeniBul(sayilar);
if (sonuc !== null) {
console.log("Tekrar eden sayı:", sonuc);
} else {
console.log("Tekrar eden sayı bulunamadı.");
}
Hedef Toplamı Bul
Elinde bir sayı listesi ve bir hedef değer bulunmaktadır.
Bu listede bulunan iki farklı sayının toplamı, verilen hedef değere eşit olabilir. Senin görevin, bu iki sayıyı bulmaktır.
Ancak burada önemli olan sadece doğru sonucu bulmak değil; bu sonuca en verimli şekilde nasıl ulaştığını tasarlamaktır.
Tüm kombinasyonları denemek mümkün olsa da, bu yaklaşım büyük veri setlerinde ciddi performans problemlerine yol açabilir.
// Problem: İki sayının toplamı hedefe eşit mi?
function twoSum(list, target) {
// 1. Görülen sayıları tutalım
const gorulenler = new Set();
for (let i = 0; i < list.length; i++) {
const sayi = list[i];
// 2. Aradığımız şey:
// target = sayi + ? → eksik olanı bul
const gereken = target - sayi;
// 3. Eğer daha önce gördüysek → bulduk!
if (gorulenler.has(gereken)) {
return [gereken, sayi];
}
// 4. Görülenlere ekle
gorulenler.add(sayi);
}
return null;
}
// Test
console.log(twoSum([2, 7, 11, 15], 9)); // [2, 7]
Serbest Düşme Süresini Hesapla
Belirli bir yükseklikten bırakılan bir cismin, yer yüzüne ulaşma süresini hesaplamak istiyoruz.
Gerçek fizik kurallarını tam olarak modellemek yerine, basitleştirilmiş bir yaklaşım kullanacağız. Cismin her zaman adımında hızının arttığını ve aşağı doğru hareket ettiğini varsay.
Amaç, cismin başlangıç yüksekliğinden sıfıra ulaşana kadar geçen süreyi bulmaktır. Bu süreçte hareketi küçük zaman adımlarına bölerek simüle etmelisin.
// Problem: Cismin yere düşme süresini hesapla (simülasyon ile)
function dusmeSuresi(yukseklik) {
// Yerçekimi sabiti (basitleştirilmiş)
const g = 9.8;
// Başlangıç değerleri
let hiz = 0;
let konum = yukseklik;
let zaman = 0;
// Zaman adımı (küçük parçalar halinde ilerleyeceğiz)
const dt = 0.1;
// Cisim yere ulaşana kadar devam et
while (konum > 0) {
// 1. Hız artar
hiz += g * dt;
// 2. Konum değişir
konum -= hiz * dt;
// 3. Zaman ilerler
zaman += dt;
}
return zaman;
}
// Test
console.log(dusmeSuresi(100)); // ~4-5 saniye civarı
Mum Tüketimi ve Yeniden Üretim Problemi
Elinde belirli sayıda mum bulunmaktadır. Her mum tamamen yandığında bir adet artık parça üretir.
Belirli sayıda artık parça bir araya getirildiğinde, yeni bir mum üretilebilir. Örneğin, 3 artık parça → 1 yeni mum oluşturur.
Amaç, başlangıçtaki mum sayısına göre toplamda kaç saat ışık elde edilebileceğini bulmaktır.
Bu süreçte mumlar yanacak, artık parçalar birikecek ve mümkün olduğunda tekrar yeni mumlara dönüşecektir. Tüm bu döngüyü doğru şekilde modellemen gerekir.
// Problem: Mumlardan maksimum kaç saat ışık elde edilir?
function toplamYanisSuresi(mumSayisi) {
// Kaç saat yandığını tutacağız
let toplamSaat = 0;
// Artıkları tutalım
let artiklar = 0;
// Yeni mum üretmek için gereken miktar
const gerekliArtik = 3;
// Elimizde mum olduğu sürece devam
while (mumSayisi > 0) {
// 1. Tüm mumları yakıyoruz
toplamSaat += mumSayisi;
// 2. Her mumdan bir artık gelir
artiklar += mumSayisi;
// 3. Artıklardan yeni mum üretelim
const yeniMum = Math.floor(artiklar / gerekliArtik);
// 4. Kalan artıkları hesapla
artiklar = artiklar % gerekliArtik;
// 5. Yeni mumlar bir sonraki turda yakılacak
mumSayisi = yeniMum;
}
return toplamSaat;
}
// Test
console.log(toplamYanisSuresi(5));
// 5 mum → toplam kaç saat?
Asansör Hareket Planlama Problemi
Bir binada çalışan bir asansör sistemi bulunmaktadır. Asansör belirli bir katta durmaktadır ve kullanıcılar farklı katlardan asansörü çağırmaktadır.
Her çağrı, bulunduğu kat ve gitmek istediği yön (yukarı / aşağı) bilgisi ile birlikte gelir.
Amaç, asansörün bu çağrılara nasıl cevap vereceğini belirlemek ve en mantıklı hareket sırasını oluşturmaktır.
Asansör bir yöne doğru hareket ederken, aynı yöndeki çağrıları öncelikli olarak karşılamalıdır. Yön değiştirme kararı ise mevcut çağrılara göre verilmelidir.
// Problem: Asansör çağrılara nasıl cevap verir?
function asansorPlanla(baslangicKat, cagriListesi) {
let mevcutKat = baslangicKat;
let yon = "yukari"; // başlangıç yönü
const rota = [];
// 1. Çağrıları ayıralım
const yukariCagrilar = cagriListesi.filter(c => c.kat >= mevcutKat);
const asagiCagrilar = cagriListesi.filter(c => c.kat < mevcutKat);
// 2. Yukarı yönünde olanları sırala
yukariCagrilar.sort((a, b) => a.kat - b.kat);
// 3. Aşağı yönünde olanları ters sırala
asagiCagrilar.sort((a, b) => b.kat - a.kat);
// 4. Önce yukarı git
for (let i = 0; i < yukariCagrilar.length; i++) {
rota.push(yukariCagrilar[i].kat);
mevcutKat = yukariCagrilar[i].kat;
}
// 5. Sonra yön değiştir
yon = "asagi";
for (let i = 0; i < asagiCagrilar.length; i++) {
rota.push(asagiCagrilar[i].kat);
mevcutKat = asagiCagrilar[i].kat;
}
return rota;
}
// Test
const cagrilar = [
{ kat: 5, yon: "yukari" },
{ kat: 2, yon: "asagi" },
{ kat: 8, yon: "yukari" },
{ kat: 1, yon: "yukari" }
];
console.log(asansorPlanla(3, cagrilar));
Trafik Lambası Zamanlama Problemi
Bir kavşakta çalışan trafik lambası sistemi bulunmaktadır. Bu sistem belirli zaman aralıklarında kırmızı, sarı ve yeşil ışıklar arasında geçiş yapar.
Her ışığın belirli bir süresi vardır. Örneğin: yeşil → 5 saniye, sarı → 2 saniye, kırmızı → 5 saniye.
Amaç, verilen toplam süre boyunca trafik lambasının hangi anlarda hangi durumda olacağını belirlemektir.
Sistem sürekli döngü halinde çalışır ve her geçiş bir zaman olayına (event) bağlıdır. Bu nedenle çözümünü zaman akışı üzerinden modellemen gerekir.
// Problem: Belirli bir zamanda trafik lambası hangi durumda?
function trafikDurumuBul(sure) {
// Işık süreleri
const yesil = 5;
const sari = 2;
const kirmizi = 5;
// Toplam döngü süresi
const dongu = yesil + sari + kirmizi;
// Zamanı döngüye indir
const zaman = sure % dongu;
// 1. Yeşil aralığı
if (zaman < yesil) {
return "Yeşil";
}
// 2. Sarı aralığı
if (zaman < yesil + sari) {
return "Sarı";
}
// 3. Kırmızı aralığı
return "Kırmızı";
}
// Test
for (let i = 0; i < 20; i++) {
console.log(i + " saniye:", trafikDurumuBul(i));
}
Oyun Karakteri HP Yenilenme (Regen) Problemi
Bir oyun karakterinin can (HP) değeri zamanla otomatik olarak yenilenmektedir.
Karakter belirli aralıklarla HP kazanır. Örneğin, her 2 saniyede +5 HP kazanabilir. Ancak HP değeri maksimum bir sınırı geçemez.
Ayrıca karakter hasar alabilir ve bu durum HP değerini düşürür.
Amaç, belirli bir zaman süresi boyunca karakterin HP değerinin nasıl değiştiğini hesaplamaktır. Bu süreçte zaman, hasar ve yenilenme birlikte değerlendirilmelidir.
// Problem: Karakterin HP değişimini zaman içinde hesapla
function hpSimulasyon(baslangicHP, maxHP, toplamSure) {
let hp = baslangicHP;
// Regen ayarları
const regenMiktar = 5;
const regenAralik = 2;
// Örnek hasar olayları (zaman → hasar)
const hasarlar = {
3: 10,
7: 15
};
// Zamanı simüle edelim
for (let zaman = 1; zaman <= toplamSure; zaman++) {
// 1. Hasar kontrolü
if (hasarlar[zaman]) {
hp -= hasarlar[zaman];
console.log(zaman + "s → Hasar alındı:", hasarlar[zaman]);
}
// 2. Regen kontrolü
if (zaman % regenAralik === 0) {
hp += regenMiktar;
console.log(zaman + "s → HP yenilendi:", regenMiktar);
}
// 3. Limitleri koru
if (hp > maxHP) hp = maxHP;
if (hp < 0) hp = 0;
console.log(zaman + "s → HP:", hp);
}
return hp;
}
// Test
hpSimulasyon(50, 100, 10);
Su Tankı Dolum Problemi
Belirli kapasiteye sahip bir su tankı bulunmaktadır. Tank zamanla dolmakta ve aynı zamanda kullanım nedeniyle su seviyesi azalmaktadır.
Tankın bir alt sınırı (kritik seviye) ve bir üst sınırı (maksimum kapasite) vardır. Su seviyesi alt sınırın altına düştüğünde dolum sistemi devreye girer.
Dolum sistemi aktifken tank suyla doldurulur; üst sınıra ulaşıldığında sistem kapanır.
Amaç, belirli bir zaman boyunca tankın su seviyesinin nasıl değiştiğini ve dolum sisteminin ne zaman aktif olduğunu modellemektir.
// Problem: Tankın dolum sistemini kontrol et
function tankSimulasyon(baslangicSeviye, kapasite, altSinir, toplamSure) {
let seviye = baslangicSeviye;
let dolumAcik = false;
const dolumHizi = 10; // saniyede +10
const tuketim = 4; // saniyede -4
for (let zaman = 1; zaman <= toplamSure; zaman++) {
// 1. Önce tüketim (su kullanımı)
seviye -= tuketim;
// 2. Alt sınır kontrolü → dolumu aç
if (seviye <= altSinir) {
dolumAcik = true;
}
// 3. Dolum açıksa su ekle
if (dolumAcik) {
seviye += dolumHizi;
}
// 4. Üst sınır → dolumu kapat
if (seviye >= kapasite) {
seviye = kapasite;
dolumAcik = false;
}
// 5. Negatif olmasın
if (seviye < 0) seviye = 0;
console.log(
zaman + "s → Seviye:",
seviye,
"| Dolum:",
dolumAcik ? "AÇIK" : "KAPALI"
);
}
return seviye;
}
// Test
tankSimulasyon(50, 100, 30, 10);
Basket Atış Olasılığı (Monte Carlo Simülasyonu)
Bir basketbol topu belirli bir yükseklikten, belirli bir açı ve hız ile potaya doğru atılmaktadır.
Ancak gerçek dünyada olduğu gibi, atış sırasında rüzgar gibi dış etkenler topun yönünü küçük miktarlarda değiştirebilir.
Bu nedenle tek bir hesaplama yapmak yerine, aynı atışı birçok kez simüle ederek topun potaya girme olasılığını tahmin etmen gerekmektedir.
Amaç, belirli sayıda deneme sonucunda kaç atışın başarılı olduğunu hesaplamak ve başarı oranını bulmaktır.
// Problem: Rastgele etkiler altında basket olma olasılığı
function basketSimulasyon(denemeSayisi) {
let basarili = 0;
console.log("Simülasyon başlıyor...");
console.log("Toplam deneme:", denemeSayisi);
for (let i = 1; i <= denemeSayisi; i++) {
// 1. Temel atış doğruluğu (ideal senaryo)
let isabet = 0.7; // %70 temel başarı
// 2. Rüzgar etkisi (rastgele sapma)
const ruzgar = (Math.random() - 0.5) * 0.4;
// -0.2 ile +0.2 arası
// 3. Son isabet oranı
const sonIsabet = isabet + ruzgar;
// 4. Rastgele atış sonucu
const atis = Math.random();
if (atis < sonIsabet) {
basarili++;
console.log("Deneme", i, "→ Basket!");
} else {
console.log("Deneme", i, "→ Kaçtı.");
}
}
const oran = basarili / denemeSayisi;
console.log("---------------");
console.log("Başarılı atış:", basarili);
console.log("Başarı oranı:", (oran * 100).toFixed(2) + "%");
return oran;
}
// Test
basketSimulasyon(10);
Kelime Zinciri Problemi (Graph & Shortest Path)
Sana bir başlangıç kelimesi, bir hedef kelime ve geçerli kelimelerden oluşan bir sözlük verilmektedir.
Her adımda yalnızca tek bir harfi değiştirerek yeni bir kelime oluşturabilirsin. Oluşturduğun her kelime sözlükte bulunmak zorundadır.
Amaç, başlangıç kelimesinden hedef kelimeye ulaşan en kısa kelime zincirini bulmaktır.
Eğer böyle bir zincir yoksa, sonuç olarak ulaşılamaz olduğunu belirtmelisin.
// Problem: En kısa kelime zincirini bul
function kelimeZinciri(baslangic, hedef, sozluk) {
// 1. Sözlüğü hızlı arama için Set'e alalım
const kelimeSet = new Set(sozluk);
// 2. BFS kuyruğu (kelime + adım sayısı)
const kuyruk = [[baslangic, 1]];
// 3. Ziyaret edilenleri tutalım
const ziyaretEdilen = new Set([baslangic]);
console.log("Başlangıç:", baslangic);
console.log("Hedef:", hedef);
console.log("------------------");
while (kuyruk.length > 0) {
const [kelime, adim] = kuyruk.shift();
console.log("İncelenen kelime:", kelime);
// 4. Hedefe ulaştık mı?
if (kelime === hedef) {
console.log("Hedef bulundu!");
return adim;
}
// 5. Her harfi tek tek değiştirelim
for (let i = 0; i < kelime.length; i++) {
for (let c = 97; c <= 122; c++) {
const yeniHarf = String.fromCharCode(c);
const yeniKelime =
kelime.slice(0, i) + yeniHarf + kelime.slice(i + 1);
// 6. Geçerli mi?
if (kelimeSet.has(yeniKelime) && !ziyaretEdilen.has(yeniKelime)) {
ziyaretEdilen.add(yeniKelime);
kuyruk.push([yeniKelime, adim + 1]);
console.log("→ Yeni kelime bulundu:", yeniKelime);
}
}
}
}
return 0; // ulaşamadı
}
// Test
const sozluk = ["hot", "dot", "dog", "lot", "log", "cog"];
console.log(
"Sonuç:",
kelimeZinciri("hit", "cog", sozluk)
);
Dijital Saat Rakamları Problemi
Bir dijital saat 00:00 ile 23:59 arasında çalışmaktadır.
Saat ekranında görünen dört hane (HH:MM) ayrı ayrı rakamlardan oluşur. Örneğin 12:34 → 1 + 2 + 3 + 4.
Amaç, tüm zaman dilimlerini inceleyerek: rakamları toplamı en büyük ve en küçük olan saatleri bulmaktır.
Tüm olası zamanları sistematik olarak kontrol etmen ve en iyi sonuçları belirlemen gerekir.
// Problem: Rakam toplamı en büyük ve en küçük saatleri bul
function saatAnaliz() {
let maxToplam = -Infinity;
let minToplam = Infinity;
let maxSaat = "";
let minSaat = "";
console.log("Tüm zamanlar inceleniyor...");
// 1. Saatleri gez (00 - 23)
for (let saat = 0; saat < 24; saat++) {
// 2. Dakikaları gez (00 - 59)
for (let dakika = 0; dakika < 60; dakika++) {
// 3. String'e çevir (leading zero için)
const saatStr = saat.toString().padStart(2, "0");
const dakikaStr = dakika.toString().padStart(2, "0");
const zaman = saatStr + ":" + dakikaStr;
// 4. Rakamları topla
const toplam =
Number(saatStr[0]) +
Number(saatStr[1]) +
Number(dakikaStr[0]) +
Number(dakikaStr[1]);
// 5. Max kontrol
if (toplam > maxToplam) {
maxToplam = toplam;
maxSaat = zaman;
console.log("Yeni MAX bulundu:", zaman, "→", toplam);
}
// 6. Min kontrol
if (toplam < minToplam) {
minToplam = toplam;
minSaat = zaman;
console.log("Yeni MIN bulundu:", zaman, "→", toplam);
}
}
}
console.log("------------------");
console.log("En büyük toplam:", maxSaat, "→", maxToplam);
console.log("En küçük toplam:", minSaat, "→", minToplam);
}
// Test
saatAnaliz();
Oyun Skoru: Kombinasyon Patlaması
Bir oyunda bloklar yatay bir sıra halinde dizilidir ve her blok farklı bir renge sahiptir.
Eğer art arda gelen 3 veya daha fazla aynı renk blok varsa, bu bloklar patlar ve oyun tahtasından kaldırılır.
Patlama sonrası, kalan bloklar boşlukları doldurmak için sola doğru kayar.
Ancak bu işlem tek seferlik değildir. Kayma sonrası tekrar aynı durum oluşursa, yeni patlamalar gerçekleşebilir.
Amaç, verilen başlangıç tahtasına göre toplam kaç patlama gerçekleştiğini bulmaktır.
// Problem: Zincirleme patlamaları simüle et
function patlamaSimulasyonu(tahta) {
let patlamaSayisi = 0;
console.log("Başlangıç:", tahta.join(" "));
while (true) {
let yeniTahta = [];
let i = 0;
let patlamaOldu = false;
// 1. Diziyi tarıyoruz
while (i < tahta.length) {
let j = i;
// 2. Aynı blokları say
while (j < tahta.length && tahta[j] === tahta[i]) {
j++;
}
const uzunluk = j - i;
// 3. Eğer 3+ ise patlat
if (uzunluk >= 3) {
console.log("Patlama:", tahta[i], "x", uzunluk);
patlamaSayisi++;
patlamaOldu = true;
} else {
// Patlama yoksa koru
for (let k = i; k < j; k++) {
yeniTahta.push(tahta[k]);
}
}
i = j;
}
// 4. Eğer patlama olmadıysa dur
if (!patlamaOldu) break;
tahta = yeniTahta;
console.log("Sonraki durum:", tahta.join(" "));
console.log("------------------");
}
console.log("Toplam patlama:", patlamaSayisi);
return patlamaSayisi;
}
// Test
patlamaSimulasyonu(["🔴","🔴","🔴","🟢","🔵"]);
Park Yeri Bulma Simülasyonu
Bir otoparkta farklı büyüklükte park alanları bulunmaktadır. Her park yeri belirli bir araç boyutuna uygundur (S, M, L).
Araçlar sırayla gelir ve her araç için: uygun büyüklükte ve kapıya en yakın boş park yeri seçilmelidir.
Eğer uygun park yeri yoksa araç park edemez.
Ayrıca bazı araçlar otoparktan çıkabilir ve bu durumda yer tekrar boş hale gelir.
Amaç, gelen araçların hangi park yerlerine yerleştirileceğini simüle etmektir.
// Problem: Araçları en uygun park yerlerine yerleştir
function parkSimulasyon(yerler, araclar) {
// Park durumu (true = dolu)
const dolu = Array(yerler.length).fill(false);
// Boyut uygunluk kontrolü
function uygunMu(yer, arac) {
if (yer === "L") return true;
if (yer === "M" && (arac === "M" || arac === "S")) return true;
if (yer === "S" && arac === "S") return true;
return false;
}
const sonuc = [];
for (let i = 0; i < araclar.length; i++) {
const arac = araclar[i];
let bulundu = false;
console.log("Araç geldi:", arac);
// 1. Baştan sona tara (kapıya en yakın)
for (let j = 0; j < yerler.length; j++) {
// 2. Boş mu ve uygun mu?
if (!dolu[j] && uygunMu(yerler[j], arac)) {
dolu[j] = true;
sonuc.push({ arac, yer: j });
console.log("→ Park edildi:", j);
bulundu = true;
break;
}
}
if (!bulundu) {
console.log("→ Yer bulunamadı!");
sonuc.push({ arac, yer: null });
}
console.log("------------------");
}
return sonuc;
}
// Test
const yerler = ["S","M","L","M","S","L","M","S","M","L"];
const araclar = ["M","S","L","M"];
console.log(parkSimulasyon(yerler, araclar));
Döngüsel Kaydırma ve Periyot Bulma
Bir sayı dizisi verilmiştir. Her adımda dizi belirli bir miktar (k) kadar sağa kaydırılır.
Kaydırma işlemi döngüseldir; yani dizinin sonundaki elemanlar başa geçer.
Amaç, dizinin tekrar başlangıç haline dönmesi için kaç adım gerektiğini bulmaktır.
Bu problemde, dizinin periyodunu hesaplaman gerekmektedir.
// Problem: Dizi kaç adımda eski haline döner?
function periyotBul(dizi, k) {
const n = dizi.length;
let sayac = 0;
let current = [...dizi];
console.log("Başlangıç:", current);
while (true) {
// 1. Sağa k kadar kaydır
const yeni = Array(n);
for (let i = 0; i < n; i++) {
yeni[(i + k) % n] = current[i];
}
current = yeni;
sayac++;
console.log("Adım", sayac, ":", current);
// 2. Eski haline döndü mü?
if (JSON.stringify(current) === JSON.stringify(dizi)) {
break;
}
}
console.log("Toplam adım:", sayac);
return sayac;
}
// Test
periyotBul([1,2,3,4], 2);
Zar Atışı ve Riskli Puan Sistemi
Bir oyuncu zar atarak puan toplamaya çalışmaktadır.
Eğer zar sonucu çift sayı gelirse, bu sayı puana eklenir.
Eğer tek sayı gelirse, o ana kadar kazanılan tüm puan sıfırlanır.
Amaç, belirli bir hedef puana ulaşmak için ortalama kaç zar atışı gerektiğini simülasyon ile hesaplamaktır.
Bu problemde tek bir sonuç değil, çoklu denemeler üzerinden olasılıksal bir ortalama hesaplanmalıdır.
// Problem: 20 puana ulaşmak için ortalama kaç zar gerekir?
function zarSimulasyon(hedef, denemeSayisi) {
let toplamAtis = 0;
for (let sim = 1; sim <= denemeSayisi; sim++) {
let puan = 0;
let atisSayisi = 0;
// Her simülasyon bağımsız
while (puan < hedef) {
const zar = Math.floor(Math.random() * 6) + 1;
atisSayisi++;
// Çift mi?
if (zar % 2 === 0) {
puan += zar;
} else {
// Tek → reset
puan = 0;
}
}
toplamAtis += atisSayisi;
}
const ortalama = toplamAtis / denemeSayisi;
console.log("Toplam simülasyon:", denemeSayisi);
console.log("Ortalama atış:", ortalama.toFixed(2));
return ortalama;
}
// Test
zarSimulasyon(20, 10000);
Hafıza Kartları Eşleştirme Problemi
4x4 boyutunda bir kart dizisi bulunmaktadır ve toplam 8 farklı kart çifti içerir. Kartlar karışık şekilde yerleştirilmiştir.
Her hamlede oyuncu iki kart açar: eğer kartlar eşleşirse açık kalır, eşleşmezse tekrar kapanır.
Bir bot bu oyunu oynamaktadır. Bot, daha önce açtığı kartların konumlarını hatırlayabilir.
Amaç, bu botun tüm kartları eşleştirmesi için ortalama kaç hamle gerektiğini simülasyon ile hesaplamaktır.
// Kartları karıştır
function karistir(dizi) {
return dizi.sort(() => Math.random() - 0.5);
}
function memorySimulasyon(denemeSayisi) {
let toplamHamle = 0;
for (let sim = 0; sim < denemeSayisi; sim++) {
// 8 çift oluştur
let kartlar = karistir([
"A","A","B","B","C","C","D","D",
"E","E","F","F","G","G","H","H"
]);
let acik = Array(16).fill(false);
let hafiza = {}; // kart → index listesi
let eslesen = 0;
let hamle = 0;
while (eslesen < 8) {
hamle++;
let secimler = [];
// 1. Hafızada eşleşme var mı?
for (let kart in hafiza) {
if (hafiza[kart].length === 2) {
secimler = hafiza[kart];
break;
}
}
// 2. Yoksa rastgele seç
if (secimler.length === 0) {
let i = Math.floor(Math.random() * 16);
let j;
do {
j = Math.floor(Math.random() * 16);
} while (j === i);
secimler = [i, j];
}
const [i, j] = secimler;
const kart1 = kartlar[i];
const kart2 = kartlar[j];
// 3. Hafızaya ekle
hafiza[kart1] = hafiza[kart1] || [];
hafiza[kart2] = hafiza[kart2] || [];
if (!hafiza[kart1].includes(i)) hafiza[kart1].push(i);
if (!hafiza[kart2].includes(j)) hafiza[kart2].push(j);
// 4. Eşleşme kontrolü
if (kart1 === kart2) {
eslesen++;
delete hafiza[kart1];
}
}
toplamHamle += hamle;
}
const ortalama = toplamHamle / denemeSayisi;
console.log("Ortalama hamle:", ortalama.toFixed(2));
return ortalama;
}
// Test
memorySimulasyon(1000);
Dairesel Sıra - Rastgele Elenme Oyunu
N tane çocuk daire şeklinde oturmaktadır.
Her turda, rastgele bir sayı (1 ile 3 arasında) seçilir ve bu sayıya göre sıradaki çocuk elenir.
Elenen çocuk oyundan çıkar ve sıra kalan çocuklar arasında devam eder.
Bu süreç yalnızca bir çocuk kalana kadar devam eder.
Amaç, bu oyunu birçok kez simüle ederek, hangi pozisyonun daha sık kazandığını analiz etmektir.
// Problem: Rastgele elenme ile son kalan kişiyi bul
function josephusSimulasyon(n, denemeSayisi) {
const kazananSayaci = Array(n).fill(0);
for (let sim = 0; sim < denemeSayisi; sim++) {
let cocuklar = Array.from({ length: n }, (_, i) => i);
let index = 0;
while (cocuklar.length > 1) {
// 1. Rastgele adım (1-3)
const k = Math.floor(Math.random() * 3) + 1;
// 2. Yeni index (dairesel)
index = (index + k - 1) % cocuklar.length;
// 3. Eleme
cocuklar.splice(index, 1);
}
// Son kalan
kazananSayaci[cocuklar[0]]++;
}
console.log("Kazanan dağılımı:");
console.log(kazananSayaci);
return kazananSayaci;
}
// Test
josephusSimulasyon(5, 10000);
Kutu İstifleme - Denge Problemi
Elinde farklı genişliklere sahip kutular bulunmaktadır.
Kutular alt alta dizilecektir. Ancak bir kutu, altındaki kutudan daha geniş olamaz.
Yani her üst kutunun genişliği, altındaki kutudan küçük veya eşit olmalıdır.
Amaç, verilen kutularla oluşturulabilecek en yüksek kuleyi (maksimum kutu sayısı) bulmaktır.
// Problem: En yüksek kuleyi bul
function maxKule(kutular) {
// 1. Büyükten küçüğe sırala
kutular.sort((a, b) => b - a);
const n = kutular.length;
// 2. DP array
const dp = Array(n).fill(1);
// 3. LIS benzeri çözüm
for (let i = 1; i < n; i++) {
for (let j = 0; j < i; j++) {
// üst ≤ alt
if (kutular[i] <= kutular[j]) {
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
}
const max = Math.max(...dp);
console.log("Kule yüksekliği:", max);
return max;
}
// Test
maxKule([5, 3, 4, 2, 1]);
Film Öneri Sistemi (k-NN Yaklaşımı)
Bir kullanıcı bazı filmleri 1-5 arasında puanlamıştır.
Elinde diğer kullanıcıların da film puanları bulunmaktadır.
Amaç, hedef kullanıcıya en benzer kullanıcıları bularak, izlenmemiş filmler için puan tahmini yapmaktır.
Bunun için kullanıcılar arası benzerlik hesaplanmalı ve en yakın kullanıcıların puanları kullanılmalıdır.
// Kullanıcı benzerliği (Euclidean Distance)
function mesafe(a, b) {
let toplam = 0;
for (let i = 0; i < a.length; i++) {
if (a[i] !== null && b[i] !== null) {
toplam += Math.pow(a[i] - b[i], 2);
}
}
return Math.sqrt(toplam);
}
function filmOneri(kullanici, digerleri, k = 2) {
// 1. Mesafeleri hesapla
const mesafeler = digerleri.map((u, index) => ({
index,
mesafe: mesafe(kullanici, u)
}));
// 2. En yakın k kullanıcı
mesafeler.sort((a, b) => a.mesafe - b.mesafe);
const enYakinlar = mesafeler.slice(0, k);
console.log("En yakın kullanıcılar:", enYakinlar);
// 3. Tahmin yap
const tahmin = [...kullanici];
for (let i = 0; i < kullanici.length; i++) {
if (kullanici[i] === null) {
let toplam = 0;
let sayi = 0;
for (let komsu of enYakinlar) {
const puan = digerleri[komsu.index][i];
if (puan !== null) {
toplam += puan;
sayi++;
}
}
if (sayi > 0) {
tahmin[i] = toplam / sayi;
}
}
}
console.log("Tahmin edilen puanlar:", tahmin);
return tahmin;
}
// Test
const hedef = [5, null, 3, null, 1];
const digerleri = [
[5, 4, 3, 2, 1],
[4, 5, 3, 3, 1],
[1, 2, 3, 4, 5]
];
filmOneri(hedef, digerleri);
Gezegen Üzerinde Oturma Eşiği (Sürekli Büyüme)
Canlı ya da cansız bir yapı, gezegen yüzeyinde dururken oturma tabanı yarıçapı r olan dairesel bir alan kaplıyor kabul edilsin. Gezegenin yarıçapı R olsun.
Her gün sonunda taban yarıçapı sabit bir çarpanla büyür: r ← r · k (k > 1). “Gezegenin üzerinde oturacak kadar büyük” tanımı şudur: yapı, gezegeni bir tabure gibi görebilsin diye tabanının yarıçapı gezegeninki kadar veya daha büyük olmalıdır; yani hedef r ≥ R.
Başlangıçta r₀ ve R veriliyor. Görevin, hedefe ulaşmak için gereken en küçük gün sayısı (tam gün) veya imkânsızsa -1 döndüren algoritmayı tasarlamaktır.
İmkânsız durumlar: r₀ ≥ R ise cevap 0 gündür. k ≤ 1 ve hâlâ r₀ < R ise büyüme yetmez; cevap -1 olmalıdır.
// Problem: r her gün r * k; en az kaç gün sonra r >= R ?
function gezegenOturmaGunu(R, r0, k) {
if (r0 >= R) {
return 0;
}
if (k <= 1) {
return -1;
}
let r = r0;
let gun = 0;
const limit = 1_000_000;
while (r < R) {
r *= k;
gun++;
if (gun > limit) {
return -1;
}
}
return gun;
}
// Test
console.log(gezegenOturmaGunu(6371, 1, 1.5));
console.log(gezegenOturmaGunu(10, 10, 2));
console.log(gezegenOturmaGunu(100, 1, 1));