Pages

Showing posts with label tutorial. Show all posts
Showing posts with label tutorial. Show all posts

Friday, November 01, 2019

Implimentasi pustaka Taburan Custom Nombor Rawak menggunakan Java

Menjana nombor rawak dengan Math.random()

Pustaka bahasa pengaturcaraan Java menyediakan fungsi/metod Math.random() untuk menjana nombor rawak dalam atur cara. Metod ini akan menjana nilai dalam julat 0 hingga 1, dan nilai ini boleh digunakan untuk menjana nombor tertentu dalam julat tertentu.

Misalnya kod atur cara berikut menjana 5 nombor rawak dalam julat 0 hingga 9 :

for(int i = 0; i < 5; i++) {
    System.out.println((int)(Math.random() * 10));
}



Manakala dengan menambah 1, seperti berikut, boleh mendapatkan 5 nombor dalam julat 1 hingga 10 :

for(int i = 0; i < 5; i++) {
     System.out.println((int)(Math.random() * 10 + 1)); 
}

Kita juga boleh menjana nombor rawak dalam julat 'min' dan 'max' yang kita sendiri tetapkan. Misalnya contoh berikut menjana nombor rawak dalam lingkungan 5 hingga 9:

int min = 5;
int max = 9;
for(int i = 0; i < 5; i++) {
    System.out.println((int)(Math.random() * (max-min+1)) + min)); 
}

Menjana nombor rawak dengan taburan custom

Kebarangkalian bagi nombor dalam julat di atas untuk muncul/dipilih, secara amnya ialah sama rata, iaitu 1/N bagi setiap nombor (Misalnya julat 1 hingga 10, N=10, maka 1/10 = 0.1 bagi 1, 1/10 = 0.1 bagi 2, dan seterusnya sehingga 10).

Tapi bagaimana kita boleh menjana nombor rawak dalam julat 1 hingga 10 dengan pemberat tertentu, misalnya 50% percubaan (kebarangkalian 0.5, atau 1/2) mendapat nombor 4? Atau menetapkan 20% balingan dadu akan kena pada nombor 6? Bagaimana kita hendak menggunakan Math.random() untuk menjana nombor rawak berdasarkan pemberat/kebarangkalian yang ditetapkan? Pustaka Java yang asas ini tidak menyediakannya.

Saya cuba mencari implimentasi sedia ada, dan terjumpa perbincangan mengenainya di sini -> https://stackoverflow.com/questions/16435639/generating-random-integers-within-range-with-a-probability-distribution

Daripada perbincangan tersebut, saya mendapat idea untuk mengimplimentasi suatu kelas pustaka sendiri (dinamakan CustomProbabilityRandom), yang membolehkan seseorang pengatur cara menjana nombor rawak mengikut kebarangkalian tertentu. (Lihat senarai kod atur cara dalam fail CustomProbabilityRandom.java di bawah. Turut disertakan contoh aplikasi untuk cara menggunakan pustaka, iaitu fail ExampleCustomDistribution.java).

Dengan menggunakan kelas ini, agak mudah untuk menetapkan taburan kebarangkalian bagi suatu senarai item-item (nombor rawak yang dijana mewakili index bagi item dalam senarai).

Sebenarnya implimentasi ini tidaklah benar-benar menghasilkan nombor rawak mengikut taburan yang ditetapkan, tetapi hanya boleh menghampiri taburan tersebut. Namun diharap dengan implimentasi ini akan memudahkan anda membina aplikasi yang melibatkan nombor rawak dengan lebih fleksibel. Untuk mengkompil kod atur cara aplikasi dalam ExampleCustomDistribution.java, anda perlu kompil bersama-sama dengan fail pustaka CustomProbabilityRandom.java.

Selamat mencuba.




Fail 'CustomProbabilityRandom.java' (pustaka)





Fail 'ExampleCustomDistribution.java' (aplikasi)





Monday, November 26, 2018

Pengalaman yang menakutkan...


Setiap orang ada pengalaman menakutkan. Saya juga tak terlepas melalui pengalaman ini.

Baru-baru ini saya berjaga malam untuk menyiapkan satu projek menggunakan Javascript. Sedang saya leka menulis atur cara ini, saya ternampak suatu benda yang sangat pelik. Saya pun cuba teliti betul-betul. Yalah, mana tahu saya tersalah tengok. Berulang kali saya periksa, rasanya macam mustahil terjadi perkara sedemikian. Selalunya tak akan muncul. Tapi yang ni betul-betul lain.


sumber foto: internet

Seram giler


