Senin, 27 Oktober 2014

The Power of Data

بسم الله الرحمن الرحيم


Berbeda dengan TPB, sekarang yang namanya waktu UTS bener-bener ada. Maksudnya jelas bisa dibedakan dengan waktu kuliah biasa
Eh UTS lo kapan?
Masih minggu depan. Selo
Enak banget! Gue besok mulai UTS
Bandingkan dengan TPB yang tiap Sabtunya dipenuhi UTS. Sungguh malang nasibnya.. HAHAHAHA
Sebenernya gak juga sih. Semua baliknya ke manajemen waktu masing-masing

Jadi beberapa minggu ini saya sudah jarang sekali buka CF. Yah selain karena UTS dan kegiatan di kampus juga terhitung banyak, juga karena saya baru dapet mainan baru. Nih

tampilan awal kaggle.com


Kaggle.com. Situs ini mirip Topcoder tapi lebih fokus untuk hal-hal yang berkaitan dengan data mining. 
Apaan tuh data mining?

Sekilas buat yang belum tau, data mining itu bisa dibilang cara mengekstrak informasi dari data. Bisa dibilang data itu bahan mentahnya yang tidak semuanya bisa kita pakai, sedangkan informasi itu hasil matangnya yang bisa langsung dipakai.

Contoh rilnya, kita punya data tinggi badan semua mahasiswa ITB 2013. Data-data itu tidak mungkin kita pergunakan semuanya. Tapi kita bisa mencari tinggi badan rata-ratanya, sebaran tinggi badan, standar deviasi, bahkan hubungan tinggi badan dengan tingkat pendapatan keluarga jika kita punya data penghasilan orang tua juga. Kurang lebih itu yang data mining. Tertarik? Masih ada yang lebih seru. Akan saya tunjukkan salah satu "game" dalam situs ini. 

Pembaca pasti tidak asing dengan kapal Titanic. Jadi dalam "game" ini kita akan bermain-main seputar kapal Titanic dan penumpangnya. Kita diberikan sejumlah data orang yang ditemukan selamat dan tidak. Nama, asal, jenis kelamin, umur, jumlah anggota keluarga, nomor tiket, kelas kabin (kelas I, II, III), terminal keberangkatan, dan lain-lain yang akan kita butuhkan dalam induksi. Kita juga tahu orang tersebut selamat (hidup) atau ditemukan tewas. Lalu tugas kita sekarang adalah diberikan sisa data-data orang-orang yang hilang (persis seperti sebelumnya kecuali status selamat tidaknya), kita akan mencari tahu apakah orang tersebut kira-kira masih hidup atau tidak. Masih tidak tertarik?

Memang kebutuhan manusia akan informasi segitu besarnya apalagi di jaman sekarang. Hanya dengan informasi, kita bisa mengendalikan suatu negara. Bahkan orang yang punya resource melimpah sekalipun tidak bisa apa-apa jika dia tidak memiliki informasi cukup tentang resource yang dimiliki. Apalagi di era sekarang, ketika informasi banyak bertebaran. Kalau kita tidak update informasi, kita akan tertinggal jauh. 

Oiya, bidang data science ini juga bisa dibilang sepupunya machine learning dan artificial intelligence (AI). Karena itu saya tertarik untuk bermain disini heheh :)

Selain diisi problemset, dalam kaggle juga ada forum, tutorial, dan job. Bener-bener tempatnya orang-orang AI berkumpul. Jadi selain bisa mengasah skill kita, kita juga bisa mengikuti perkembangan terbaru AI dan bertukar ide di forum. Juga ada informasi tentang job dan internship bagi yang berminat. 

Sekian. Semoga bermanfaat :)

Senin, 18 Agustus 2014

Jadul

بسم الله الرحمن الرحيم

"Witing trisna jalaran saka kulina"
Kurang lebih artinya, "datangnya cinta berawal dari kebiasaan". Inilah pepatah Jawa yang sering sekali kita dengar dari orang tua, guru, masyarakat, media, atau bahkan buku-buku bacaan. Tapi jangan remehkan pepatah ini. Bisa jadi pepatah ini ada benarnya.

Kalau kita tidak suka mengajar, bisa jadi karena kita tidak pernah merasakan asiknya mengajar anak-anak.

Kalau kita tidak suka berbuat baik pada orang, bisa jadi memang kita tidak pernah berbuat baik pada orang yang kita temui.

Begitupun kalau kita merasa malas shalat di masjid ataupun membaca Qur'an. Mungkin selama ini kita tidak pernah meluangkan waktu untuk shalat jamaah maupun tilawah. Mungkin kita terlalu sibuk mengurus kuliah/sekolah, pekerjaan, unit/ekskul, organisasi, dan sebajek aktivitas lainnya.

Mulailah membiasakan diri. Pasang target rutin yang harus dilakukan tiap hari. Sedikit dulu saja. Kalau sudah konsisten baru tambah target lagi. Begitu terus sampai kita bisa semua menikmati kegiatan-kegiatan positif itu.

Begitupun ... err ... tentang memilih jodoh #eaa. Mereka yang langgeng sampai akhir hayatnya bukan cuma karena memang mereka jodoh - walaupun sebenarnya iya itu takdir. Tapi mereka menikmati proses berkeluarga karena memang mereka sudah terbiasa. Witing trisna jalaran saka kulina. Mereka memilih, lalu meyakini bahwa itu jodoh terbaik mereka dan teguh dalam komitmen mereka.

Ya, menjaga komitmen. 

Selasa, 15 Juli 2014

About Fibonacci : Identitas Fibonacci - part 4

بسم الله الرحمن الرحيم

Setelah sekian lama tidak berjumpa dengan pembaca setia -kalau ada-, kali ini saya menemukan persoalan menarik tentang fibonacci. Saya tidak akan bahas persoalan sebenarnya (yang mana mungkin jauh lebih susah dari topik yang akan ditulis) tapi akan saya fokuskan ke identitas barisan Fibonacci yang lain. Selamat membaca :)

Review

Di pos sebelumnya tentang fibonacci kita sudah dapat menyatakan fibonacci dalam matriks berpangkat, yaitu

\[ \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array}\right) = \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array}\right)^n \]

Coba jika kita pecah menjadi dua buah matriks berpangkat

\[ \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array}\right)^n = \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array}\right)^k \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array}\right)^{n-k} \]
\[ \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array}\right) = \left( \begin{array}{cc} F_{k+1} & F_k \\ F_k & F_{k-1} \end{array}\right) \left( \begin{array}{cc} F_{n-k+1} & F_{n-k} \\ F_{n-k} & F_{n-k-1} \end{array}\right) \]

Perhatikan bahwa
\[ F_n = F_{k+1} F_{n-k} + F_k F_{n-k-1}\]
atau dalam bentuk lain
\[ F_{m+n} = F_{n+1} F_m + F_n F_{m-1}\]
muncul ketika kita menyamakan perkalian baris pertama dan kolom kedua. Inilah yang akan saya sebut sebagai magic identity karena sangat useful untuk penyelesaian parsial fibonacci.

Aplikasi pada dynamic range query

Sebagai contoh, misalkan kita pada suatu range \( [L \dots R] \) ingin menambah semua array dalam range itu dengan \(F_1\), \(F_2\), \(F_3\), dan seterusnya sampai \(F_{R-L+1}\) lalu mencari jumlah semua elemen dalam range tersebut. Dengan bruteforce hal ini mungkin dilakukan, tapi akan sangat memakan waktu jika proses tersebut dilakukan berulang-ulang.

Maka kita hanya perlu menambah parameter update \(F_1\), \(F_2\), dan \(Sum\). Dengan ketiga parameter tersebut kita bisa menggunakan magic identity yang telah dibuktikan di atas dengan sedikit memodifikasinya menjadi
\[ S_n = F_{k+1} S_{n-k} + F_k S_{n-k-1}\]
dengan \(k = 1\), menjadi
\[ S_n = F_2 S_{n-1} + F_1 S_{n-2} \]
untuk mengupdate \(Sum\) yang telah di precompute sebelumnya. Presum dari Fibonacci \(S_n\) bisa di precompute di awal. Sehingga update dan pencarian nilai bisa dilakukan dengan cepat.

Hal ini akan sangat terasa ketika berhadapan dengan soal dynamic query yang menggunakan Fibonacci sebagai update query nya. Sebagai contoh, soal yang baru saja saya solve menggunakan struktur data segment tree dan magic identity diatas yaitu Codeforces Round #FF Div2 E.

Back-Fibonacci  dan d'Ocagne's identity

Sejauh ini kita telah membahas bilangan Fibonacci berindeks non-negatif. Kita tidak memasukkan indeks negatif karena definisi yang dipakai adalah persamaan yang tersusun menaik
\[ F_n = F_{n-2} + F_{n-1} \]
Bagaimana jika kita ubah definisinya menjadi
\[ F_n = F_{n+2} - F_{n+1} \]
Maka, akan ada definisi untuk Fibonacci berindeks negatif kan? Lalu untuk apa kita harus mencari bilangan Fibonacci berindeks negatif?

Coba perhatikan barisan berikut
\( 0, 1, 1, 2, 3, 5, 8, 11, \dots \)
Barisan di atas tidak akan berakhir. Jika kita susun barisan mundur, juga akan didapati barisan tiada berakhir
\( \dots, 13, -8, 5, -3, 2, -1, 1, 0, 1, 1, 2, 3, 5, 8, 13, \dots \)

Perhatikan bahwa akan didapat hubungan antara bilangan berindeks negatif dengan bilangan berindeks positifnya, yaitu
\[ F_{-n} = (-1)^{n+1} F_n \]

Pembuktiannya sederhana, bisa dengan induksi atau dengan yang lain ( coba pembaca buktikan sendiri :) ).

Lalu, setelah ini apa?
Kita akan menemukan bahwa identitas negativitas diatas bersama-sama magic identity berpotensi menimbulkan identitas baru, yang ditemukan oleh d'Ocagne. Coba kita turunkan persamaan ini.

Kita telah mengetahui bahwa bilangan Fibonacci dapat dinyatakan dalam jumlah dari 2 perkalian Fibonacci
\[ F_{m+n} = F_{n+1} F_m + F_n F_{m-1}\]
Jika kita asumsikan \(m < 0\), maka kita subtitusikan identitas negativitas menjadi
\begin{array}{lcl}
 F_{n-m} & = & F_{n+1} F_{-m} + F_n F_{-m-1}
\\ & = & F_{n+1} (-1)^{m-1} F_m + F_n (-1)^{m+2} F_{m+1}
\\ & = & - (-1)^m F_{n+1} F_m + (-1)^m F_n F_{m+1}
\end{array}
Kalikan kedua ruas dengan \((-1)^m\) menjadi
\[ (-1)^m F_{n-m} = F_n F_{m+1} - F_{n+1} F_m \]

Inilah identitas d'Ocagne yang tidak kalah serunya dengan magic identity. Jika pada segment tree kita cukup memerlukan magic identity, maka pada binary indexed tree(BIT) kita memerlukan identitas yang bisa mengupdate sum dengan lebih fleksibel ( Ini karena yang dilakukan BIT yaitu 'hanya' mengupdate presum dari query. Kita akan bahas kedua struktur data ini lebih lanjut nanti ;D )

Sum of Fibonacci

Banyak problem yang mensyaratkan penjumlahan elemen-elemen Fibonacci dalam inti soalnya. Dan kadang kita tidak dapat menggunakan precompute untuk menghitungnya entah karena terlalu banyak atau batasan memori maupun runtime yang memaksa kita menggunakan cara yang lebih canggih.

Sebenarnya percaya atau tidak penjumlahan dari elemen-elemen Fibonacci itu adalah elemen Fibonacci lain. Percaya nggak?
Kali ini akan saya buktikan sedikit tentang Fibonacci summation yang sebenarnya sangat sederhana.

Perhatikan bahwa \( F_n = F_{n+2} - F_{n+1} \) maka jika kita jumlahkan \(F_1\) sampai \(F_n\) dengan menyubtitusikan persamaan ini, akan didapatkan \begin{array}{lcl} \sum_{i=1}^{n} F_i & = & F_1 + F_2 + F_3 + \dots + F_n \\ & = & (F_3 - F_2) + (F_4 - F_3) + \cdots + (F_{n+2} - F_{n+1}) \\ & = & F_{n+2} - F_2 \end{array}

Simple but cool, right?
Trik ini juga bisa diaplikasikan pada soal serupa dengan yang disebut di atas. Tanpa melakukan precompute kita bisa menggunakan formula ini sebagai pengganti. Lumayan menghemat runtime processing.

Penutup

Sebenarnya ada banyak identitas Fibonacci yang bisa didapatkan dengan berbagai cara (yang tentunya pembuktian lengkapnya akan diserahkan pada pembaca), diantaranya :
  • Catalan identity : \( F_n^2 - F_{n+r}F_{n-r} = (-1)^{n-r}F_r^2 \) ( Hint : bukti menggunakan pemangkatan matriks dan determinan )
  • Cassini identity : \( F_n^2 - F_{n+1}F_{n-1} = (-1)^{n-1} \) ( Kasus khusus dari Catalan identity )
dan lain sebagainya yang mungkin terlalu banyak jika diuraikan disini.

Sekian bahasan tentang identitas Fibonacci, mungkin juga bahasan terakhir tentang bilangan Fibonacci. Jika ada kekurangan mohon dimaklumi dan dikoreksi sebagai pelajaran berharga bagi saya. Terima kasih :)

Catatan :
Saya terinspirasi untuk membahas ini setelah melihat berbagai variasi jawaban untuk soal Codeforces yang di pos diatas. Karena saya anggap soal ini unik dan bisa disolve dengan banyak theorem dan struktur data, maka saya memutuskan menulis tentang ini.

Minggu, 06 April 2014

Ini Gadgetku, Mana Punyamu

بسم الله الرحمن الرحيم

Masih ingat jelas sebelum aku tidak memegang satupun gadget. 
Merasakan bermain dalam arti yang sebenarnya. 
Bersepeda bareng, ngobrol di rental, di rumah teman sudah biasa. 
Kadang diselingi "game" ringan macam delikan (petak umpet), nekeran (kelereng), congklak, juga banyak lainnya.
Kadang olahraga sepak bola, renang, tiduran (loh ?), dan lainnya.


Sekarang keliatannya langka.
Tiap naik angkutan umum yang dilihat di angkot kebanyakan hape, atau tab.
Sesekali menengok ke luar melihat apa sudah sampai tujuan.
Bahkan pernah ketika seorang penjual koran menjual koran, seorang pembeli lupa membayar dan sibuk dengan.. 
yah sesuatu yang saya tidak tahu.
Mungkin urusan profesional yang urgent
Mungkin sanak saudara yang ditimpa musibah
Dan mungkin yang lainnya yang tidak bisa disebutkan karena ketidaktahuan kita

Jadi salah siapa? Atau haruskah disebut salah apanya?

Sabtu, 08 Maret 2014

About Fibonacci : Divide and Conquer Approach; Matrix Exponentiation - part 3

بسم الله الرحمن الرحيم

Telah dibahas di post sebelumnya cara untuk menghitung bilangan Fibonacci dengan menggunakan Dynamic Programming aka DP. Sekarang kita menghadapi masalah lain yang lebih sulit. Jika kita diminta menentukan bilangan Fibonacci ke-20000000000 maka akan memakan waktu cukup lama jika kita menggunakan DP.

Maka ada pendekatan yang cukup menarik untuk menyelesaikan masalah ini, yaitu Divide and Conquer (DnC). Sekilas tentang DnC, intinya ada 3 tahap utama :
  • Divide : membagi problem menjadi beberapa subproblem yang relatif sama (idealnya sama)
  • Conquer : menyelesaikan tiap bagian subproblem secara independen
  • Combine : menggabungkan solusi tiap subproblem menjadi solusi yang dicari

Untuk detil lebih jelas tentang DnC silahkan mencari sendiri di internet. Ada banyak contoh-contoh aplikasi DnC seperti quicksort, mergesort, binary search, dsb. Beberapa halaman yang disarankan :

Fibonacci Matrix Form


Misal kita punya dua matrix yang dikalikan sebagai berikut

\[ \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right) \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array} \right) \] maka hasilnya akan menjadi \[ \left( \begin{array}{cc} F_{n+1}+F_n & F_{n-1}+F_n \\ F_{n+1} & F_n \end{array} \right) = \left( \begin{array}{cc} F_{n+2} & F_{n+1} \\ F_{n+1} & F_n \end{array} \right) \]

sesuai identitas Fibonacci yang telah dibahas di post sebelumnya. Coba perhatikan juga bahwa persamaan matrix di atas memberi kesan bahwa DP Fibonacci juga bisa dilakukan secara perkalian matrix\[  \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right) \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array} \right) = \left( \begin{array}{cc} F_{n+2} & F_{n+1} \\ F_{n+1} & F_n \end{array} \right) \]
Lalu apa basisnya?

Ketika n=1, maka \(F_n=1\),  \(F_{n-1}=0\), dan  \(F_{n+1}=1\). Sehingga matrixnya menjadi \( \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right) \)
yang ternyata sama dengan matrix pengali yang telah digunakan sebelumnya. Maka kita bisa definisikan \[ P_n = \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array} \right) \]
dan
\[ A = \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right) \]
sehingga
\[ \begin{eqnarray*} P_n & = & A.P_{n-1} \\ & = & A^n \\ \left( \begin{array}{cc} F_{n+1} & F_n \\ F_n & F_{n-1} \end{array} \right) & = & \left( \begin{array}{cc} 1 & 1 \\ 1 & 0 \end{array} \right)^n \end{eqnarray*} \]

Divide and Conquer Matrix Exponential


Sampai sini kita telah menurunkan persamaan matrix Fibonacci dengan pemangkatan matrix. Lalu kenapa kita harus mengubah bentuk DP perkalian matrix yang sebelumnya telah kita peroleh menjadi pemangkatan matrix?
Kita telah bahas sebelumnya kita ingin mencari cara yang lebih cepat dari DP untuk mencari bilangan Fibonacci. Jika kita menggunakan perkalian matrix, kita masih bergelut dengan kompleksitas yang sama (malah cenderung lebih lambat karena mengalikan elemen-elemen matrix dibutuhkan beberapa kali operasi).

Maka tujuan kita sebenarnya adalah mengubah bentuk DP menjadi bentuk DnC yang bisa dipecah menjadi dua bagian matrix yang relatif sama. Prinsipnya sama seperti pemangkatan bilangan dengan DnC. Dengan DnC kita bisa dapatkan :
\[ P_n = A^n = \left\{ \begin{array}{ll} \left( P_{n/2} \right)^2 & \mbox{,n genap} \\ A \left( P_{ \left\lfloor{ n/2 }\right\rfloor } \right)^2 & \mbox{,n ganjil} \end{array} \right. \]
Dan kompleksitas yang diharapkan dari algoritma ini
\[ T(n) = \left\{ \begin{array}{ll} 1 & \mbox{,n=1} \\ T \left( \left\lfloor{ \frac{n}{2} }\right\rfloor \right) +1 & \mbox{,n>1} \end{array} \right. \]
yang memiliki penyelesaian \(O(log N)\). Dengan kata lain algoritma ini tumbuh sebanding dengan logaritma dari N yang mana sangat lambat sehingga sangat efisien untuk masukan yang besar (jika belum mengerti tentang kompleksitas silahkan lihat halaman Ristek Fasilkom).

Implementasi yang saya buat kurang lebih seperti berikut :
long long fibonacci(int n)
{
    long long fib[2][2]= {{1,1},{1,0}}, //matrix A
      ret[2][2]= {{1,0},{0,1}}, //matrix result
             //initialized with identity matrix
      tmp[2][2]= {{0,0},{0,0}}; //temporary matrix
      
    int i,j,k;
    
    while(n)
    {
     // if n odd multiply [ret] with [A]
        if (n&1) {
            memset(tmp,0,sizeof tmp);
            for(i=0; i<2; i++) 
             for(j=0; j<2; j++) 
              for(k=0; k<2; k++)
                        tmp[i][j]=(tmp[i][j]+ret[i][k]*fib[k][j]);
            for(i=0; i<2; i++) 
             for(j=0; j<2; j++) 
              ret[i][j]=tmp[i][j];
        }
        
        memset(tmp,0,sizeof tmp);  // clear tmp
        
        // multiply [ret] with [ret]
        for(i=0; i<2; i++) 
         for(j=0; j<2; j++) 
          for(k=0; k<2; k++)
                    tmp[i][j]=(tmp[i][j]+fib[i][k]*fib[k][j]);
        for(i=0; i<2; i++) 
         for(j=0; j<2; j++) 
          fib[i][j]=tmp[i][j];
          
        n/=2;
    }
    
    return (ret[0][1]);
}

Bila ada kekurangan dimohon kritik dan sarannya untuk perbaikan kita bersama. Terimakasih.

Kamis, 27 Februari 2014

About Fibonacci : Fungsi Rekursif dan Dynamic Programming Sederhana - part 2

بسم الله الرحمن الرحيم

Di posting sebelumnya telah dibahas identitas penting dari barisan Fibonacci, yaitu relasi rekurens yang biasa dikenal \[F_{n}=F_{n-1}+F_{n-2}\]

Kita akan membahas lebih lanjut relasi ini dan menentukan cara untuk menghitung barisan Fibonacci ini.

Bagi yang belum tahu tentang Fibonacci silahkan lihat post sebelumnya: About Fibonacci : Menghitung Kelinci - part 1

Relasi Rekurens

Sekilas relasi rekurens intinya adalah menggunakan fungsi yang sama dalam pemanggilan fungsi, seperti \(L_{n}=L_{n-1}+2n-1\). Terlihat bahwa di dalam fungsi \(L_{n}\) ada fungsi \(L_{n-1}\). Dengan kata lain nilai fungsi parameter N bergantung pada fungsi yang sama dengan parameter N-1. Dan juga relasi rekursif berguna mengubah problem yang besar menjadi problem yang lebih kecil. Fibonacci ke-N menjadi Fibonacci ke-(N-1) dan ke-(N-2). Kurang lebih seperti itu cara kerja fungsi rekursif.

Dalam pendefinisian fungsi rekursif ada 2 hal yang harus diperhatikan : basis dan rekurens.
  • Basis bisa dikatakan kondisi awal dari fungsi rekurens. Ketika fungsi telah punya nilai di suatu parameter tertentu, itulah yang disebut basis. Basis bisa lebih dari satu nilai, bisa hanya memiliki satu nilai. Intinya basis ini yang jadi titik berhentinya pemanggilan fungsi.
  • Rekurens adalah pemanggilan fungsi yang menuju basis dengan memanggil dirinya sendiri. Ketika nilai fungsi belum ada, maka kita gunakan rekurens untuk memanggil fungsi serupa yang menuju basis. Jika masih belum di basis, pemanggilan rekurens akan terus dilakukan sampai bertemu basis.
Contoh pada suku ke-N Fibonacci yaitu F(n) = F(n-1) + F(n-2). Bagian ini dinamakan rekurens fungsi.
Sedangkan untuk menentukan basis kita lihat bahwa fungsi di atas berhenti saat F(3) = F(2) + F(1), karena tidak mungkin dilakukan pemanggilan F(1) = F(0) + F(-1) karena bilangan Fibonacci terkecil yaitu bilangan ke-1 yaitu 1 (ada juga yang mendefinisikan bilangan Fibonacci dimulai Fibonacci ke-0 yaitu 0). Maka kita ambil basisnya F(1) = 1 dan F(2) = 1. (atau F(1) = 1 dan F(0) = 0 jika menggunakan definisi Fibonacci ke-0).

Dengan memanfaatkan definisi rekursif ini kita bisa implementasikan Fibonacci ke-N dengan fungsi rekursif seperti berikut.

function fibonacci (n : integer) : integer;
begin
   if n<3 then
      fibonacci := 1           { basis : suku pertama dan kedua bernilai 1 }
   else
      fibonacci := fibonacci(n-1)+fibonacci(n-2); { rekurens }
end;

Tapi ada kelemahan dalam pemanggilan fungsi seperti ini. Coba perhatikan skema pemanggilan fungsi di bawah!

Setiap pemanggilan F(n) akan memanggil F(n-1) dan F(n-2) meskipun F(n-2) juga telah dipanggil pada penghitungan F(n-1). Gambar di atas menunjukkan dalam pemanggilan F(5) saja F(3) dipanggil berulang-ulang, yaitu 2 kali. Begitupun dengan F(1) dan F(2). Coba bayangkan jika F(500) dipanggil! Maka F(1) dan F(2) akan dipanggil ribuan kali. Betapa tidak efektifnya program kita!!

Maka kita bisa menggunakan alternatif lain yaitu dengan memakai teknik Dynamic Programming.

Dynamic Programming (DP)

Kelemahan kita menggunakan rekursif biasa yang baru saja dibahas yaitu terjadinya overlapping subproblem, yaitu subproblem yang telah dihitung dicari lagi sehingga pemanggilan fungsi menjadi sangat boros. Dengan DP kelemahan rekursif yang satu ini bisa tercover.

Inti dari DP adalah mengubah parameter menjadi state fungsi dan menyimpan setiap state yang dilalui fungsi dalam suatu struktur data. Sehingga sekali kita telah menghitungnya kita tidak perlu mengulanginya lagi.

Dalam membuat konstruksi DP kita dapat memakai 2 cara : top-down dan bottom-up.

1. DP Top-Down
Style yang satu ini cukup mudah. Dengan menggunakan fungsi rekursif yang sebelumnya dan sedikit modifikasi maka akan terbentuk DP top-down.

Jika kita lihat, kelemahan rekursi yang sebelumnya hanyalah pada kurangnya penyimpanan state sehingga menyebabkan overlap. Maka sekarang kita akan menyimpan nilai semua state F(n) pada suatu variabel (misal array) yang kita namakan variabel memo sehingga kita tidak perlu menghitung berulang-ulang. Metode ini disebut memoization/memoisasi.

Coba perhatikan ilustrasi di bawah!



Dengan menyimpan nilai state nilai F(3) bisa dimanfaatkan langsung tanpa menghitung lagi dari F(2) dan F(1). Kurang lebih implementasinya DP top-down seperti berikut.

var fibo : array[1..1000] of integer;   { variabel memo }
{ anggap array fibo telah diinisiasi nilai 0 }

function fibonacci (n : integer) : integer; { fungsi rekursif }
begin
   if n<3 then 
      fibonacci := fibo[n] := 1
   else if fibo[n]=0 then
           fibonacci := fibo[n] := fibonacci(n-1)+fibonacci(n-2)
        else
           fibonacci := fibo[n];
end;

2. DP Bottom-Up
Jika DP top-down membuat solusi dengan fungsi rekursif (yang cenderung mengecil ke basis), maka DP bottom-up membuat solusi dari basis lalu membesar ke state berikutnya sampai batas yang diinginkan. Bisa dikatakan style ini kebalikan dari style sebelumnya.

Coba perhatikan ilustrasi berikut!


Dari gambar terlihat bahwa relasi rekursif F(n) = F(n-1) + F(n-2) digunakan untuk membangun solusi F(3), F(4), dan seterusnya hingga F(8). Dimulai dari basis F(1) dan F(2), solusi ini membangun solusi hingga F(n) yang diinginkan.

Implementasinya cukup dengan iterasi biasa untuk mengisi F(3) sampai F(n) sehingga masih cukup mudah dibuat. Dan karena implementasinya bebas rekursi maka untuk Fibonacci dengan urutan yang sangat besar (contoh: F(2000,000,000)) tidak akan mengalami stack overflow ketika pemanggilannya seperti terjadi pada fungsi rekursi. Inilah keunggulan utama DP bottom-up dibanding top-down. Meskipun kadang implementasi bottom-up umumnya lebih susah dari top-down karena dimulai dari basis dan pemilihan rekurens yang harus ekstra hati-hati (karena bisa jadi tidak semua parameter memo digunakan dalam DP). Pengalaman saya sendiri saat ICPC kemarin tim saya harus bergelut selama hampir sejam untuk mendebug solusi DP bottom-up karena ada bagian yang harusnya tidak dimasukkan rekurens ternyata belum dikecualikan.

Kurang lebih implementasinya seperti berikut.

var fibo : array[1..1000] of integer;   { variabel memo }

begin {program utama}
   fibo[1]:=1;    { inisiasi basis }
   fibo[2]:=1;
   for i:=3 to n do   { pengisian memo dengan iterasi }
       fibo[i]:=fibo[i-1]+fibo[i-2];
   { output fibo[n] jika perlu }
end.

Dan perlu diketahui untuk kedua solusi DP di atas memiliki kompleksitas \(O(N)\), yaitu linear terhadap nilai N. Sehingga untuk N < 100 juta, runtime yang dibutuhkan masih sekitar 1 detik atau kurang.

Untuk N yang sangat besar kita tidak dapat memakai solusi ini karena pasti akan mendapat verdict TLE karena runtime yang dibutuhkan bisa 100 detik atau malah jauh lebih besar. Maka ada cara yang lebih baik menggunakan teknik Divide and Conquer dengan perkalian matriks.
Tunggu di post berikutnya : 
About Fibonacci : Divide and Conquer Approach; Matrix Exponentiation- part 3

Selasa, 25 Februari 2014

About Fibonacci : Menghitung Kelinci - part 1

بسم الله الرحمن الرحيم

Kita semua pasti sudah tau apa itu barisan Fibonacci. Yaitu barisan yang sukunya adalah jumlah 2 suku sebelumnya. Tapi kenapa bisa gitu dan gimana problem ini muncul ? Coba simak narasi di bawah.


Misal kita beli seekor anak kelinci. Anak kelinci itu akan dewasa setelah setahun. Lalu pada tahun berikutnya kelinci dewasa tadi akan melahirkan seekor anak kelinci. Tiap tahun si kelinci dewasa akan melahirkan anak terus-menerus. Dan si anak kelinci ini akan mengikuti pola yang sama dengan induknya. Dewasa setelah setahun lahir, lalu melahirkan seekor anak tiap tahun -dengan asumsi kelinci tidak akan mati dan beranak tanpa pasangan selamanya hihi







Jika kita lihat polanya, anak kelinci pada tiap tahun :

Pada tahun pertama, 1 ekor (anak kelinci yang baru dibeli)
Pada tahun kedua, 1 ekor (anak kelinci sudah dewasa)
Pada tahun ketiga, 2 ekor (kelinci dewasa melahirkan seekor anak)
Pada tahun keempat, 3 ekor (kelinci dewasa melahirkan seekor anak dan si anak kelinci yang tadi sudah dewasa)
dan seterusnya yang dapat kita simpulkan menjadi

1, 1, 2, 3, 5, 8, 13, 21 ...

Ternyata pola yang didapat yaitu jumlah kelinci pada tahun ini adalah kelinci pada tahun lalu ditambah kelinci dua tahun lalu. Kok bisa?

Coba diingat lagi, kelinci bisa melahirkan ketika dia sudah berumur minimal 2 tahun.
Maka jumlah kelinci dewasa saat ini sejumlah kelinci dewasa tahun lalu plus kelinci yang dilahirkan tahun lalu yang baru saja dewasa.
\[D_{n} = D_{n-1} + A_{n-1} = F_{n-1}\]
Lalu jumlah kelinci muda sekarang sejumlah kelinci dewasa dua tahun lalu dan jumlah kelinci yang baru dewasa tahun lalu/kelinci muda dua tahun lalu. 
\[A_{n} = D_{n-2} + A_{n-2} = F_{n-2}\]
Maka jumlah kelinci sekarang = jumlah kelinci muda sekarang + jumlah kelinci dewasa sekarang.
\[F_{n}=D_{n} + A_{n}= F_{n-1} + F_{n-2}\]

Pembuktian sudah beres. Sekarang kita coba terapkan pola ini ke salah satu soal OSK 2013 (bidang informatika) yang telah dimodif sedemikian rupa. Check this out!!




Sebuah katak berada pada salah satu sisi sungai ingin menyeberangi sungai dengan melewati 5 buah batu yang berada di sepanjang aliran sungai (lihat gambar di bawah). Katak ingin menghitung berapa kemungkinan cara yang dia gunakan untuk menyeberangi sungai. Karena katak jago melompat, katak mampu melompat dari batu satu ke batu sebelahnya, atau melompati satu batu tersebut dan mendarat di sampingnya lagi. Coba bantu katak menghitung berapa kemungkinan langkah yang diambil untuk bisa ke ujung satunya!



Jawab :

Coba kita lihat jumlah cara yang bisa diambil untuk mendarat di batu ke-n

Pada batu ke-1 : hanya 1 cara
Pada batu ke-2 : karena bisa dari batu pertama atau dari ujung, maka jumlah cara ada 1+1=2 cara
Pada batu ke-3 : idem di atas, 2+1=3 ( coba cari tahu kenapa 2+1 :) )
Pada batu ke-4 : ada 3+2=5 cara
Pada batu ke-5 : ada 3+5=8 cara
Dan ke ujung kanan ada 8+5=13 cara.

Simpel kan :). Meski begitu banyak masalah dalam bidang ilmu komputer yang melibatkan perhitungan bilangan Fibonacci. Misal, dalam perhitungan kompleksitas algoritma Euclid dalam mencari GCD (Greatest Common Divisor), pembentukan struktur data Fibonacci, dan beberapa masalah komputasi lainnya.

Perlu diketahui meskipun barisan Fibonacci termasuk barisan yang tidak tentu (karena tergantung suku-suku sebelumnya), tapi rasio \(\frac{F_{n+1}}{F_{n}}\) akan mendekati suatu nilai yang disebut Phi / Golden Ratio. Dengan menggunakan Golden Ratio ini kita dapat menghitung barisan Fibonacci secara langsung tanpa harus tahu suku-suku sebelumnya. Tapi kita tidak akan menggunakan ini karena dalam pemrograman perhitungan nilai real (bilangan non bulat) rawan menghasilkan nilai yang salah dalam pembulatan. Dan juga pemrosesan bilangan real cenderung lebih lambat dibanding bilangan bulat.

Lalu bagaimana cara menghitung Fibonacci dengan cepat? Tunggu postingan berikutnya :
About Fibonacci : Fungsi Rekursif dan Dynamic Programming Sederhana - part 2

Sabtu, 15 Februari 2014

Barbershop, Antara Masyarakat Kritis dan Mahasiswa Apatis

بسم الله الرحمن الرحيم

Sebenarnya ini tejadi ketika liburan belum berakhir dan saya masih di kota buaya. Berawal ketika rambut shaggy ku semakin tidak karuan. Ibu saya yang notabene perfeksionis -kayaknya hampir semua ibu gitu- memaksa saya untuk potong rambut. Maka saya pun melangkah bersama adik saya yang agak besar ke barber shop langganan saya.

Seperti biasa disana benar-benar ramai. Bukan hanya karena yang datang banyak dan hanya dilayani dua orang. Tapi memang banyak hal yang jadi perbincangan. Terutama karena menjelang pemilu April besok, banyak opini tentang politik bertaburan di ruangan itu.

Ada yang bilang percuma milih caleg itu. Buat kampanye aja udah sekian miliar. Kalau terpilih apa ndak ingin balik bondo (Jawa; arti : balik modal).

Ada lagi kalau salah satu gubernur DKI yang sekarang sedang populer itu kerjanya cuma pencitraan. Tiap blusukan mesti ada wartawan. Tapi itu dibantah orang yang bilang kalau itu ancaman untuk pihak-pihak yang tidak ingin berbenah dan cuma makan gaji buta.

Intinya kepekaan masyarakat masih terasa di ruangan itu. Di beberapa tempat lain seperti warung kopi, warung nasi, dan pangkalan ojek atau becak sering saya juga mendengar perbincangan serupa. Membuktikan bahwa masyarakat kita masih kritis dalam melihat realita yang terjadi.

Jujur, saya melihat bahwa sebagai mahasiswa -orang yang harusnya lebih terpelajar- kita kurang mengamati dan berdiskusi. Wawasan kita harusnya lebih terbuka karena kita telah belajar lebih banyak dari mereka. Dan tanggung jawab kita untuk peka dan lebih responsif terhadap kebutuhan masyarakat.

Jadi masih mau tidak peduli?