Monday 21 August 2017

Pseudo code moving average


Analisis Seri Waktu dan Aplikasinya: Dengan Kode R Contoh yang Digunakan dalam Edisi Ketiga Contoh Berikut adalah kode yang digunakan untuk setiap contoh numerik dalam teks. Barang ini tidak akan berfungsi kecuali Anda telah memuat astsa dan file data pada awal sesi. Jika ini adalah pertama kalinya Anda di sini, Anda mungkin ingin membaca daftar catatan paket astsa untuk informasi lebih lanjut. Bab 1 Contoh 1.1 Contoh 1.2 Contoh 1.3 Contoh 1.4 Contoh 1.5 Contoh 1.6 Contoh 1.7 Contoh 1.9 Contoh 1.10 Contoh 1.11 Contoh 1.12 Contoh 1.24 Contoh 1.25 Contoh 1.26 Contoh 1.27 Bab 2 Contoh 2.1 Contoh 2.2 Contoh 2.3 Contoh 2.4 dan 2.5 Contoh 2.6 Contoh 2.7 Contoh 2.8 Contoh 2.9 Contoh 2.10 Contoh 2.11 Contoh 2.12 Contoh 2.13 Contoh 2.14 Contoh 2.15 Bab 3 Contoh 3.1 Contoh 3.4 Contoh 3.10 Contoh 3.11 Contoh 3.15 Contoh 3.17 Contoh 3.24 Contoh 3.27 Contoh 3.28 Contoh 3.29 Contoh 3.35 Contoh 3.37 Contoh 3.38 Contoh 3.40 Contoh 3.44 Contoh 3.46 Bab 4 Contoh 4.1 Contoh 4.2 Contoh 4.6 Contoh 4.8 Contoh 4.10 Contoh 4.11 Contoh 4.12 Contoh 4.13 Contoh 4.14 Contoh 4.15 Contoh 4.18 Contoh 4.19 Contoh 4.21 Contoh 4.22 Contoh 4.23 Contoh 4.24 Contoh 4.25 Contoh 4.26 Bab 5 Contoh 5.1 Contoh 5.2 Contoh 5.3 Contoh 5.4 Contoh 5.5 Contoh 5.6 Contoh 5.7 Contoh 5.8 Contoh 5.9 Contoh 5.10 Contoh 5.11 Bab Ter 6 Contoh 6.1 Contoh 6.2 Contoh 6.5 Contoh 6.6 Contoh 6.7 Contoh 6.8 Contoh 6.9 Contoh 6.10 Contoh 6.12 Contoh 6.13 Contoh 6.17 Contoh 6.18 Contoh 6.19 Contoh 6.23 Bab 7 Kode dalam Pendahuluan Contoh 7.1 Contoh 7.2 Contoh 7.4 Contoh 7.6 Contoh 7.7 Contoh 7.8 Contoh 7.9 Contoh 7.10 Contoh 7.11 Contoh 7.12 Contoh 7.13 Contoh 7.14 Contoh 7.15 Contoh 7.16 Contoh 7.18 Contoh 7.19 Contoh 7.20MOVING FORTH Bagian 1: Tentukan Keputusan Desain di Kernel Forth oleh Brad Rodriguez Artikel ini pertama kali dimuat di The Computer Journal 59 (JanuariFebruari 1993). PENDAHULUAN Semua orang di komunitas Forth berbicara tentang betapa mudahnya untuk memasuki Forth ke CPU baru. Tapi seperti banyak kutipan dan pertanyaan kuotomatis, tidak banyak yang tertulis tentang bagaimana melakukannya. Jadi, ketika Bill Kibler menyarankan topik ini untuk sebuah artikel, saya memutuskan untuk memutuskan tradisi lisan Forthwrights yang hebat, dan mendokumentasikan prosesnya dalam warna hitam dan putih. Selama artikel ini saya akan mengembangkan Forths untuk 6809, 8051, dan Z80. Im melakukan 6809 untuk mengilustrasikan model Forth yang mudah dan konvensional plus, saya telah mempublikasikan 6809 assembler ROD91, ROD92, dan Ill membutuhkan 6809 Forth untuk proyek TCJ di masa depan. Saya melakukan Proyek 8.051 untuk sebuah proyek di Universitas, namun juga menggambarkan beberapa keputusan desain yang agak berbeda. Z80 Forth adalah untuk semua pembaca CPM TCJ, dan untuk beberapa teman dengan mengumpulkan sampah TRS-80an. PERANGKAT KERAS PENTING Anda harus memilih CPU. Saya tidak akan menyelidiki kelebihan satu CPU di atas yang lain untuk Forth, karena pilihan CPU biasanya dipaksakan pada Anda oleh pertimbangan lain. Selain itu, objek dari artikel ini adalah untuk menunjukkan bagaimana cara memindahkan Forth ke CPU manapun. Anda bisa mengharapkan kernel 16-bit Forth yang biasa (lihat di bawah) untuk menempati sekitar 8K byte ruang program. Untuk kernel lengkap yang bisa mengkompilasi definisi Forth, Anda harus mengizinkan RAM 1K byte minimum. Untuk menggunakan sistem pengelolaan blok Forths untuk penyimpanan disk, Anda harus menambahkan 3 Kbytes atau lebih untuk buffer. Untuk model Forth 32-bit, dua kali lipat angka ini. Ini adalah minimum untuk mendapatkan kernel dan kernel Forth. Untuk menjalankan aplikasi pada perangkat keras Anda, Anda harus meningkatkan ukuran PROM dan RAM yang sesuai. 16 ATAU 32 BIT Ukuran kata yang digunakan oleh Forth tidak harus sama dengan CPU. Forth praktis terkecil adalah model 16-bit yaitu satu yang menggunakan bilangan bulat 16 bit dan alamat 16 bit. Komunitas Forth menyebut ini ukuran quotcellquot, karena kata kunci mengacu pada definisi Forth. CPU 8-bit hampir selalu mendukung 16-bit Forths. Ini biasanya membutuhkan pengkodean eksplisit dari aritmatika double-byte, walaupun beberapa CPU 8-bit memang memiliki beberapa operasi 16 bit. 16-bit CPU biasanya menjalankan 16-bit Forths, meskipun teknik presisi ganda yang sama dapat digunakan untuk menulis 32-bit Forth pada CPU 16 bit. Setidaknya satu 32-bit Forth telah ditulis untuk 80868088. CPU 32-bit biasanya menjalankan 32-bit Forths. Model Forth yang lebih kecil jarang menghemat panjang kode atau waktu prosesor. Namun, saya tahu setidaknya ada 16 bit Forth yang ditulis untuk 68000. Ini mengurangi ukuran kode aplikasi dengan faktor dua, karena definisi Forth tingkat tinggi menjadi rangkaian alamat 16 bit daripada string 32- Alamat bit (Ini akan menjadi jelas segera.) Kebanyakan 68.000, meskipun, memiliki banyak RAM. Semua contoh yang dijelaskan dalam artikel ini adalah 16-bit Forths yang berjalan pada CPU 8-bit. TEKNIK PENCIPTAANKodeTotot yang dicentang adalah ciri khas Forth. Sebuah kutipan kuantum hanya daftar alamat rutin yang akan dieksekusi. Anda bisa menganggap ini sebagai daftar panggilan subrutin, dengan petunjuk CALL dihapus. Selama bertahun-tahun banyak variasi threading telah dirancang, dan mana yang terbaik tergantung pada CPU dan aplikasi. Untuk membuat keputusan, Anda perlu memahami bagaimana mereka bekerja, dan pengorbanan mereka. Tidak Langsung Threaded Code (ITC) Ini adalah teknik pengarsipan Forth klasik, yang digunakan pada fig-Forth dan F83, dan dijelaskan dalam kebanyakan buku di Forth. Semua skema threading lainnya adalah quotimprovementsquot pada hal ini, jadi Anda perlu memahami ITC untuk menghargai yang lain. Mari kita lihat definisi kata depan SQUARE: Dalam ITC Forth yang khas, ini akan muncul di memori seperti yang ditunjukkan pada Gambar 1. (Header akan dibahas di artikel masa depan yang berisi informasi rumah tangga yang digunakan untuk kompilasi, dan tidak terlibat dalam threading .) Asumsikan SQUARE ditemui saat mengeksekusi kata Forth lainnya. Forths Interpreter Pointer (IP) akan menunjuk ke sel dalam memori - terdapat dalam kata quototherquot tersebut - yang berisi alamat kata SQUARE. (Tepatnya, sel itu berisi alamat Lapangan Kode SQUAREs.) Penafsir mengambil alamat itu, dan kemudian menggunakannya untuk mengambil isi Lapangan Kode SQUAREs. Isi ini adalah alamat lain - alamat subroutine bahasa mesin yang melakukan kata SQUARE. Dalam pseudo-code, ini adalah: Ini menggambarkan sebuah prinsip penting namun jarang dijelaskan: alamat kata Forth yang baru dimasukkan disimpan dalam W. KODE kata-kata tidak memerlukan informasi ini, tapi semua jenis kata-kata Forth lainnya. Jika SQUARE ditulis dalam kode mesin, ini akan menjadi akhir dari cerita: sedikit kode mesin akan dieksekusi, dan kemudian beralih kembali ke interpreter Forth - yang, sejak IP bertambah, mengarah ke kata berikutnya ke dihukum mati. Inilah sebabnya mengapa juru bahasa Forth biasanya disebut NEXT. Tapi, SQUARE adalah definisi quotcolonquot tingkat tinggi - ini berisi quotthreadquot, daftar alamat. Untuk melakukan definisi ini, juru bahasa Forth harus dimulai kembali di lokasi baru: Bidang Parameter SQUARE. Tentu saja, lokasi penerjemah lama harus diselamatkan, untuk melanjutkan kata Quotquot Forth dulu setelah SQUARE selesai. Ini sama seperti panggilan subrutin Tindakan bahasa mesin SQUARE hanyalah untuk mendorong IP lama, mengatur IP ke lokasi baru, menjalankan penerjemah, dan saat SQUARE dilakukan, pop IP. (Seperti yang Anda lihat, IP adalah kuartalan counterquot dari tingkat tinggi Forth.) Ini disebut DOCOLON atau ENTER di berbagai Forths: Fragmen kode yang identik ini digunakan oleh semua definisi Forth tingkat tinggi (yaitu threaded) Thats why a pointer Untuk fragmen kode ini, bukan fragmen itu sendiri, termasuk dalam definisi Forth. Lebih dari ratusan definisi, penghematan bertambah dan inilah mengapa disebut threading tidak langsung. The quotreturn dari subroutinequot adalah kata EXIT, yang dikompilasi saat Forth melihat. (Beberapa orang menyebutnya S bukan EXIT.) EXIT hanya menjalankan rutinitas bahasa mesin yang melakukan hal berikut: Berjalanlah melalui beberapa definisi Forth bersarang, hanya untuk meyakinkan diri Anda bahwa ini berhasil. Perhatikan karakteristik ITC: setiap kata Forth memiliki Field Code satu sel. Definisi usus mengkompilasi satu sel untuk setiap kata yang digunakan dalam definisi. Dan juru bahasa Forth harus benar-benar melakukan tipuan ganda untuk mendapatkan alamat kode mesin berikutnya untuk dijalankan (pertama melalui IP, lalu melalui W). ITC bukanlah teknik threading terkecil maupun tercepat. Ini mungkin yang paling sederhana meski DTC (dijelaskan selanjutnya) sama sekali tidak lebih kompleks. Jadi mengapa begitu banyak Forths tidak langsung threaded Terutama karena sebelumnya Forths, digunakan sebagai model, tidak langsung. Hari-hari ini, DTC menjadi lebih populer. Jadi, kapan sebaiknya ITC digunakan Dari berbagai teknik, ITC menghasilkan definisi terbersih dan paling elegan - tidak lain kecuali alamat. Jika Anda menyesuaikan diri dengan pertimbangan tersebut, ITC dapat mengajukan banding kepada Anda. Jika kode Anda berkutat dengan bagian dalam definisi, kesederhanaan dan keseragaman representasi ITC dapat meningkatkan portabilitas. ITC adalah model Forth klasik, jadi mungkin lebih disukai untuk pendidikan. Akhirnya, pada CPU yang kekurangan instruksi panggilan subrutin - seperti 1802 - ITC seringkali lebih efisien daripada DTC. Kode Berurutan Langsung (DTC) Direct Threaded Code berbeda dari ITC hanya dalam satu hal: alih-alih Kode Bidang yang berisi alamat beberapa kode mesin, Kode Bidang berisi kode mesin sebenarnya. Saya tidak mengatakan bahwa kode lengkap untuk ENTER terdapat dalam masing-masing dan setiap definisi kolon. Dalam kata-kata kuotri tingkat tinggi, Field Kode akan berisi panggilan subrutin. Seperti yang ditunjukkan pada Gambar 2. Definisi usus, misalnya, akan berisi panggilan ke rutinitas ENTER. Kode pseudo-NEXT untuk threading langsung adalah sederhana: Kecepatan keuntungan ini: penafsir sekarang hanya melakukan tipuan tunggal. Pada Z80 ini akan mengurangi rutinitas BERIKUTNYA - fragmen kode yang paling banyak digunakan di kernel Forth - dari sebelas instruksi ke tujuh Ruang biaya ini: setiap definisi tingkat tinggi di Z80 Forth (misalnya) sekarang satu byte lebih lama, Karena alamat 2 byte telah diganti dengan panggilan 3 byte. Tapi ini tidak benar secara universal. 32-bit 68000 Forth dapat mengganti alamat 4 byte dengan instruksi BSR 4-byte, tanpa rugi bersih. Dan pada Zilog Super8, yang memiliki instruksi mesin untuk DTC Forth, alamat 2 byte diganti dengan instruksi ENTER 1 byte, membuat DTC Forth lebih kecil pada Super8 Tentu saja, definisi DTC CODE dua byte lebih pendek, karena mereka Tidak lagi membutuhkan pointer sama sekali Saya biasa berpikir bahwa definisi tingkat tinggi di DTC Forths mengharuskan penggunaan panggilan subrutin di Bidang Kode. Frank Sergeant Pygmy Forth SER90 menunjukkan bahwa lompatan sederhana dapat digunakan dengan mudah, dan biasanya akan lebih cepat. Guy Kelly telah menyusun ulasan hebat tentang implementasi Forth untuk IBM PC KEL92, yang sangat saya rekomendasikan untuk semua penulis kernel Forth. Dari 19 jurusan yang ia pelajari, 10 menggunakan DTC, 7 menggunakan ITC, dan 2 menggunakan thread subrutin (dibahas selanjutnya). Saya merekomendasikan penggunaan Direct-Threaded Code over-Direct Threaded Code untuk semua kernel Forth yang baru. Langsung ke NEXT, atau kode itu in-line The Forth inner interpreter, NEXT, adalah rutinitas umum untuk semua definisi KODE. Anda mungkin hanya menyimpan satu salinan rutinitas umum ini, dan meminta semua kata KODE melompat ke sana. (Perhatikan bahwa Anda Langsung ke NEXT a subroutine Call tidak diperlukan). Namun, kecepatan NEXT sangat penting bagi kecepatan keseluruhan sistem Forth. Selain itu, pada banyak CPU, rutinitas BERIKUTNYA cukup singkat sering hanya dua atau tiga instruksi. Jadi mungkin lebih baik kode NEXT in-line, dimanapun itu digunakan. Hal ini sering dilakukan dengan membuat NEXT sebuah assembler macro. Ini adalah kecepatan sederhana vs keputusan ruang: in-line NEXT selalu lebih cepat, tapi hampir selalu lebih besar. Peningkatan ukuran total adalah jumlah byte tambahan yang diperlukan untuk ekspansi in-line, kali jumlah kata-kata KODE dalam sistem. Kadang-kadang tidak ada tradeoff sama sekali: dalam DTC Forth 6809, in-line NEXT lebih pendek daripada instruksi Jump Subroutine Threaded Code (STC) Definisi Forth tingkat tinggi tidak lain adalah daftar subrutin yang akan dieksekusi. Anda tidak memerlukan juru bahasa untuk melakukannya, Anda bisa mendapatkan efek yang sama dengan hanya merangkai daftar panggilan subrutin bersama-sama: Lihat Gambar 3. Representasi kata-kata Forth ini telah digunakan sebagai titik awal untuk menjelaskan teknik threading ke bahasa assembly programmer KOG82. STC adalah representasi elegan definisi kolon dan kata-kata KODE sekarang identik. QuotDefined wordsquot (VARIABLE, CONSTANTs, dan sejenisnya) ditangani sama seperti di DTC - Field Code dimulai dengan lompatan atau panggilan ke beberapa kode mesin di tempat lain. Kelemahan utama adalah bahwa panggilan subrutin biasanya lebih besar daripada alamat sederhana. Pada Z80, misalnya, ukuran definisi usus besar meningkat sebesar 50 - dan sebagian besar aplikasi Anda adalah definisi usus buntu. Sebaliknya, pada 68000 32-bit mungkin tidak ada peningkatan ukuran sama sekali, ketika alamat 4 byte diganti dengan BSR 4 byte. (Tapi jika ukuran kode Anda melebihi 64K, beberapa alamat tersebut harus diganti dengan JSR 6-byte.) Antrian subroutine mungkin lebih cepat daripada threading langsung. Anda menghemat waktu dengan tidak memiliki juru bahasa, tapi Anda kehilangan waktu karena setiap referensi ke kata Forth melibatkan push and pop dari alamat pengirim. Dalam DTC Forth, hanya kata-kata tingkat tinggi yang menyebabkan aktivitas di tumpukan kembali. Pada 6809 atau Zilog Super8, DTC lebih cepat dari STC. Ada keuntungan lain untuk STC: ini membagi-bagikan dengan register IP. Beberapa prosesor - seperti 8.051 - sangat kekurangan register pengalamatan. Menghilangkan IP benar-benar dapat menyederhanakan dan mempercepat kernel. Satu-satunya cara untuk mengetahui dengan pasti adalah dengan menulis kode contoh. Hal ini terkait erat dengan seleksi register, yang dibahas pada bagian selanjutnya. STC dengan in-line expansion optimization direct compilation Pada CPU lama dan 8-bit, hampir setiap primitif Forth melibatkan beberapa instruksi mesin. Tetapi pada CPU yang lebih kuat, kebanyakan primitif Forth ditulis dalam satu instruksi. Sebagai contoh, pada 32-bit 68000, DROP hanya Dalam subwutine-threaded Forth, menggunakan DROP dalam definisi kolon akan menghasilkan urutan ADDQ adalah instruksi dua byte. Mengapa menulis panggilan subrutin empat byte ke instruksi dua byte Tidak peduli berapa kali DROP digunakan, tidak ada penghematan Kode lebih kecil dan lebih cepat jika ADDQ dikodekan langsung ke arus BSR. Beberapa kompiler Forth melakukan ekspansi kuototometer ini dari kata-kata KODE CUR93a. Kerugian dari ekspansi in-line adalah dekompilasi kembali ke kode sumber asli menjadi sangat sulit. Selama panggilan subrutin digunakan, Anda masih memiliki petunjuk (alamat subrutin) ke kata-kata Forth yang terdiri dari benang. Dengan petunjuk pada kata-kata itu, Anda bisa mendapatkan namanya. Tapi begitu sebuah kata diperluas menjadi kode in-line, semua pengetahuan tentang asal kode itu hilang. Keuntungan dari ekspansi in-line - selain dari kecepatan dan ukuran - adalah potensi pengoptimalan kode. Sebagai contoh, urutan Forth akan disusun dalam 68000 STC karena namun dapat diperluas secara in-line sebagai instruksi mesin tunggal Mengoptimalkan kompiler Forth terlalu luas menjadi topik untuk artikel ini. Ini adalah area aktif penelitian bahasa Forth yang melihat, misalnya, SCO89 dan CUR93b. Puncak terakhir dari STC yang dioptimalkan adalah Forth yang mengkompilasi kode mesin quotpurequot, seperti kompiler C atau Fortran. Token Threaded Code (TTC) DTC dan STC bertujuan untuk meningkatkan kecepatan program Forth, dengan beberapa biaya dalam memori. Sekarang mari kita bergerak arah lain dari ITC, menuju sesuatu yang lebih lambat tapi lebih kecil. Tujuan thread Forth adalah untuk menentukan daftar kata-kata Forth (subrutin) yang akan dilakukan. Misalkan sistem 16-bit Forth hanya memiliki 256 kata yang berbeda. Kemudian setiap kata bisa dikenali secara unik oleh nomor 8-bit. Alih-alih daftar alamat 16 bit, Anda akan memiliki daftar pengenal 8-bit atau quottokens, quot dan ukuran definisi kolon akan dibelah dua. Forth menyimpan sebuah tabel alamat dari semua kata-kata Forth, seperti Ditunjukkan pada Gambar 4. Nilai token kemudian digunakan untuk mengindeks ke dalam tabel ini, untuk menemukan kata Forth yang sesuai dengan tanda yang diberikan. Ini menambahkan satu tingkat tipuan pada juru bahasa Forth, jadi lebih lambat dari pada kuadrat-threadedquot Forth. Keuntungan utama dari token-threaded Forths adalah ukuran kecil. TTC paling sering terlihat pada komputer genggam dan aplikasi dengan ukuran terbatas lainnya. Juga, tabel tanda kutip masuk ke dalam semua kata-kata Forth dapat mempermudah keterkaitan modul yang disusun secara terpisah. Kerugian dari TTC adalah kecepatan: TTC membuat Forths paling lambat. Juga, kompiler TTC sedikit lebih kompleks. Jika Anda memerlukan lebih dari 256 kata terakhir, diperlukan beberapa skema pengkodean terbuka untuk menggabungkan token 8 bit dan lebih besar. Saya dapat membayangkan 32-bit Forth menggunakan token 16 bit, tapi berapa banyak sistem 32-bit yang dibatasi oleh ukuran Segmen Threaded Code Karena ada begitu banyak turunan 8086 di dunia, threading segmen pantas untuk disebutkan secara singkat. Alih-alih menggunakan alamat byte quotnormalquot dalam segmen 64K, alamat paragraf digunakan. (A quotparagraphquot adalah 16 byte di 8086.) Kemudian, penafsir dapat memuat alamat ini ke dalam register segmen, bukan ke register alamat biasa. Ini memungkinkan model Forth 16 bit untuk mengakses memori megabyte 8086 secara efisien. Kelemahan utama dari threading segmen adalah kuotimasi frekuensi 16 byte pada ruang memori. Setiap kata Forth harus disesuaikan dengan batas 16 byte. Jika kata-kata Forth memiliki panjang acak, rata-rata 8 byte akan terbuang per kata Forth. ALOKASI PENDAFTARAN Di samping teknik threading, penggunaan register CPU adalah keputusan desain yang paling penting. Mungkin yang paling sulit. Ketersediaan register CPU dapat menentukan teknik threading apa yang dapat digunakan, dan bahkan peta memori apa yang akan menjadi The Classical Forth Register Model klasik Forth memiliki lima register bilangan bulat. Ini adalah entitas abstrak yang digunakan dalam operasi primitif Forth. NEXT, ENTER, dan EXIT didefinisikan sebelumnya dalam hal register abstrak ini. Masing-masing adalah satu sel lebar - yaitu di Forth 16 bit, ini adalah register 16 bit. (Ada pengecualian untuk aturan ini, seperti yang akan Anda lihat nanti.) Ini mungkin tidak semua register CPU. Jika CPU Anda tidak memiliki cukup register, beberapa di antaranya dapat disimpan di memori. Saya menggambarkannya sesuai urutan kepentingannya, yaitu bagian bawah daftar ini adalah kandidat terbaik untuk disimpan di memori. W adalah register kerja. Ini digunakan untuk banyak hal, termasuk referensi memori, jadi seharusnya register alamat misalnya Anda harus bisa mengambil dan menyimpan memori dengan menggunakan isi W sebagai alamatnya. Anda juga harus bisa melakukan aritmatika pada W. (Dalam DTC Forths, Anda juga harus bisa melompat secara tidak langsung menggunakan W.) W digunakan oleh penafsir dalam setiap kata di Forth. Dalam sebuah CPU yang hanya memiliki satu register, Anda akan menggunakannya untuk W dan menyimpan memori lainnya (dan sistemnya akan sangat lambat). IP adalah Interpreter Pointer. Ini digunakan oleh setiap kata Forth (melalui NEXT, ENTER, atau EXIT). IP harus berupa register alamat. Anda juga perlu untuk bisa kenaikan IP. Subroutine threaded Forths tidak memerlukan register ini. PSP adalah Parameter Stack (atau quotdata stackquot) Pointer, terkadang disebut hanya SP. Saya lebih suka PSP karena SP sering nama register CPU, dan mereka tidak boleh bingung. Sebagian besar kata KODE menggunakan ini. PSP harus berupa stack pointer, atau register alamat yang bisa bertambah dan decremented. Its juga plus jika Anda bisa melakukan pengalamatan terindeks dari PSP. RSP adalah Return Stack Pointer, terkadang disebut hanya RP. Ini digunakan oleh definisi kolon di ITC dan DTC Forths, dan dengan semua kata dalam STC Forths. RSP harus berupa stack pointer, atau register alamat yang bisa bertambah dan decremented. Jika memungkinkan. Letakkan W, IP, PSP, dan RSP di register. Register virtual yang mengikuti dapat disimpan dalam memori, namun biasanya ada keuntungan kecepatan untuk menyimpannya dalam register CPU. X adalah daftar kerja, bukan dianggap sebagai salah satu register Quantum kuotomatis, meskipun ITC Forth klasik membutuhkannya untuk tipuan kedua. Dalam ITC Anda harus bisa melompat secara tidak langsung menggunakan X. X juga bisa digunakan oleh beberapa kata KODE untuk melakukan aritmatika dan semacamnya. Hal ini sangat penting pada prosesor yang tidak dapat menggunakan memori sebagai operan. Misalnya, ADD on a Z80 mungkin (dalam pseudo-code) Kadang-kadang register kerja lain, Y, juga didefinisikan. UP adalah User Pointer, memegang alamat dasar area pengguna tugas. UP biasanya ditambahkan ke offset, dan digunakan oleh kode Forth tingkat tinggi, sehingga bisa disimpan di suatu tempat. Tetapi jika CPU bisa melakukan pengalamatan terindeks dari register UP, kata-kata KODE dapat lebih mudah dan cepat mengakses variabel pengguna. Jika Anda memiliki surplus register alamat, gunakan satu untuk UP. Tugas tunggal untuk tidak membutuhkan UP. X - jika diperlukan - lebih penting untuk tetap mendaftar daripada UP. UP adalah termudah dari register virtual Forth untuk pindah ke memori. Penggunaan Hardware Stack Sebagian besar CPU memiliki stack pointer sebagai bagian dari perangkat keras mereka, yang digunakan oleh interrupt dan subroutine calls. Bagaimana peta ini masuk ke register Forth Jika itu adalah PSP atau RSP Jawaban singkatnya adalah, itu tergantung. Dikatakan bahwa PSP digunakan lebih banyak daripada RSP di ITC dan DTC Forths. Jika CPU Anda memiliki beberapa register alamat, dan PUSH dan POP lebih cepat daripada referensi eksplisit, gunakan tumpukan perangkat keras sebagai Parameter Stack. Di sisi lain, jika CPU Anda kaya dalam mode pengalamatan - dan memungkinkan pengalamatan terindeks - ada nilai tambah dalam memiliki PSP sebagai register alamat tujuan umum. Dalam kasus ini, gunakan tumpukan perangkat keras sebagai Return Stack. Terkadang Anda tidak menggunakan tumpukan perangkat keras TMS320C25 hanya delapan sel dalam - semuanya tapi tidak berguna untuk Forth. Jadi tumpukan perangkat kerasnya hanya digunakan untuk interrupts, dan kedua PSP dan RSP adalah register alamat tujuan umum. (ANS Forth menentukan minimal 32 sel Parameter Stack dan 24 sel dari Return Stack I lebih memilih 64 sel dari masing-masing.) Anda kadang-kadang akan menemukan dogma yang ada di markas Parameter Stack hardware, atau quothust bequot the Return Stack. Sebagai gantinya, catat beberapa contoh primitif Forth, seperti dan lihat pendekatan mana yang lebih kecil atau lebih cepat. (DUP dan DROP, tidak ada ujian - biasanya sangat sepele.) Kadang-kadang Anda mencapai kesimpulan aneh Gary Bergstrom telah menunjukkan bahwa sebuah DTC Forth 6809 dapat dibuat beberapa siklus lebih cepat dengan menggunakan pointer stack pengguna 6809 sebagai IP NEXT menjadi POP. Dia menggunakan daftar indeks untuk salah satu tumpukan Forths. Top-Of-Stack in Register Kinerja lebih jauh dapat ditingkatkan dengan tetap menjaga elemen teratas Parameter Stack dalam daftar Banyak kata-kata Forth (seperti 0) maka jangan perlu menggunakan stack. Kata lain masih melakukan jumlah push dan pops yang sama, hanya di tempat yang berbeda dalam kode. Hanya beberapa kata-kata Forth (DROP dan 2DROP) menjadi lebih rumit, karena Anda tidak bisa lagi mengatur stack stack - Anda harus memperbarui daftar TOS juga. Ada beberapa peraturan saat menulis kata-kata KODE: Kata yang menghilangkan barang dari tumpukan harus memasukkan TOS kuotomatis ke dalam registernya. Sebuah kata yang menambahkan item pada stack harus mendorong TOS quotoldquot ke stack (kecuali, tentu saja, dikonsumsi oleh kata). Jika Anda memiliki setidaknya enam register CPU ukuran sel, saya sarankan untuk menyimpan TOS dalam daftar. Saya menganggap TOS lebih penting daripada UP untuk mendaftar, tapi kurang penting dibanding W, IP, PSP, dan RSP. (KL di register melakukan banyak fungsi register X). Berguna jika register ini bisa melakukan pengalamatan memori. PDP-11, Z8, dan 68000 adalah kandidat yang baik. Sembilan dari 19 PC IBM Forth yang dipelajari oleh Guy Kelly KEL92 tetap melakukan TOS dalam daftar. Saya pikir inovasi ini telah dilawan karena kepercayaan salah bahwa a) ia menambahkan instruksi, dan b) elemen tumpukan teratas harus dapat diakses sebagai memori. Ternyata bahkan kata-kata seperti PICK, ROLL, dan DEPTH sepele dimodifikasi untuk TOS-in-register. Bagaimana dengan menyangga dua elemen tumpukan di register Bila Anda menyimpan tumpukan teratas dalam daftar, jumlah total operasi yang dilakukan tetap pada dasarnya sama. Dorongan tetap mendorong, terlepas dari apakah sebelum atau sesudah operasi yang Anda lakukan. Di sisi lain, menyangga dua elemen tumpukan di register menambahkan sejumlah besar instruksi - dorongan menjadi dorongan diikuti dengan gerakan. Hanya prosesor Forth yang berdedikasi seperti RTX2000 dan kompiler pengoptimalan yang sangat pintar bisa mendapatkan keuntungan dari penyangga dua elemen tumpukan di register. Beberapa contoh Berikut adalah daftar tugas yang dibuat oleh Forths untuk sejumlah CPU yang berbeda. Cobalah untuk menyimpulkan keputusan desain penulis dari daftar ini. QuotSPquot mengacu pada stack stack perangkat keras. QuotZpagequot mengacu pada nilai yang tersimpan di halaman memori 6502, nol, yang hampir sama bergunanya - terkadang lebih berguna daripada - nilai yang disimpan dalam register mis. Mereka bisa digunakan untuk pengalamatan memori. QuotFixedquot berarti bahwa Paynes 8051 Forth memiliki area pengguna tunggal yang tak tergoyahkan, dan UP adalah konstanta dengan kode keras. Register Sempit Perhatikan sesuatu yang aneh dalam daftar sebelumnya The 6502 Forth - model 16-bit - menggunakan pointer 8-bit stack Hal ini dimungkinkan untuk membuat PSP, RSP, dan UP lebih kecil dari ukuran sel Forth. Ini karena tumpukan dan area pengguna keduanya merupakan bidang memori yang relatif kecil. Setiap tumpukan mungkin berukuran sekecil 64 sel, dan area pengguna jarang melebihi 128 sel. Anda hanya perlu memastikan bahwa a) area data ini terbatas pada area memori kecil, jadi alamat pendek dapat digunakan, atau b) bit alamat yang tinggi disediakan dengan cara lain, mis. Halaman memori pilih. Pada 6502, tumpukan perangkat keras terbatas pada halaman satu RAM (alamat 01xxh) oleh perancangan CPU. Penunjuk stack 8-bit dapat digunakan untuk Return Stack. Parameter Stack disimpan di halaman nol dari RAM, yang secara tidak langsung dapat diakses oleh register indeks 8-bit X. (Pertanyaan untuk siswa tingkat lanjut: mengapa menggunakan X 6502, dan bukan Y Petunjuk: lihat mode pengalamatan yang tersedia. ) Pada 8051, Anda dapat menggunakan register 8 bit R0 dan R1 untuk menangani RAM eksternal, asalkan Anda secara eksplisit mengeluarkan 8 bit alamat yang tinggi ke port 2. Ini memungkinkan sebuah quotpage selectquot untuk dua tumpukan. UP berbeda dengan PSP dan RSP: ini hanya menyediakan alamat dasar tidak pernah bertambah atau berkurang. Jadi praktis untuk memasok hanya bit tinggi dari register virtual ini. Bit yang rendah kemudian harus disediakan teknik pengindeksan apapun yang diindeks. Sebagai contoh, pada 6809, Anda dapat menggunakan register DP untuk menahan 8 bit UP tinggi, dan kemudian menggunakan pengalamatan Direct Page untuk mengakses salah satu dari 256 lokasi di halaman ini. Ini memaksa semua area pengguna untuk memulai pada alamat xx00h, yang tidak memiliki kesulitan besar, dan membatasi area pengguna menjadi 128 sel. Pada 8086, Anda dapat menggunakan daftar segmen untuk menentukan alamat dasar area pengguna. DAFTAR PUSTAKA CUR93a Curley, Charles, quotLife di FastForth Lane, menunggu publikasi di Forth Dimensions. Deskripsi dari 68000 subroutine-threaded Forth. CUR93b Curley, Charles, mengutip secara proporsional dalam BSRJSR Threaded Forth, menunggu publikasi di Forth Dimensions. Pengoptimalan kode single pass untuk FastForth, hanya dalam lima layar kode Termasuk daftar. KEL92 Kelly, Guy M. quotForth Systems Comparisons, quot Forth Dimensions XIII: 6 (MarApr 1992). Juga diterbitkan dalam Prosiding Konferensi FORTO 1991. Keduanya tersedia dari Forth Interest Group, P. O. Kotak 2154, Oakland, CA 94621. Menggambarkan rancangan pengorbanan banyak 8086 Forth dengan fragmen kode dan tolok ukur - KOG82 Kogge yang sangat dianjurkan, Peter M. quotAn Trail Arsitektur untuk Sistem Threaded - Code, quot IEEE Computer, vol. 15 tidak 3 (Mar 1982). Tetap deskripsi definitif berbagai teknik threading. ROD91 Rodriguez, B. J. quotB. Y.O. Assembler, quot Bagian 1, Jurnal Komputer 52 (SepOct 1991). Prinsip umum penulisan perwira Forth. ROD92 Rodriguez, B. J. quotB. Y.O. Assembler, quot Bagian 2, Jurnal Komputer 54 (JanFeb 1992). Perakit 6809 di Forth. SCO89 Scott, Andrew, quotAn Pengoptimal Ekstensi untuk Kompilasi Forth, 1989 Prosiding FORML Prosiding. Forth Interest Group, P. O. Kotak 2154, Oakland, CA 94621. Deskripsi bagus tentang pengoptimasi 68000 tidak ada kode yang tersedia. CUR86 Curley, Charles, real-Forth untuk 68.000. Didistribusikan secara pribadi (1986). JAM80 James, John S. fig-Forth untuk PDP-11. Forth Interest Group (1980). KUN81 Kuntze, Robert E. MVP-Forth untuk Apple II. Mountain View Press (1981). LAX84 Laxen, H. dan Perry, M. F83 untuk PC IBM. Versi 2.1.0 (1984). Didistribusikan oleh penulis, tersedia dari Forth Interest Group atau GEnie. LOE81 Loeliger, R. G. Threaded Interpretive Languages. Publikasi BYTE (1981), ISBN 0-07-038360-X. Mungkin satu-satunya buku yang pernah ditulis tentang pembuatan kernel seperti Forth (contoh yang digunakan adalah Z80). Patutlah jika Anda bisa menemukan salinannya. MPE92 MicroProcessor Engineering Ltd MPE Z8Super8 PowerForth Target. MPE Ltd 133 Hill Lane, Shirley, Southampton, S01 5AF, Inggris (Juni 1992). Produk komersial PAY90 Payne, William H. Embedded Controller FORTH untuk keluarga 8.051. Academic Press (1990), ISBN 0-12-547570-5. Ini adalah kuotot yang lengkap untuk Forth 8051, termasuk metacompiler untuk PC IBM. File hardcopy saja bisa diunduh dari GEnie. Bukan untuk Sersan SER90 pemula, Frank, Pygmy Forth untuk PC IBM. Versi 1.3 (1990). Didistribusikan oleh penulis, tersedia dari Forth Interest Group. Versi 1.4 sekarang tersedia di GEnie, dan sepadan dengan usaha ekstra untuk mendapatkan. TAL80 Talbot, R. J. fig-Forth untuk tahun 6809. Forth Interest Group (1980). Catatan penulis untuk publikasi web: file yang sebelumnya tersedia di layanan online GEnie sekarang tersedia dari server FTP Kelompok Minat Minoritas, ftp: ftp. forth. orgpubForth. SIFAT KODE Sifat Pengenalan Kode Saya dua orang dengan alam. Woody Allen Inilah kita: awal. Hampir awal. Jika sudah lama sejak Anda menyelesaikan pemrograman di Processing (atau matematika, dalam hal ini), pendahuluan ini akan membawa pikiran Anda kembali ke pemikiran komputasi sebelum kita mendekati beberapa materi yang lebih sulit dan kompleks. Pada Bab 1, akan membahas tentang konsep vektor dan bagaimana hal itu akan berfungsi sebagai blok bangunan untuk simulasi gerak sepanjang buku ini. Tapi sebelum kita mengambil langkah itu, mari kita pikirkan apa artinya sesuatu untuk sekadar bergerak di sekitar layar. Mari kita mulai dengan salah satu simulasi motion motion yang paling terkenal dan paling sederhana. I.1 Jalan Acak Bayangkan Anda berdiri di tengah balok keseimbangan. Setiap sepuluh detik, Anda membalik koin. Kepala, maju selangkah. Ekor, mundur selangkah. Ini adalah jalur walka acak yang didefinisikan sebagai rangkaian langkah acak. Stepping off that balance beam and onto the floor, you could perform a random walk in two dimensions by flipping that same coin twice with the following results: Yes, this may seem like a particularly unsophisticated algorithm. Nevertheless, random walks can be used to model phenomena that occur in the real world, from the movements of molecules in a gas to the behavior of a gambler spending a day at the casino. As for us, we begin this book studying a random walk with three goals in mind. We need to review a programming concept central to this bookobject-oriented programming. The random walker will serve as a template for how we will use object-oriented design to make things that move around a Processing window. The random walk instigates the two questions that we will ask over and over again throughout this book: How do we define the rules that govern the behavior of our objects and then, How do we implement these rules in Processing Throughout the book, well periodically need a basic understanding of randomness, probability, and Perlin noise. The random walk will allow us to demonstrate a few key points that will come in handy later. I.2 The Random Walker Class Lets review a bit of object-oriented programming (OOP) first by building a Walker object. This will be only a cursory review. If you have never worked with OOP before, you may want something more comprehensive Id suggest stopping here and reviewing the basics on the Processing website before continuing. An object in Processing is an entity that has both data and functionality. We are looking to design a Walker object that both keeps track of its data (where it exists on the screen) and has the capability to perform certain actions (such as draw itself or take a step). A class is the template for building actual instances of objects. Think of a class as the cookie cutter the objects are the cookies themselves. Lets begin by defining the Walker classwhat it means to be a Walker object. The Walker only needs two pieces of dataa number for its x-location and one for its y-location. Objects have data. Since we only draw the background once in setup(). rather than clearing it continually each time through draw(). we see the trail of the random walk in our Processing window. Your browser does not support the canvas tag. There are a couple improvements we could make to the random walker. For one, this Walker s step choices are limited to four optionsup, down, left, and right. But any given pixel in the window has eight possible neighbors, and a ninth possibility is to stay in the same place. To implement a Walker object that can step to any neighboring pixel (or stay put), we could pick a number between 0 and 8 (nine possible choices). However, a more efficient way to write the code would be to simply pick from three possible steps along the x-axis (-1, 0, or 1) and three possible steps along the y-axis. Yields -1, 0, or 1 All of these variations on the traditional random walk have one thing in common: at any moment in time, the probability that the Walker will take a step in a given direction is equal to the probability that the Walker will take a step in any direction. In other words, if there are four possible steps, there is a 1 in 4 (or 25) chance the Walker will take any given step. With nine possible steps, its a 1 in 9 (or 11.1) chance. Conveniently, this is how the random() function works. Processings random number generator (which operates behind the scenes) produces what is known as a uniform distribution of numbers. We can test this distribution with a Processing sketch that counts each time a random number is picked and graphs it as the height of a rectangle. Your browser does not support the canvas tag. Example I.2: Random number distribution An array to keep track of how often random numbers are picked Pick a random number and increase the count. Graphing the results The above screenshot shows the result of the sketch running for a few minutes. Notice how each bar of the graph differs in height. Our sample size (i. e. the number of random numbers weve picked) is rather small and there are some occasional discrepancies, where certain numbers are picked more often. Over time, with a good random number generator, this would even out. Pseudo-Random Numbers The random numbers we get from the random() function are not truly random therefore they are known as pseudo-random. They are the result of a mathematical function that simulates randomness. This function would yield a pattern over time, but that time period is so long that for us, its just as good as pure randomness Exercise I.1 Create a random walker that has a tendency to move down and to the right. (Well see the solution to this in the next section.) I.3 Probability and Non-Uniform Distributions Remember when you first started programming in Processing Perhaps you wanted to draw a lot of circles on the screen. So you said to yourself: Oh, I know. Ill draw all these circles at random locations, with random sizes and random colors. In a computer graphics system, its often easiest to seed a system with randomness. In this book, however, were looking to build systems modeled on what we see in nature. Defaulting to randomness is not a particularly thoughtful solution to a design problemin particular, the kind of problem that involves creating an organic or natural-looking simulation. With a few tricks, we can change the way we use random() to produce non-uniform distributions of random numbers. This will come in handy throughout the book as we look at a number of different scenarios. When we examine genetic algorithms, for example, well need a methodology for performing selectionwhich members of our population should be selected to pass their DNA to the next generation Remember the concept of survival of the fittest Lets say we have a population of monkeys evolving. Not every monkey will have a equal chance of reproducing. To simulate Darwinian evolution, we cant simply pick two random monkeys to be parents. We need the more fit ones to be more likely to be chosen. We need to define the probability of the fittest. For example, a particularly fast and strong monkey might have a 90 chance of procreating, while a weaker one has only a 10 chance. Lets pause here and take a look at probabilitys basic principles. First well examine single event probability, i. e. the likelihood that a given event will occur. If you have a system with a certain number of possible outcomes, the probability of the occurrence of a given event equals the number of outcomes that qualify as that event divided by the total number of all possible outcomes. A coin toss is a simple exampleit has only two possible outcomes, heads or tails. There is only one way to flip heads. The probability that the coin will turn up heads, therefore, is one divided by two: 12 or 50. Take a deck of fifty-two cards. The probability of drawing an ace from that deck is: number of aces number of cards 4 52 0.077 The probability of drawing a diamond is: number of diamonds number of cards 13 52 0.25 25 We can also calculate the probability of multiple events occurring in sequence. To do this, we simply multiply the individual probabilities of each event. The probability of a coin turning up heads three times in a row is: (12) (12) (12) 18 (or 0.125) meaning that a coin will turn up heads three times in a row one out of eight times (each time being three tosses). Exercise I.2 What is the probability of drawing two aces in a row from a deck of fifty-two cards There are a couple of ways in which we can use the random() function with probability in code. One technique is to fill an array with a selection of numberssome of which are repeatedthen choose random numbers from that array and generate events based on those choices. 1 is stored in the array twice, making it more likely to be picked. Exercise I.3 Create a random walker with dynamic probabilities. For example, can you give it a 50 chance of moving in the direction of the mouse I.4 A Normal Distribution of Random Numbers Lets go back to that population of simulated Processing monkeys. Your program generates a thousand Monkey objects, each with a height value between 200 and 300 (as this is a world of monkeys that have heights between 200 and 300 pixels). Does this accurately depict the heights of real-world beings Think of a crowded sidewalk in New York City. Pick any person off the street and it may appear that their height is random. Nevertheless, its not the kind of random that random() produces. Peoples heights are not uniformly distributed there are a great deal more people of average height than there are very tall or very short ones. To simulate nature, we may want it to be more likely that our monkeys are of average height (250 pixels), yet still allow them to be, on occasion, very short or very tall. A distribution of values that cluster around an average (referred to as the mean) is known as a normal distribution. It is also called the Gaussian distribution (named for mathematician Carl Friedrich Gauss) or, if you are French, the Laplacian distribution (named for Pierre-Simon Laplace). Both mathematicians were working concurrently in the early nineteenth century on defining such a distribution. When you graph the distribution, you get something that looks like the following, informally known as a bell curve: The curve is generated by a mathematical function that defines the probability of any given value occurring as a function of the mean (often written as , the Greek letter mu ) and standard deviation (, the Greek letter sigma ). The mean is pretty easy to understand. In the case of our height values between 200 and 300, you probably have an intuitive sense of the mean (i. e. average) as 250. However, what if I were to say that the standard deviation is 3 or 15 What does this mean for the numbers The graphs above should give us a hint. The graph on the left shows us the distribution with a very low standard deviation, where the majority of the values cluster closely around the mean. The graph on the right shows us a higher standard deviation, where the values are more evenly spread out from the average. The numbers work out as follows: Given a population, 68 of the members of that population will have values in the range of one standard deviation from the mean, 98 within two standard deviations, and 99.7 within three standard deviations. Given a standard deviation of 5 pixels, only 0.3 of the monkey heights will be less than 235 pixels (three standard deviations below the mean of 250) or greater than 265 pixels (three standard deviations above the mean of 250). Calculating Mean and Standard Deviation Consider a class of ten students who receive the following scores (out of 100) on a test: 85, 82, 88, 86, 85, 93, 98, 40, 73, 83 The standard deviation is calculated as the square root of the average of the squares of deviations around the mean. In other words, take the difference from the mean for each person and square it (variance). Calculate the average of all these values and take the square root as the standard deviation. The standard deviation is the square root of the average variance: 15.13 Luckily for us, to use a normal distribution of random numbers in a Processing sketch, we dont have to do any of these calculations ourselves. Instead, we can make use of a class known as Random. which we get for free as part of the default Java libraries imported into Processing (see the JavaDocs for more information). To use the Random class, we must first declare a variable of type Random and create the Random object in setup() . We use the variable name generator because what we have here can be thought of as a random number generator. If we want to produce a random number with a normal (or Gaussian) distribution each time we run through draw(). its as easy as calling the function nextGaussian() . Asking for a Gaussian random number. (Note nextGaussian() returns a double and must be converted to float.) Heres the thing. What are we supposed to do with this value What if we wanted to use it, for example, to assign the x-position of a shape we draw on screen The nextGaussian() function returns a normal distribution of random numbers with the following parameters: a mean of zero and a standard deviation of one . Lets say we want a mean of 320 (the center horizontal pixel in a window of width 640) and a standard deviation of 60 pixels. We can adjust the value to our parameters by multiplying it by the standard deviation and adding the mean. Your browser does not support the canvas tag. Example I.4: Gaussian distribution Note that nextGaussian() returns a double. Multiply by the standard deviation and add the mean. By drawing the ellipses on top of each other with some transparency, we can actually see the distribution. The brightest spot is near the center, where most of the values cluster, but every so often circles are drawn farther to the right or left of the center. Exercise I.4 Consider a simulation of paint splatter drawn as a collection of colored dots. Most of the paint clusters around a central location, but some dots do splatter out towards the edges. Can you use a normal distribution of random numbers to generate the locations of the dots Can you also use a normal distribution of random numbers to generate a color palette Exercise I.5 A Gaussian random walk is defined as one in which the step size (how far the object moves in a given direction) is generated with a normal distribution. Implement this variation of our random walk. I.5 A Custom Distribution of Random Numbers There will come a time in your life when you do not want a uniform distribution of random values, or a Gaussian one. Lets imagine for a moment that you are a random walker in search of food. Moving randomly around a space seems like a reasonable strategy for finding something to eat. After all, you dont know where the food is, so you might as well search randomly until you find it. The problem, as you may have noticed, is that random walkers return to previously visited locations many times (this is known as oversampling). One strategy to avoid such a problem is to, every so often, take a very large step. This allows the walker to forage randomly around a specific location while periodically jumping very far away to reduce the amount of oversampling. This variation on the random walk (known as a Lvy flight) requires a custom set of probabilities. Though not an exact implementation of a Lvy flight, we could state the probability distribution as follows: the longer the step, the less likely it is to be picked the shorter the step, the more likely. Earlier in this prologue, we saw that we could generate custom probability distributions by filling an array with values (some duplicated so that they would be picked more frequently) or by testing the result of random(). We could implement a Lvy flight by saying that there is a 1 chance of the walker taking a large step. A 1 chance of taking a large step However, this reduces the probabilities to a fixed number of options. What if we wanted to make a more general rulethe higher a number, the more likely it is to be picked 3.145 would be more likely to be picked than 3.144, even if that likelihood is just a tiny bit greater. In other words, if x is the random number, we could map the likelihood on the y-axis with y x . If we can figure out how to generate a distribution of random numbers according to the above graph, then we will be able to apply the same methodology to any curve for which we have a formula. One solution is to pick two random numbers instead of one. The first random number is just that, a random number. The second one, however, is what well call a qualifying random value. It will tell us whether to use the first one or throw it away and pick another one. Numbers that have an easier time qualifying will be picked more often, and numbers that rarely qualify will be picked infrequently. Here are the steps (for now, lets consider only random values between 0 and 1): Pick a random number: R1 Compute a probability P that R1 should qualify. Lets try: P R1. Pick another random number: R2 If R2 is less than P, then we have found our numberR1 If R2 is not less than P, go back to step 1 and start over. Here we are saying that the likelihood that a random value will qualify is equal to the random number itself. Lets say we pick 0.1 for R1. This means that R1 will have a 10 chance of qualifying. If we pick 0.83 for R1 then it will have a 83 chance of qualifying. The higher the number, the greater the likelihood that we will actually use it. Here is a function (named for the Monte Carlo method, which was named for the Monte Carlo casino) that implements the above algorithm, returning a random value between 0 and 1. We do this forever until we find a qualifying random value. Exercise I.6 Use a custom probability distribution to vary the size of a step taken by the random walker. The step size can be determined by influencing the range of values picked. Can you map the probability exponentiallyi. e. making the likelihood that a value is picked equal to the value squared A uniform distribution of step sizes. Change this (Later well see how to do this more efficiently using vectors.) I.6 Perlin Noise (A Smoother Approach) A good random number generator produces numbers that have no relationship and show no discernible pattern. As we are beginning to see, a little bit of randomness can be a good thing when programming organic, lifelike behaviors. However, randomness as the single guiding principle is not necessarily natural. An algorithm known as Perlin noise, named for its inventor Ken Perlin, takes this concept into account. Perlin developed the noise function while working on the original Tron movie in the early 1980s it was designed to create procedural textures for computer-generated effects. In 1997 Perlin won an Academy Award in technical achievement for this work. Perlin noise can be used to generate various effects with natural qualities, such as clouds, landscapes, and patterned textures like marble. Perlin noise has a more organic appearance because it produces a naturally ordered (smooth) sequence of pseudo-random numbers. The graph on the left below shows Perlin noise over time, with the x-axis representing time note the smoothness of the curve. The graph on the right shows pure random numbers over time. (The code for generating these graphs is available in the accompanying book downloads.) Figure I.5: Noise Figure I.6: Random Processing has a built-in implementation of the Perlin noise algorithm: the function noise(). The noise() function takes one, two, or three arguments, as noise is computed in one, two, or three dimensions. Lets start by looking at one-dimensional noise. Noise Detail The Processing noise reference tells us that noise is calculated over several octaves. Calling the noiseDetail() function will change both the number of octaves and their importance relative to one another. This in turn changes how the noise function behaves. An online lecture by Ken Perlin lets you learn more about how noise works from Perlin himself . Consider drawing a circle in our Processing window at a random x-location. A random x-location Now, instead of a random x-location, we want a Perlin noise x-location that is smoother. You might think that all you need to do is replace random() with noise(). i. e. A noise x-location While conceptually this is exactly what we want to docalculate an x-value that ranges between 0 and the width according to Perlin noisethis is not the correct implementation. While the arguments to the random() function specify a range of values between a minimum and a maximum, noise() does not work this way. Instead, the output range is fixedit always returns a value between 0 and 1. Well see in a moment that we can get around this easily with Processings map() function, but first we must examine what exactly noise() expects us to pass in as an argument. We can think of one-dimensional Perlin noise as a linear sequence of values over time. For example: How quickly we increment t also affects the smoothness of the noise. If we make large jumps in time, then we are skipping ahead and the values will be more random. Try running the code several times, incrementing t by 0.01, 0.02, 0.05, 0.1, 0.0001, and you will see different results. Mapping Noise Now were ready to answer the question of what to do with the noise value. Once we have the value with a range between 0 and 1, its up to us to map that range to what we want. The easiest way to do this is with Processings map() function. The map() function takes five arguments. First up is the value we want to map, in this case n. Then we have to give it the values current range (minimum and maximum), followed by our desired range. In this case, we know that noise has a range between 0 and 1, but wed like to draw our circle with a range between 0 and the windows width. Notice how the above example requires an additional pair of variables: tx and ty. This is because we need to keep track of two time variables, one for the x-location of the Walker object and one for the y-location. But there is something a bit odd about these variables. Why does tx start at 0 and ty at 10,000 While these numbers are arbitrary choices, we have very specifically initialized our two time variables with different values. This is because the noise function is deterministic: it gives you the same result for a specific time t each and every time. If we asked for the noise value at the same time t for both x and y. then x and y would always be equal, meaning that the Walker object would only move along a diagonal. Instead, we simply use two different parts of the noise space, starting at 0 for x and 10,000 for y so that x and y can appear to act independently of each other. In truth, there is no actual concept of time at play here. Its a useful metaphor to help us understand how the noise function works, but really what we have is space, rather than time. The graph above depicts a linear sequence of noise values in a one-dimensional space, and we can ask for a value at a specific x-location whenever we want. In examples, you will often see a variable named xoff to indicate the x-offset along the noise graph, rather than t for time (as noted in the diagram). Exercise I.7 In the above random walker, the result of the noise function is mapped directly to the Walker s location. Create a random walker where you instead map the result of the noise() function to a Walker s step size. Two-Dimensional Noise This idea of noise values living in a one-dimensional space is important because it leads us right into a discussion of two-dimensional space. Lets think about this for a moment. With one-dimensional noise, we have a sequence of values in which any given value is similar to its neighbor. Because the value is in one dimension, it only has two neighbors: a value that comes before it (to the left on the graph) and one that comes after it (to the right). Figure I.10: 1D Noise Figure I.11: 2D Noise Two-dimensional noise works exactly the same way conceptually. The difference of course is that we arent looking at values along a linear path, but values that are sitting on a grid. Think of a piece of graph paper with numbers written into each cell. A given value will be similar to all of its neighbors: above, below, to the right, to the left, and along any diagonal. If you were to visualize this graph paper with each value mapped to the brightness of a color, you would get something that looks like clouds. White sits next to light gray, which sits next to gray, which sits next to dark gray, which sits next to black, which sits next to dark gray, etc. This is why noise was originally invented. You tweak the parameters a bit or play with color to make the resulting image look more like marble or wood or any other organic texture. Lets take a quick look at how to implement two-dimensional noise in Processing. If you wanted to color every pixel of a window randomly, you would need a nested loop, one that accessed each pixel and picked a random brightness. A random brightness To color each pixel according to the noise() function, well do exactly the same thing, only instead of calling random() well call noise() . A Perlin noise brightness This is a nice start conceptuallyit gives you a noise value for every ( x. y ) location in our two-dimensional space. The problem is that this wont have the cloudy quality we want. Jumping from pixel 200 to pixel 201 is too large of a jump through noise. Remember, when we worked with one-dimensional noise, we incremented our time variable by 0.01 each frame, not by 1 A pretty good solution to this problem is to just use different variables for the noise arguments. For example, we could increment a variable called xoff each time we move horizontally, and a yoff variable each time we move vertically through the nested loops. Example I.6: 2D Perlin noise Start xoff at 0. For every xoff, start yoff at 0.

No comments:

Post a Comment