Perhatikan kod aturcara Javascript di bawah:

function 
index2QuestionId( index ) {
    return 'q' + ( index + 1 );   
}

Operator + dalam Javascript boleh digunakan untuk operasi aritmetik (cth. 1+1=2) dan operasi pencantuman (concatenation) rentetan cth. "q" + 1 = "q1". Penentuan operasi akan dibuat secara automatik oleh penterjemah Javascript.

Fungsi Javascript di atas sepatutnya membina rentetan (string) berbentuk 'q1' (index=0), 'q29' (index=28) dsb. Parameter index sepatutnya menerima nilai integer untuk indeks suatu tatasusunan (array, yang bermula dari 0). Apa yang pelik? Nampak biasa saja kan? 

Tapi bukan bagi Javascript. Jika fungsi di atas dipanggil,


index2QuestionId(0);     // oops! return 'q01'??
index2QuestionId(28);    // oops! return 'q281'??

Kod tu saya rasa betul! Kenapa hasilnya salah? Ni yang jadi seram ni.

Setelah berhempas pulas debug dengan menggunakan Chrome developer tool (kemudianlah saya cerita pasal tool ni) akhirnya saya dapat kesan punca masalah dari fungsi ini dan kenapa ia berlaku.

Penjelasan


Penentuan operasi akan dibuat secara automatik oleh penterjemah Javascript. Ini bahayanya. Tujuannya memudahkan pengatur cara tetapi berisiko. Dalam contoh fungsi di atas, apa yang dibuat ialah (andaikan index=0),

=> 'q' + ( index + 1 )
=> 'q' + ( '0' + 1 )
=> 'q' + ( '01' )
=> 'q01'

Dalam kes ini index diterjemah sebagai string! (sepatutnya integer, 0 + 1 = 1) oleh penterjemah (bijak la sangat...)

Ini yang saya kata menakutkan, sebab tindakan penterjemah sebegini memang tak dijangka, dan susah nak dikesan.

Rasionalnya dalam kesilapan di atas, penterjemah tak ada cara menentukan parameter yang diberi ialah berjenis integer sebab bahasa Javascript tidak menggunakan penanda jenis data atau pemboleh ubah (bahasa Java atau C ada int, float, char dsb.). Jadi penterjemah cuba buat tekaan (dan tersilap).

Untuk memperbaiki masalah ini (memastikan penterjemah buat tekaan yang betul), boleh dilakukan dengan 2 cara seperti berikut:

CARA 1 - asingkan operasi untuk jenis data yang berbeza.

function 
index2QuestionId( index ) {
     var x = index + 1;      // aritmetik
     return 'q' + x;         // cantum
}

CARA 2 - jelaskan pada penterjemah operasi yang diperlukan (cth. parseInt() hasilkan integer, jadi dapat jelaskan pada penterjemah pembolehubah yang nak diguna ialah jenis integer)

function 
index2QuestionId( index ) {
     return 'q' + ( parseInt(index) + 1);
}

Kedua-dua cara ini akan hasilkan output yang betul seperti berikut

index2QuestionId(0);     // return 'q1'
index2QuestionId(28);    // return 'q29'

Bila dapat kesan punca kesilapan, dan tahu penyelesaiannya, barulah lega sikit.

Semoga perkongsian pengalaman menakutkan ini dapat memberi manfaat.
Jadi tuan-tuan, bila mengaturcara dengan Javascript berhati-hatilah.

Jangan biarkan diri anda diselubungi misteri...

Wednesday, April 18, 2018

Tutorial - Menggunakan JFreeChart untuk simulasi penjadualan proses

Pengenalan JFreeChart

JFreeChart ialah pustaka kod sumber terbuka (open source) yang dibangunkan oleh David Gilbert sejak tahun 2000. Pustaka ini boleh diperoleh dan digunakan secara percuma (tetapi perlu menyatakan hakcipta pemilik/pencipta asal). 

Fungsi pustaka ini ialah untuk membolehkan pengatur cara menghasilkan carta melalui atur cara Java. Contoh-contoh carta yang boleh dihasilkan oleh pustaka ini boleh dirujuk di laman JFreeChart.org. Antaranya termasuklah seperti yang dipaparkan di bawah:

sumber: JFreeChart.org


Tutorial - membina carta Gantt untuk simulasi penjadualan proses.


Tutorial ini bertujuan menunjukkan cara-cara menggunakan JFreeChart dalam aturcara Java untuk membina aplikasi simulasi penjadualan proses.

Contoh aplikasi yang ditunjukkan dalam tutorial ini ialah aplikasi simulasi penjadualan proses untuk penggunaan CPU (Central Processing Unit, atau Unit Pemprosesan Pusat). Aplikasi simulasi ini akan membentuk 5 proses yang akan dijadualkan (untuk menggunakan 'CPU') menggunakan algoritma Round-Robin. Hasil simulasi penjadualan akan ditunjukkan seperti dalam gambarajah di bawah, yang mengandungi 1 tetingkap untuk memaparkan carta Gantt, dan maklumat terperinci dipaparkan dalam paparan konsol (console).

Tutorial ini melibatkan tiga peringkat berikut:

  • Memasukkan pustaka JFreeChart dalam projek
  • Memasukkan panggilan pustaka JFreeChart yang asas ke dalam atur cara Java
  • Membina aplikasi simulasi penjadualan
.

Peringkat 1: Memasukkan pustaka JFreeChart dalam projek.


Untuk menggunakan pustaka JFreeChart, kita boleh mendapatkan kod sumber sepenuhnya dan kemudian mengkompilnya ke dalam bentuk fail JAR (Java ARchive). Walau bagaimanapun kita boleh terus mendapatkan fail JAR yang sudah siap dikompil, kemudian memasukkannya ke dalam projek aplikasi Java yang kita bina. 

Untuk tutorial ini, kita perlukan dua fail JAR berikut :
  • jCommon.jar
  • jFreeChart.jar

Versi terkini fail-fail ini boleh dimuat turun dari laman web JFreeChart.org

Berikut ialah langkah memasukkan fail JAR ke dalam projek (IDE Netbeans):
  • Langkah 1: Muat turun dan letakkan fail-fail JAR tersebut ke dalam sebuah folder/direktori.
  • Langkah 2: Buka tetapan (setting) projek, dengan klik butang kanan pada nama projek dan pilih Properties (merujuk 'Project Properties').
  • Langkah 3: Dalam tetingkap dialog yang muncul, klik Libraries, dan pilih tab Compile. Tekan butang Add JAR/folder dan cari semula folder yang mengandungi fail-fail JAR yang sudah dimuat turun.

.

Peringkat 2: Memasukkan panggilan pustaka JFreeChart yang asas dalam atur cara Java.


Nota : JFreeChart menggunakan pustaka Java Swing, iaitu salah satu platform atau pustaka GUI bagi Java. Diandaikan anda tahu cara-cara menggunakan Java Swing. Jika tidak tahu, anda perlu belajar dahulu cara menggunakan Java Swing.

Untuk memahami konsep JFreeChart, kita akan bina satu aplikasi asas menggunakan JFreeChart. Dalam aplikasi asas ini, kita membina sebuah kelas JFrame (kelas dari Java Swing) dan memaparkan sebuah carta Gantt di dalamnya. 

Umumnya, untuk membina carta Gantt menggunakan JFreeChart, 

  1. Kita perlu sediakan satu siri data menggunakan kelas TaskSeries, dan dimasukkan ke dalam sebuah koleksi dari kelas TaskSeriesCollection (IntervalCategoryDataset ialah super-kelas kepada kelas ini).

    ...
        private IntervalCategoryDataset buildChartDataset()      {
            
            // Bina beberapa siri data
            TaskSeries series = new TaskSeries("Data Series 1");
            Task t1 = new Task("T1", new SimpleTimePeriod( 0, 100 ));  
            series.add( t1 );
            Task t2 = new Task("T2", new SimpleTimePeriod( 110, 180 )); 
            series.add( t2 );
            Task t3 = new Task("T3", new SimpleTimePeriod( 180, 220 )); 
            series.add( t3 );

            TaskSeriesCollection dataset = new TaskSeriesCollection();
            dataset.add(series);
            
            return dataset;
        }
    ...
  2. Set data ini akan kita jadikan ke dalam bentuk carta menggunakan metod dari kelas statik ChartFactory.createGanttChart(). Metod ini menghasilkan sebuah objek dari jenis kelas JFreeChart.

    ...
        public TestChart(String title) {
            super(title);

            // Bina data set untuk carta
            IntervalCategoryDataset dataset = buildChartDataset();

            // Create chart
            JFreeChart chart = ChartFactory.createGanttChart(
                                    "Simple Gantt Chart",    // Tajuk
                                    "Task",                  // Label Y-Axis
                                    "Timeline",              // Label X-Axis
                                    dataset);

            // set x-axis range supaya bermula dengan 0
            ( chart.getCategoryPlot() ).setRangeAxis(new NumberAxis());  
    ...
  3. Objek carta yang terhasil dari metod statik ChartFactory.createGanttChart() perlu dimasukkan ke dalam sebuah container dari kelas  JPanel. Container yang mengandungi carta akan dimasukkan ke dalam JFrame untuk dipaparkan.

    ...
            ChartPanel panel = new ChartPanel(chart);   // tambah ke dalam panel 
            setContentPane(panel);                      // paparkan panel
    ...
Atur cara di bawah menunjukkan keseluruhan atur cara yang menggunakan langkah-langkah yang dinyatakan ini.


Output daripada atur cara ini ialah seperti berikut:





Peringkat 3: Membina aplikasi simulasi penjadualan.

Setelah mengetahui bagaimana hendak membina carta menggunakan JFreeChart, kita gabungkan kemudahan ini untuk membina simulasi penjadualan proses.


Algoritma Penjadualan Proses

Penjadualan proses ialah antara tugas utama yang dijalankan oleh sistem pengoperasian (OS) dalam mengurus perlaksanaan atur cara dan program komputer. Penjadualan proses boleh dilakukan dengan menggunakan pelbagai jenis algoritma. Antara algoritma penjadualan proses yang asas ialah First-come-first-serve (FCFS), Round-Robin (RR), Shortest-Job-First (SJF) dan Priority-based. Kod atur cara berikut ialah sebahagian atur cara yang mengandungi implementasi algoritma RR (Lihat kod atur cara penuh dipaparkan di bawahnya):

 ... 
    private Process[] simulateRR(Process[] pt, int quantum)
    {

        // RR-queue, to manage the round-robin scheduling
        class RRQItem {
            public Process process;
            public int remaining;
            public int stopped;
        }
        Queue<RRQItem> rrQ = new LinkedList();   // Round-Robin queue

        int clk_tick = 0;                        // reset clk_tick

        int cpu_tick = 0;             // timer for running process
        boolean cpu_idle = true;      // if not idle, then CPU is busy

        int totDone = 0;
        while(totDone < pt.length) {

            // is it time to execute (submit to queue) ?
            for(int p=0; p < pt.length; p++ ) {
                if (clk_tick == pt[p].arrivalTime) {

                    // put inside RR-queue
                    RRQItem item = new RRQItem();
                    item.process = pt[p];
                    item.remaining = pt[p].burstTime;
                    item.stopped = pt[p].arrivalTime;
                    rrQ.add(item);
                }
            }
            if(!cpu_idle) {
                cpu_tick--;           // deplete cpu burst time
                if(cpu_tick <= 0) {   // if timeout
                    // remove currently running process
                    RRQItem running = rrQ.remove();
                    cpu_idle = true;

                    if(running.remaining > 0) {       // if not yet finish
                        running.stopped = clk_tick;   // record when this process
                                                      //   is stopped
                        running.process.numOfTimesStopped++;   // record stoppage
                                                               //   count
                        rrQ.add(running);             // put back into queue

                    } else {
                        // process finish, calculate turnaround time
                        running.process.turnaroundTime =
                                clk_tick - running.process.arrivalTime;
                        totDone++;

                    }
                }
            }
            // if CPU idle, and no process is running
            if(cpu_idle) {
                // submit next remaining process in queue
                if(!rrQ.isEmpty()) {
                    RRQItem front = rrQ.peek();            // check who is at
                                                           //   the front of Q

                    if(front.process.startTime < 0) {      // record the first
                                                           //   time process start
                        front.process.startTime = clk_tick;  // record starting
                                                             //   time
                    }
                    front.process.waitingTime += clk_tick - front.stopped;

                    // submit and set for how long is the execution time
                    cpu_idle = false;
                    cpu_tick = (front.remaining >= quantum ? quantum :
                                front.remaining);

                    Process part = new Process( "" + front.process.id + "_" +
                              front.process.subtask.size(), cpu_tick, clk_tick);
                    part.startTime = clk_tick;
                    // record waiting time for this part
                    part.waitingTime = clk_tick - front.stopped;    
                    
                    // add new part (sub-process)
                    front.process.subtask.add( part );                 
                    
                    // update remaining time for this process
                    front.remaining -= cpu_tick;

                }
            }
            clk_tick++;                     // update clk_tick
        }
        return pt;
   }   
...

Simulasi Penjadualan Proses 

Berikut ialah atur cara penuh Java yang digunakan untuk menghasilkan simulasi penjadualan proses menggunakan algoritma Round-Robin.


Output yang dipaparkan daripada aplikasi simulasi penjadualan proses: