Ben Matematikten pek anlamıyorum

Eğer bir “yazılımcı” iseniz bu cümleyi söylemeye hakkınız yok!

Yazılımla ilgili olarak eğitim gören ya da meslekteki arkadaşlarımızın başlıktaki sözü söylemeye hakları olmadığını düşünüyorum. Bir yazılımcı, hatta kişisel görüşüm yaşayan herkes, Matematikten anlamak zorundadır. Buradaki “Matematik” kelimesinden katsım illa ki katlı integral çözmek ya da bir takım formülleri hatırlamak değil, Matematiğin özü olan temel mantıksal çıkarım ve sayılarla oynama marifetine, o düşünüş biçimine sahip olmaktır. Bence, hayatta olan herkesin nefes alma, yemek yeme, su içme gibi temel ihtiyaçlarından biridir Matematik. Matematik ve Mantık biliminin hayatta kattıklarından uzak her insan, beyinsel olarak gıda ve içecekten uzak bir insanla aynı çaresizlik içindedir.

Konu yazılım olduğunda ise bu konu çok daha ciddi boyutlara ulaşır. Çoğu arkadaşımız bu konuya “uzaya rokey fırlatmıyoruz“, “yoğun matematiksel çözümler gerektiren bir iş yapmıyoruz” ya da “işim Matematik gerektirmiyor” anlayışı ile baksa da kodlarınız değilse bile kod yazışınız, kod yazma biçiminiz Matematiğe muhtaç. Farkında olun ya da olmayın Matematiğin programlamaya kattığı o kadar çok şey var ki. Pek çok insan yazılıma bugün bir mühendislik nosyonu olarak baksa da, aslında yazılım temelini birebir Matematikten alan, bugünkü varlığını birebir Matematiğe borçlu bir alan. Bunun için isterseniz aşağıdaki listeye bir bakalım.

  • Muhammed ibn Musa El-Harezmi :Algoritma” kelimesi adından türetilen Matematikçi, Astronom ve Coğrafyacı.
  • Frances Elizabeth Allen :Bit vektör” ve “Kontrol akış gösterimi” kavramlarını kazandıran Öğretmen ve Matematikçi.
  • George Boole:Bilgisayar bilimi“nin temeli olan ve “Sayısal Mantık“ı bulan Matematikçi, Filozof ve Mantıkçı.
  • John Guttag :Soyut veri tipleri” ve “Veri yapılarının geliştirilmesi” konularında çalışmış Matematikçi.
  • Edsger Wybe Dijkstra :İşletim sistemleri“, “Sıralı ve eş-zamanlı programlama“, “Dağıtık sistemler” konularında çalışmış Teorik Fizikçi ve Matematikçi.
  • Ole-Johan Dahl :Sınıf ve Alt-sınıf Bildirimleri, Kalıtım, Dinamik Nesne Yaratımı” gibi Nesneye Yönelik Programlama’nın temellerini oluşturan fikirleri ortaya atan bilim adamı. Oslo Üniversitesi’nde Nümerik Matematik eğitimi görmüştür.
  • Peter Chen : 1970’lerde “Entity-Relationship Model” fikrini ortaya atan Elektrik Mühendisi. Doktorasını Bilgisayar Bilimleri/Uygulamalı Matematik üzerine yapmıştır.
  • Barry Boehm : Yazılım geliştirmeye “Spiral model“ini kazandıran; “Süreç modelleme“, “Gereksinim Mühendisliği“, “Yazılım ölçütleri” gibi konularda katkıları olan Matematikçi.
  • Edgar Frank Codd :İlişkisel Veritabanı Modelleme” kavramını bilişim dünyasını kazandıran Matematikçi ve Kimyacı.
  • Friedrich Ludwig Bauer :İfade değerlendirme(Expression evaluation)’de yığıt metodu“nu ortaya koyan, “Zorunlu (Imperative) programlama dilleri ALGOL58 ve ALGOL60“ı geliştiren ekipte yer alan Matematikçi ve Teorik Fizikçi.
  • Alan Kay :Nesneye Yönelik Programlama“nın fikir babalarından, “Dynabook” ile günümüzde kullanılan laptop, tablet ve e-kitapların öncüsü ve “Grafik Kullanıcı Arabirimi“nin öncülerinden Biyolog ve Matematikçi.
  • Katherine Johnson :Hidden Figures” filminde hikayesi anlatılan, Apollo 13’ün aya olan seyahatinde Matematik hesaplamaları yapan ekipte de yer almış Matematikçi.
  • Margaret Hamilton : Aya giden “Apollo” projesinde yazılımcı olarak katkı vermiş bir diğer Matematikçi.
  • Charles Babbage : Günümüz bilgisayarlarının atası sayılan, ilk mekanik bilgisayarı yaratan Matematikçi, Filozof ve Mucit.
  • Ada Lovelace : Charles Babbage’ın yarattığı mekanik bilgilsayarın, salt hesaplamadan öte işlevler içerebileceği fikrini ortaya atan ve bu makinelerde işletilmek üzere ilk programların algoritmalarını yazan Matematikçi ve Yazar.

Burada sayılan isimlerin tamamına “Matematikçi” demem belki pek uygun kaçmadı, zira içlerinde akademik hayatını Matematik bilimi dışında çalışarak geçirmiş kişiler de mevcut, ancak demem o ki hepsinin Lisans ya da Yüksek Lisans seviyesinde ciddi bir Matematik eğitimi var.

Bugün yaptığınız işin temelleri ve bu işte kullanılan temel kavramlarının pek çoğu Matematikçiler tarafından ortaya atılmış iken sizlerin de Matematik’i özümsemeden bunların üzerine başarılı bir şey inşa etme şansınız pek yok.

Peki bu gerçek hayatta ne işime yarayacak?

Lisans ve Yüksek Lisans eğitimimi Elektrik-Elektronik Mühendisliği üzerine yaptığımdan dolayı Bilgisayar Mühendisliği/Programcılığı bölümünde verilen pek çok faydalı dersten mahrum kaldım ve bu alandaki eksiklerimi daha sonra, iş hayatında, hep kendi çabamla öğrenmek durumunda kaldım. Bilgisayar eğitiminde verilen “Mikroişlemciler“, “Sayısal Tasarım” ve “Veri yapıları & algoritmalar” derslerini almış olmam en büyük tesellim. Kişisel fikrim odur ki Bilgisayar Bilimlerinde öğretilen “Sayısal Tasarım“, “Mikroişlemciler“, “İşletim sistemleri“, “Derleyiciler” ve “Veri yapıları & algoritmalar” dersleri diğerlerinden bir adım önde ve çok önem arz eden dersler. Belki “Sayısal Tasarım“, “Mikroişlemciler“, “Derleyiciler” ve “İşletim sistemleri” derslerinde öğrendiğiniz konuları yazılım geliştirmede kullanmayacaksınız ancak yazılım geliştirdiğiniz platformu içeriği bilinmez kara bir kutu olmaktan çıkarıp tanımanıza yol açması açısından çok önemli buluyorum. Neyi neden yaptığını bilmeden, nasıl çalıştığını anlamadan yapmak çok tehlikeli; kişisel görüşüm de iyi bir yazılım geliştirme için bu bilgilerin ders değilse bile kültürel olarak kendinize katmanızın çok büyük faydası olacağı yönünde. Gelgelelim “Veri yapıları & algoritmalar” dersine. İşte bu ders kod yazabilen bir kişiyi basamak atlatıp, iyi bir yazılımcı olma yoluna sokabilecek en büyük artılardan biri. Kahvaltıda yenen yumurta gibi, güne iyi başlamanız için vazgeçilmez ve yerine başka bir şey konulamaz bir şey.

Önceden söyleyeyim ki burada gerekli olan Matematik bilgisi “asal sayı“nın ne olduğu değil. Basit bir Google araması ile de ulaşabileceğiniz bu bilgiyi, sizi zahmetten kurtararak ben burada vereyim.

Yalnız 1’e ve kendisine kalansız bölünebilen, 1’den büyük doğal sayılara asal sayı denir.

Şimdi bu bilgi ile yola çıkarsak asal sayı bulan bir metodu nasıl yazarız?

static Tuple<bool, long> Test1(long num)
{
    Stopwatch sw = new Stopwatch();
    sw.Start();
    if(num <= 1)
        return new Tuple<bool, long>(false, -1);
    bool isPrime = true;
    for (long i = 2; i < num; i++)
        if (num % i == 0)
            isPrime = false;
    sw.Stop();
    return new Tuple<bool, long>(isPrime, sw.ElapsedTicks);
}

Burada asal sayı hesabına ilaveten performans ölçümünü de işin içine kattığım için geriye her iki değeri de döndürdüğümü belirtmek isterim.

Bu kodu yazarken oldukça düz bir şekilde, işin içine hiçbir mantıksal incelik katmadan, 2’den sayının kendisine kadar, kendisi hariç, her sayının verilen sayıyı kalansız bölüp bölemediğini denetledim ve bölebilmesi durumunda da sayının asal olmadığına karar verdim. Kod doğru çalışıyor ancak daha verimli yazılabilir miydi?

num (bölen)sonuçkalan
250
331
422
520
614
713
812
911

İlk gördüğümüz şey, kalansız bölen tüm rakamların, sayının yarısına eşit ya da yarısından küçük olduğu; ki bu da çok mantıklı. Bir sayıyı yarısına böldüğümüzde (eğer çift sayı ise) 2 rakamını elde ederiz, ki kendinden başka, bundan daha küçük, kalansız bölünebileceği bir başka sayı da yoktur.

O zaman kodumuzu, döngümüz sayının kendisine değil, yarısına kadar gelecek şekilde güncelleyebiliriz.

static Tuple<bool, long> Test2(long num)
{
    bool isPrime = true;
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (long i = 2; i <= num / 2; i++)
    {
        if (num % i == 0)
            isPrime = false;
    }
    sw.Stop();
    return new Tuple<bool, long>(isPrime, sw.ElapsedTicks);
}

Şimdi, yukarıda yaptığımız incelemeyi bu sefer biraz daha büyük bir rakam, 100 için tekrarlayalım.

num (bölen)sonuçkalan
2500
4250
5200
10100
2050
2540
5020

Yukarıdaki sonucu bildiğim için, zaten döngüyü sayımız olan 100’ün yarısı olan 50’ye kadar işletmiştim. Fakat bölen ve sonuç rakamlarının incelediğimde ikisinin birbirinin tersten dizilmişi bir seri oluşturduğunu görmekteyim. Tekrarın başladığı nokta ise 10, yani 100’ün karekökü. Buradan da güzel bir ipucu elde ettim. Peki o zaman kodumuzu bu sefer girilen sayının kareköküne kadar dönecek şekilde yeniden uyarlayalım.

static Tuple<bool, long> Test3(long num)
{
    bool isPrime = true;
    Stopwatch sw = new Stopwatch();
    sw.Start();
    for (long i = 2; i <= Math.Ceiling(Math.Sqrt(num)); i++)
    {
        if (num % i == 0)
        isPrime = false;
    }
    sw.Stop();
    return new Tuple<bool, long>(isPrime, sw.ElapsedTicks);
}

Şimdi kodumuz önceki versiyonlarına göre çok daha az döngü ile aynı işlemi yapar hale geldi. Peki ne kadar performans kazandık? Bunu yazının sonunda göreceğiz.

Mantık sizi A noktasından B noktasına götürür, hayal gücü ise her yere.” — Albert EINSTEIN

Buraya kadar en temel Matematik bilgisi ile geldik. Peki bundan daha etkili bir yol yok mu? Elbette var, ve bunu bulmanın yolu ise yine Matematik’ten geçmekte (Üzgünüm, programlamada Matematik’ten kaçış yoktur!). Çok fazla detaya girmek istemesem de bu noktada “Eratosthenes Süzgeci“ne bir bakmakta fayda var. Milattan önce 5. yüzyılda yaşamış olan Matematikçi Eratosthenes, temeli tüm tamsayıları yazdıktan sonra en baştan başlayarak bir sayıyı ve varsa o sayının katlarını listeden çıkarıp bunu tekrarlı bir şekilde gerçekleştirdiğimizde asal tamsayıları elde edebileceğimiz bir yöntem önermiş. Buradan çok güzel bir çıkarım ile tüm asal sayıların (2 ve 3 hariç) 6k±1 şeklinde formülize edilebileceği ifade edilmiş. Hatta bu konu üzerinde Quora‘da çok güzel bir yazı dizisi de mevcut.

O zaman bir de kodumuzu bu şekilde yazalım.

static Tuple<bool, long> Test4(long num)
{
    bool isPrime = true;
    Stopwatch sw = new Stopwatch();
    sw.Start();
    if (num <= 1)
        isPrime = false;
    else if (num <= 3)
        isPrime = true;
    else if (num % 2 == 0 || num % 3 == 0)
        isPrime = false;
    long i = 5;
    while (i * i <= num)
    {
        if (num % i == 0 || num % (i + 2) == 0)
            isPrime = false;
        i += 6;
    }
    sw.Stop();
    return new Tuple<bool, long>(isPrime, sw.ElapsedTicks);
}

Şimdi, yazdığımız bu 4 ayrı metodu büyük bir asal sayı olan 2,147,483,647 ile test edip sonuçlarına bakalım.

İlk aklımıza gelen yöntemle hesaplama yaklaşık 57.5 milyon işlemci saat vuruşuna karşılık gelen bir süre gerektirmiş. Daha sonra yaptığımız şekilde, döngüyü yarıya indirerek yaklaşık 5 milyon işlemci saat vuruşu tasarruf ederek yaklaşık %11’lik bir performans artışı elde etmişiz.

Daha dikkatli bir inceleme ile döngüyü sayının karekökü kadar döndürmemizi yeterli olacağını bulmakla ise, elde ettiğimiz performans artışı %99.98’lik bir kazanç ile hesaplamadan çıktık.

6k±1 metodunu kullanmamız ise bize %99.99999’luk bir kazanç sağladı.

Tekrar başa dönersek, burada bilmemiz gereken şey Matematikte Asal sayılar konusu değil. Buradaki amacım kodu daha da hızlandırmak için gerekli döngü sayısını nasıl azaltabileceğim. Ki döngü hemen her programda mutlaka kullandığımız bir yapı.

İşte bu yüzden bir programcının Matematik olmazsa olmazı. Daha iyi, daha verimli, daha hızlı bir kod yazabilmenin yolu kodu Matematiksel bir gözle inceleyip, “nasıl daha etkin bir şekilde yazabilirim” sorusunun cevabını aramakta yatıyor.

Programlama bilginiz sizi A noktasından B noktasına götürebilir. Ama Matematik sizi her yere götürür! Hem de hızlı ve etkin bir şekilde.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir