Tag: Snippet

Catatan Sibudi

  • Memeriksa Bilangan Palindrom

    Palindrom adalah kata, rangkaian kata, atau bilangan yang terbaca sama, baik dari depan maupun dari belakang. Beberapa contoh palindrom adalah radar, kasur rusak, 1001.

    Pada bahasa pemrograman Java kita dapat dengan mudah memastikan bahwa suatu string adalah palindrom atau bukan.

    boolean isPalindrom (String str) {
      
      return str.equals(new StringBuffer().append(str).reverse().toString());
    }
    

    Kode di atas mengembalikan nilai true jika argumennya adalah palindrom. Namun untuk memeriksa palindrom dari argumen berupa bilangan, kita membutuhkan type casting. Type casting berguna untuk mengkonversi suatu tipe data ke tipe data yang lain. Dalam kasus ini kita membutuhkan konversi dari tipe data int ke String. Caranya mudah saja, tinggal tambahkan saja bilangan tersebut dengan sebuah string kosong.

    boolean isPalindrom (int bilangan) {
      
      String str = "" + bilangan;
      return str.equals(new StringBuffer().append(str).reverse().toString());
    }
    

    Dengan kode di atas kita bisa memeriksa apakah sebuah bilangan termasuk palindrom atau bukan.

    Untuk Anda yang malas menghafal kode yang panjang di atas, kita bisa menggunakan pemeriksaan palindrom secara manual. Pada dasarnya algoritma palindrom yaitu membandingkan karakter pertama dengan karakter terakhir, lalu karakter kedua dengan karakter kedua dari belakang, dan seterusnya. Jika jumlah karakternya ganjil, misalnya “radar” (5 karakter), karakter yang berada tepat di tengah-tengah, yaitu karakter ketiga (“d”) tidak perlu diperiksa.

    boolean isPalindrom (int bilangan) {
    
      String str = "" + bilangan;    // konversi dari int ke Str
      char [] c = str.toCharArray(); // konversi dari String ke array char
      int i1 = 0,        // indeks pertama
      i2 = c.length - 1; // indeks terakhir
    
      while (i2 > i1) {
    
        if (c[i1 ++] != c[i2 --])
          return false;
      }
    
      return true;
    }
    

    Pada baris keempat kita konversikan tipe data String ke bentuk dasarnya yaitu sekumpulan karakter dengan method toCharArray(). Pada baris kesepuluh, kita akan membandingkan sekaligus menambah indeks i1 dan mengurangi indeks i2. Perhatikan bahwa operator unary ditulis setelah variable berarti bahwa pembadingan dilakukan terlebih dahulu kemudian baru menambah/mengurangi indeksnya.

  • Bagaimana Mencari Bilangan Prima?

    Salah satu pertanyaan paling penting dalam dunia Matematika adalah menentukan apakah suatu angka merupakan bilangan prima atau tidak. Topik bilangan prima ini juga menjadi salah satu dari tujuh masalah utama dalam bidang Matematika (Millennium Prize Problems), yaitu Riemann hypothesis. Orang pertama yang sanggup memecahkan salah satunya akan mendapat hadiah US$1,000,000 dari Clay Mathematics Institute. Sampai sekarang masih tersisa enam soal yang belum terpecahkan termasuk Riemann hypothesis yang bila dapat dipecahkan dapat menguak pola distribusi bilangan prima.

    Bilangan prima adalah bilangan asli yang lebih besar dari 1 dan hanya memiliki dua faktor (pembagi) yaitu 1 dan bilangan itu sendiri. Contoh bilangan prima adalah 7, yang hanya memiliki dua faktor yaitu 1 dan 7. Sebaliknya, 6 bukan merupakan bilangan prima karena memiliki faktor-faktor 1, 2, 3, dan 6. Bilangan selain bilangan prima disebut bilangan komposit.

    Bilangan prima sangat menarik karena aturan untuk menentukan bilangan prima sudah sangat jelas dan mudah dipahami, namun belum ada rumus atau persamaan yang mudah untuk menentukan apakah sebuah angka merupakan bilangan prima atau bukan.

    Dalam tutorial kali ini akan dibahas bagaimana menentukan sebuah angka adalah prima atau bukan dengan menggunakan bahasa pemrograman Java. Cara yang paling sederhana adalah dengan mencoba membagi sebuah bilangan dengan setiap bilangan dari 2 sampai ke bilangan n – 1. Jika ada satu bilangan saja yang dapat habis membagi berarti bilangan tersebut bukan prima.

    boolean isPrime (int n) {
    
      for (int i=2; i<n; i++)
        if (n % i == 0)
          return false;
    
      return true;
    }
    

    Angka 2 adalah satu-satunya bilangan prima genap, jadi tidak perlu dipusingkan dengan mencobanya. Kode di atas mungkin cukup untuk mencari bilangan kecil, tapi kita butuh algoritma yang lebih cepat/mangkus untuk mencari bilangan besar. Kita tidak perlu mencari dari 2 sampai bilangan n, tapi cukup sampai n/2. Karena jika angka 2 habis membagi n, dapat dipastikan n/2 juga akan habis membagi n.

    boolean isPrime (int n) {
    
      for (int i=2; i*2<n; i++)
        if (n % i == 0)
          return false;
    
      return true;
    }
    

    Masih belum cukup cepat? Algoritma di atas bisa dioptimalkan lagi, yaitu dengan hanya memeriksa bilangan ganjil saja, karena semua bilangan genap pasti habis dibagi 2. Kemudian kita bisa efisienkan percobaan di atas dengan hanya memeriksa pembaginya sampai ke akar kuadrat dari bilangan n (dengan pembulatan ke bawah). Karena jika kita menderetkan semua faktor dari sebuah bilangan, akar kuadratnya pasti selalu berada di tengah-tengah deret tersebut. Misal faktor dari 81 adalah 1, 3, 9, 27, 81. Akar kuadrat dari 81 adalah 9, terletak tepat di tengah-tengah deret tersebut. Tidak perlu diperiksa setengah bagian setelah akar kuadrat karena pasti sudah ketahuan prima atau bukan.

    boolean isPrime (long n) {
    
      if (n % 2 == 0) return false;
        
        for (long i=3; i*i<=n; i+=2)
          if (n % i == 0)
            return false;
        
        return true;
    }
    

    Seperti terlihat pada potongan kode di atas, kita hanya memeriksa sampai ke akar kuadrat dari bilangan n dan hanya memproses bilangan ganjil. Dengan algoritma ini, program kita pasti mengalami peningkatan yang signifikan. Terutama ketika bekerja dengan angka yang sangat besar, itulah kenapa digunakan tipe data long.

    Sebenarnya ada satu lagi metode yang lebih mangkus yang disebut The Sieve of Eratosthenes. Namun kurang cocok dengan kebutuhan karena metode ini digunakan untuk mencari semua bilangan prima dari 2 sampai ke bilangan n.

  • Complement

    Saya menemukan sebuah lowongan menarik, yaitu setiap kandidatnya diharuskan mengerjakan sebuah soal dan jawabannya dikirimkan beserta surat lamaran.

    A complement of a number is defined as inversion (if the bit value = 0, change it to 1 and vice-versa) of all bits of the number starting from the leftmost bit that is set to 1.
    For example, if N = 5, N is 101 in binary. The complement of N is 010, which is 2 in decimal. Similarly if N = 50, then complement of N is 13
    Complete the function getIntegerComplement(). This function takes N as it’s parameter. The function should return the complement of N.

    Constraints :
    0 ≤ N ≤ 100000.
    Sample Test Cases:

    Input #00:
    50

    Output #00:
    13

    Explanation:
    50 in decimal form equals: 110010.
    Inverting the bit sequence: 001101. This is 13 in decimal
    Input #01:
    100

    Output #01:
    27

    Explanation:
    The bit sequence for 100 equals 1100100.
    Inverting the sequence we get 0011011 which is 27 in decimal.

    Tidak ada keterangan harus menggunakan bahasa pemrograman tertentu. Saya coba kerjakan dengan PHP dengan syarat tidak menggunakan fungsi bawaan PHP decbin() atau sebaliknya bindec(). Kita disuruh mencari complement bit dari masukan berupa bilangan desimal.

    Pertama kali kita harus mengkonversi bilangan tersebut menjadi bentuk biner (secara visual, karena pada dasarnya komputer hanya mengenal bilangan biner). Konversi dari desimal ke biner dapat dilakukan dengan menyimpan hasil bagi bilangan desimal dengan angka 2 dan menuliskannya secara terbalik dari hasil yang terakhir sampai hasil yang pertama. Kemudian hasilnya diubah satu persatu dari yang semula 0 ke 1 dan sebaliknya.

    <?php
    while ($input != 0) {
    
      $sisa[$i] = ~($input % 2) + 2;
      $input = floor($input / 2);
      $i++;
    }
    

    Kode di atas untuk memperoleh sisa hasil bagi, sekaligus dicari complement-nya. Namun kenapa harus ditambah 2? Karena komputer hanya mengenal bilangan biner, jadi kita harus menyesuaikan cara berpikir komputer. Kemudian komputer juga menggunakan two’s complement untuk operasi bilangan binernya. Sehingga angka 0 setelah di-complement akan menghasilkan -1 dan angka 1 akan menghasilkan -2. Hal ini tentu saja tidak sesuai harapan. Jadi ditambahkan dengan angka 2 sehinggan 0 -> -1 + 2 = 1 dan 1 -> -2 + 2 = 0.

    Penjelasannya adalah sebagai berikut. Misalnya pada komputer 8 bit, angka 0 bagi komputer adalah 0000 0000, jika dibalik menjadi 1111 1111. Bilangan biner 1111 1111 adalah two’s complement dari angka 1. Buktinya angka 1 biner 0000 0001 dibalik menjadi 1111 1110 lalu ditambah 1 = 1111 1111. Jadi pernyataan ~0 akan menghasilkan two’s complement dari 1. Operator tilde (~) hanya berfungsi membalik bit, interpretasi angkanya tetap bergantung kepada komputer.

    Tugas selanjutnya adalah mengkonversi lagi bilangan biner ke desimal. Caranya adalah dengan mengalikan tiap-tiap bit (dimulai dari LSB) dengan 2^0, 2^1, dst sampai ke MSB (most-significant bit), kemudian semua hasilnya dijumlahkan. Bilangan biner yang disimpan pada array sisa[] di atas urut dari LSB ke MSB (tidak diminta tampilannya jadi tidak perlu dibalik-balik urutannya).

    <?php
    foreach($sisa as $key => $value) {
    
      if ($key == 0) {
    
        $output = $value * 1;
        continue;
      }
    
      $output += $value * pangkat($key);
    }
    
    echo $output;
    

    Sebenarnya di PHP sudah ada fungsi pangkat, tapi kita buat sendiri aja toh sederhana ini. Sekaligus mengurangi ketergantungan terhadap satu bahasa.

    <?php
    function pangkat($a) {
    
      if ($a == 1)
        return 2;
    
      else
        return 2 * pangkat($a - 1);
    }
    

    Fungsi di atas rekursif. Parameternya adalah indeks dari array sisa[], jadi kalau indeks 3 berarti 2 * 2 * 2. Berikut ini ada listing lengkapnya.

    <?php
    $input = 50;
    $output = 0;
    $sisa[] = 0;
    $i = 0;
    
    if ($input >= 0 && $input <= 100000) {
    
      if ($input == 0)
        echo 1;
    
      else {
    	
        while ($input != 0) {
    
          $sisa[$i] = ~($input % 2) + 2;
          $input = floor($input / 2);
          $i++;
        }
    
        foreach($sisa as $key => $value) {
    
          if ($key == 0) {
    
            $output = $value * 1;
            continue;
          }
    
          $output += $value * pangkat($key);
        }
    
        echo $output;
      }
    }
    
    function pangkat($a) {
    
      if ($a == 1)
        return 2;
    
      else
        return 2 * pangkat($a - 1);
    }
    
  • Akses Database Menggunakan PDO pada PHP

    Sebagian besar programer PHP pasti pernah membuat aplikasi yang harus berinteraksi dengan database. Sampai pada PHP versi 5.1, pilihan yang direkomendasikan adalah menggunakan native drivers seperti Mysql Extensions.

    Sayangnya, pada PHP versi 5.4 penggunaan Mysql Extensions mulai ditinggalkan, dan akan dihapus secara keseluruhan pada PHP versi 5.5. Itu artinya, penggunaan fungsi mysql_connect() atau mysql_query(), seperti pada postingan yang lalu, harus diganti. Pilihannya adalah menggunakan Mysqli Extensions atau yang akan dibahas berikut ini, PDO.

    PDO (PHP Data Object), yang diperkenalkan sejak PHP versi 5.1, menyediakan antarmuka untuk berinteraksi dengan berbagai jenis database secara seragam. Gampangnya, kita bisa mengakses database MySQL, Firebird, atau IBM dengan syntax yang sama. Namun tidak semua driver otomatis tersedia pada sistem Anda. Untuk memeriksanya kita gunakan:

    <?php
    print_r(PDO::getAvailableDrivers()); 
    

    Pada artikel ini menggunakan database MySQL, jadi pastikan tulisan berikut ini yang muncul.

    Array ( [0] => mysql )

    Berikut ini adalah syntax untuk koneksi awal dengan database dan query SELECT MySQL.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $pdo->query('SELECT nama FROM tabel_pengguna WHERE id = ' . $_GET['id']);
    

    Kode di atas adalah contoh yang buruk karena data dari pengguna langsung dimasukan ke dalam query SQL tanpa difilter, sehingga rentan disusupi kode dari luar (SQL Injection). Jika dahulu kita biasa menggunakan fungsi mysql_real_escape_string(), sekarang dapat menggunakan PDO statement prepare() dan bindParam().

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    $stmt->bindParam(':id', filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT), PDO::PARAM_INT);
    $stmt->execute();
    

    Pada baris ke-3, masukan dari pengguna diganti dengan sebuah placeholder. Nama untuk placeholder dimulai dengan tanda titik dua (:). Fungsi filter_input() pada baris ke-4 digunakan untuk memastikan masukan dari luar, dalam hal ini $_GET['id'], benar-benar berupa angka integer. Dengan PDO statement dan bindParam() di atas, kode Anda akan aman dari serangan SQL Injection.

    Meskipun kode di atas dapat berjalan dengan baik, namun Anda akan mendapat pesan peringatan (notice):

    Only variables should be passed by reference bla.. bla..

    Itu terjadi karena parameter kedua dari bindParam() harus berupa variabel. Jadi kode di atas diubah menjadi seperti ini:

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('SELECT nama FROM tabel_pengguna WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->execute();
    

    Untuk contoh berikutnya, akan kita coba query INSERT dan UPDATE.

    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('INSERT INTO tabel_pengguna (id, nama) VALUES (:id, :nama)');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    
    <?php
    $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
    $stmt = $pdo->prepare('UPDATE tabel_pengguna SET nama = :nama WHERE id = :id');
    
    $id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT);
    $nama = filter_input(INPUT_GET, 'nama', FILTER_SANITIZE_STRING)
    $stmt->bindParam(':id', $id, PDO::PARAM_INT);
    $stmt->bindParam(':nama', $nama, PDO::PARAM_STR);
    $stmt->execute();
    

    Cara mencoba kode-kode di atas, langsung ketik saja di address bar. Contoh: localhost/namafile.php?id=10&nama=ronaldo. Jangan lupa isi variabel-variabel $host, $dbname, $user, dan $pass.

  • Menyesuaikan Zona Waktu di MySQL dan PHP

    Ketika membuat sebuah aplikasi website, seringkali waktu yang ditunjukkan oleh server tidak sama dengan waktu client. Ini terjadi karena server terletak di zona waktu yang berbeda dengan client. Misalnya server yang berlokasi di USA, jelas berada di zona waktu yang berbeda dengan client di Indonesia.

    echo date('d-m-Y H:i:s');
    

    Kode PHP di atas akan menampilkan waktu aktual pada server. Jika dicoba di localhost tentu saja waktunya akan sama seperti komputer lokal, namun hasilnya bisa berbeda jika dicoba pada server web Anda. Agar hasilnya sesuai dengan waktu di komputer kita, tambahkan pengaturan zona waktu.

    date_default_timezone_set('Asia/Jakarta');
    echo date('d-m-Y H:i:s');
    

    Untuk zona waktu yang lain bisa menyesuaikan.

    Namun cara di atas memiliki beberapa kelemahan, terutama pada aplikasi yang menggunakan database. Selain kemungkinan terkena bug Y2K38, MySQL dan PHP memiliki pengaturan zona waktu yang terpisah. Misal kita menjalankan fungsi NOW() pada MySQL, waktu yang ditampilkan tidak sesuai dengan zona waktu kita. Selain itu, aplikasi juga membutuhkan tambahan proses setiap kali mengolah data dari field waktu pada MySQL agar sesuai dengan zona waktu kita.

    Agar proses penyesuaian waktu bisa berjalan secara otomatis, kita harus mengetahui berapa selisih waktu client dengan UTC, kemudian beritahukan kepada MySQL. Pertama-tama dengan bantuan class Datetime, kita dapatkan offset (untuk menutupi selisih yang ada) dalam detik, kemudian mudah saja kita konversikan ke dalam menit dan jam.

    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    

    Kemudian cari tau apakah offset bernilai positif atau negatif, lalu konversikan saja ke bilangan positif untuk memudahkan penghitungan.

    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    

    Gunakan fungsi sprintf untuk memformat string agar bisa diterima MySQL (format yang bisa diterima misalnya: +07:00).

    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    

    Terakhir jalankan query SET time_zone setelah mengkoneksikan aplikasi dengan database. Silakan ganti dengan koneksi database yang Anda gunakan.

    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");
    

    Akhirnya pengaturan zona waktu antara PHP dan MySQL telah sesuai. Berikut ini adalah gabungan kode-kode snippet di atas.

    <?php
    date_default_timezone_set('Asia/Jakarta');
    
    $sekarang = new DateTime();
    $menit = $sekarang -> getOffset() / 60;
    
    $tanda = ($menit < 0 ? -1 : 1);
    $menit = abs($menit);
    $jam = floor($menit / 60);
    $menit -= $jam * 60;
    
    $offset = sprintf('%+d:%02d', $tanda * $jam, $menit);
    
    mysql_connect($server, $username, $password);
    mysql_select_db($database);
    
    mysql_query("SET time_zone = '$offset'");