Seperti yang dijelaskan judulnya, saya memiliki pertanyaan pemrograman yang sangat mendasar yang belum bisa saya bahas. Menyaring semua (sangat pintar) "Untuk memahami rekursi, Anda harus terlebih dahulu memahami rekursi." balasan dari berbagai utas online saya masih belum cukup mengerti.
Memahami bahwa ketika dihadapkan dengan ketidaktahuan tentang apa yang tidak kita ketahui, kita cenderung mengajukan pertanyaan yang salah atau mengajukan pertanyaan yang benar secara tidak benar. Saya akan membagikan apa yang saya "pikirkan" pertanyaan saya dengan harapan seseorang dengan pandangan yang sama dapat berbagi beberapa sedikit pengetahuan yang akan membantu menyalakan bola lampu rekursif untuk saya!
Berikut fungsinya (sintaksnya ditulis di Swift):
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a: a + 1, b: b)
}
}
Kami akan menggunakan 2 dan 5 sebagai argumen kami:
println(sumInts(a: 2, b: 5))
Jelas jawabannya adalah 14. Tapi saya tidak mengerti bagaimana nilai itu dicapai.
Ini adalah 2 hangup saya:
Fungsi tersebut dipanggil secara rekursif hingga suatu kondisi terpenuhi. Kondisi itu adalah a> b. Ketika kondisi ini terpenuhi, kembalikan 0. Pada pandangan pertama, saya mengharapkan nilai kembali menjadi 0 yang jelas salah.
Mencetak nilai 'a' pada setiap iterasi menghasilkan nilai yang saya harapkan: 2, 3, 4, 5 (di mana titik 5 + 1> b yang memenuhi syarat pertama: a> b) tetapi saya tetap tidak ' t melihat bagaimana nilai 14 dicapai.
Pikiran pertama saya adalah bahwa sesuatu yang mirip dengan berikut ini terjadi secara ajaib:
var answer = a;
answer += a+1 until a > b;
return answer;
Jadi mengesampingkan sihir, saya hanya tidak mendapatkan sesuatu. Saya ingin sekali memahami apa yang terjadi lebih dari sekadar secara implisit.
Jika seseorang dapat dengan ramah menjelaskan apa yang secara teknis terjadi selama fungsi semacam ini dan mengapa hasilnya tidak 0 dan bagaimana, pada akhirnya a + sumInts(a: a + 1, b: b) = 14
, saya akan selamanya berhutang budi.
sumber
LearnYouARecursion
, set soal lengkap dari profesor kelas dunia!Jawaban:
Saya pikir kebingungan ini berasal dari anggapan bahwa "fungsi yang sama" dipanggil berkali-kali. Jika Anda menganggapnya sebagai "banyak salinan dari fungsi yang sama yang dipanggil", mungkin akan lebih jelas:
Hanya satu salinan fungsi yang pernah mengembalikan 0, dan ini bukan yang pertama (ini yang terakhir). Jadi hasil pemanggilan yang pertama bukan 0.
Untuk kebingungan kedua, saya pikir akan lebih mudah mengeja rekursi dalam bahasa Inggris. Baca baris ini:
sebagai "kembalikan nilai 'a' plus (nilai kembalian dari salinan lain dari fungsi tersebut, yang merupakan nilai salinan 'a' plus (nilai kembalian dari salinan lain dari fungsi tersebut, yang merupakan nilai salinan kedua dari ' a 'plus (... ", dengan setiap salinan dari fungsi tersebut menghasilkan salinan baru dari dirinya sendiri dengan peningkatan sebesar 1, hingga kondisi a> b terpenuhi.
Pada saat Anda mencapai kondisi a> b menjadi true, Anda memiliki tumpukan salinan fungsi yang (berpotensi secara sewenang-wenang) yang panjang, semuanya sedang dijalankan, semua menunggu hasil salinan berikutnya untuk mencari tahu apa yang mereka lakukan. harus menambahkan 'a'.
(edit: juga, sesuatu yang harus diperhatikan adalah bahwa tumpukan salinan dari fungsi yang saya sebutkan adalah hal nyata yang memakan memori nyata, dan akan merusak program Anda jika terlalu besar. Kompiler dapat mengoptimalkannya di beberapa kasus, tetapi ruang tumpukan yang melelahkan adalah batasan fungsi rekursif yang signifikan dan tidak menguntungkan dalam banyak bahasa)
sumber
a
danb
.Inilah yang akan dipikirkan oleh komputasi komputer
sumInts(2,5)
jika mampu:Seperti yang Anda lihat, beberapa panggilan ke fungsi
sumInts
sebenarnya mengembalikan 0 namun ini bukan nilai akhir karena komputer masih harus menambahkan 5 ke 0 itu, lalu 4 ke hasilnya, lalu 3, lalu 2, seperti yang dijelaskan oleh empat kalimat terakhir dari pikiran komputer kita. Perhatikan bahwa dalam rekursi, komputer tidak hanya harus menghitung panggilan rekursif, tetapi juga harus mengingat apa yang harus dilakukan dengan nilai yang dikembalikan oleh panggilan rekursif. Ada area khusus dalam memori komputer yang disebut tumpukan di mana jenis informasi ini disimpan, ruang ini terbatas dan fungsi yang terlalu rekursif dapat menghabiskan tumpukan: ini adalah luapan tumpukan yang memberikan namanya ke situs web favorit kami.Pernyataan Anda tampaknya membuat asumsi implisit bahwa komputer melupakan apa yang terjadi saat melakukan panggilan rekursif, tetapi ternyata tidak, inilah mengapa kesimpulan Anda tidak sesuai dengan pengamatan Anda.
Ini karena nilai yang dikembalikan bukan
a
dirinya sendiri tetapi jumlah dari nilaia
dan nilai yang dikembalikan oleh panggilan rekursif.sumber
sumInts
sehingga benar-benar menuliskan “pengalaman komputer”. Setelah Anda menulis satu tangan dari fungsi-fungsi tersebut, Anda mungkin sudah "mengerti"!Untuk memahami rekursi, Anda harus memikirkan masalahnya dengan cara yang berbeda. Alih-alih urutan logis besar langkah-langkah yang masuk akal secara keseluruhan, Anda malah mengambil masalah besar dan memecah menjadi masalah yang lebih kecil dan menyelesaikannya, setelah Anda memiliki jawaban untuk sub masalah Anda menggabungkan hasil dari sub masalah untuk membuat solusi untuk masalah yang lebih besar. Pikirkan Anda dan teman Anda perlu menghitung jumlah kelereng dalam ember besar. Anda masing-masing mengambil ember yang lebih kecil dan menghitungnya satu per satu dan setelah selesai Anda menjumlahkan totalnya .. Nah sekarang jika masing-masing dari Anda menemukan beberapa teman dan membagi ember lebih jauh, maka Anda hanya perlu menunggu teman-teman yang lain ini untuk cari tahu totalnya, berikan kembali kepada Anda masing-masing, Anda menambahkannya. Dan seterusnya.
Anda harus ingat setiap kali fungsi memanggil dirinya sendiri secara rekursif, ia membuat konteks baru dengan subset masalah, setelah bagian tersebut diselesaikan, ia akan dikembalikan sehingga iterasi sebelumnya dapat diselesaikan.
Mari saya tunjukkan langkah-langkahnya:
setelah penjumlahan (a: 6, b: 5) telah dieksekusi, hasilnya dapat dihitung sehingga kembali ke rantai dengan hasil yang Anda dapatkan:
Cara lain untuk merepresentasikan struktur rekursi:
sumber
Rekursi adalah topik yang sulit untuk dipahami dan saya rasa saya tidak dapat sepenuhnya melakukannya dengan adil di sini. Sebaliknya, saya akan mencoba untuk fokus pada bagian kode tertentu yang Anda miliki di sini dan mencoba menjelaskan intuisi mengapa solusi tersebut bekerja dan mekanisme bagaimana kode tersebut menghitung hasilnya.
Kode yang Anda berikan di sini memecahkan masalah berikut: Anda ingin mengetahui jumlah semua bilangan bulat dari a hingga b, inklusif. Sebagai contoh, Anda ingin menjumlahkan angka dari 2 hingga 5, inklusif, yaitu
Saat mencoba memecahkan masalah secara rekursif, salah satu langkah pertama yang harus dilakukan adalah mencari cara untuk memecah masalah menjadi masalah yang lebih kecil dengan struktur yang sama. Jadi misalkan Anda ingin menjumlahkan angka dari 2 menjadi 5, inklusif. Salah satu cara untuk menyederhanakannya adalah dengan memperhatikan bahwa jumlah di atas dapat ditulis ulang sebagai
Di sini, (3 + 4 + 5) kebetulan merupakan jumlah dari semua bilangan bulat antara 3 dan 5, inklusif. Dengan kata lain, jika Anda ingin mengetahui jumlah semua bilangan bulat antara 2 dan 5, mulailah dengan menghitung jumlah semua bilangan bulat antara 3 dan 5, lalu tambahkan 2.
Jadi, bagaimana Anda menghitung jumlah semua bilangan bulat antara 3 dan 5, inklusif? Nah, jumlah itu
yang bisa dianggap sebagai gantinya
Di sini, (4 + 5) adalah jumlah dari semua bilangan bulat antara 4 dan 5, inklusif. Jadi, jika Anda ingin menghitung jumlah semua angka antara 3 dan 5, inklusif, Anda akan menghitung jumlah semua bilangan bulat antara 4 dan 5, lalu menambahkan 3.
Ada pola di sini! Jika Anda ingin menghitung jumlah bilangan bulat antara a dan b, inklusif, Anda dapat melakukan hal berikut. Pertama, hitung jumlah bilangan bulat antara a + 1 dan b, inklusif. Selanjutnya, tambahkan ke total itu. Anda akan melihat bahwa "menghitung jumlah bilangan bulat antara a + 1 dan b, inklusif" kebetulan merupakan jenis masalah yang hampir sama yang sudah kita coba selesaikan, tetapi dengan parameter yang sedikit berbeda. Daripada menghitung dari a ke b, inklusif, kami menghitung dari a + 1 ke b, inklusif. Itulah langkah rekursif - untuk memecahkan masalah yang lebih besar ("jumlah dari a ke b, inklusif"), kita mengurangi masalah ke versi yang lebih kecil dari dirinya sendiri ("jumlah dari a + 1 ke b, inklusif.").
Jika Anda melihat kode yang Anda miliki di atas, Anda akan melihat bahwa ada langkah ini di dalamnya:
Kode ini hanyalah terjemahan dari logika di atas - jika Anda ingin menjumlahkan dari a ke b, inklusif, mulailah dengan menjumlahkan a + 1 ke b, inklusif (itulah panggilan rekursif ke
sumInt
s), lalu tambahkana
.Tentu saja, dengan sendirinya pendekatan ini tidak akan berhasil. Misalnya, bagaimana Anda menghitung jumlah semua bilangan bulat antara 5 dan 5 inklusif? Nah, dengan menggunakan logika kita saat ini, Anda akan menghitung jumlah semua bilangan bulat antara 6 dan 5, inklusif, lalu menambahkan 5. Jadi bagaimana Anda menghitung jumlah semua bilangan bulat antara 6 dan 5, inklusif? Nah, menggunakan logika kita saat ini, Anda akan menghitung jumlah semua bilangan bulat antara 7 dan 5, inklusif, lalu menambahkan 6. Anda akan melihat masalah di sini - ini terus berjalan dan berlangsung!
Dalam pemecahan masalah rekursif, perlu ada beberapa cara untuk berhenti menyederhanakan masalah dan sebaliknya hanya menyelesaikannya secara langsung. Biasanya, Anda akan menemukan kasus sederhana di mana jawabannya dapat ditentukan dengan segera, kemudian susun solusi Anda untuk menyelesaikan kasus sederhana secara langsung saat muncul. Ini biasanya disebut kasus dasar atau basis rekursif .
Jadi apa kasus dasar dalam masalah khusus ini? Saat Anda menjumlahkan bilangan bulat dari a ke b, inklusif, jika a ternyata lebih besar dari b, maka jawabannya adalah 0 - tidak ada angka dalam rentang tersebut! Oleh karena itu, kami akan menyusun solusi kami sebagai berikut:
Sekarang, bandingkan pseudocode ini dengan kode Anda yang sebenarnya:
Perhatikan bahwa hampir ada peta satu-ke-satu antara solusi yang diuraikan dalam kodesemu dan kode aktual ini. Langkah pertama adalah kasus dasar - jika Anda meminta jumlah rentang angka kosong, Anda mendapatkan 0. Jika tidak, hitung jumlah antara a + 1 dan b, lalu tambahkan a.
Sejauh ini, saya hanya memberikan ide tingkat tinggi di balik kode. Tapi Anda punya dua pertanyaan lain yang sangat bagus. Pertama, mengapa ini tidak selalu mengembalikan 0, mengingat fungsinya mengatakan mengembalikan 0 jika a> b? Kedua, dari mana sebenarnya ke-14 itu berasal? Mari kita lihat ini secara bergantian.
Mari kita coba kasus yang sangat, sangat sederhana. Apa yang terjadi jika Anda menelepon
sumInts(6, 5)
? Dalam kasus ini, menelusuri kode, Anda melihat bahwa fungsinya hanya mengembalikan 0. Itu hal yang benar untuk dilakukan, untuk - tidak ada angka dalam rentang. Sekarang, coba sesuatu yang lebih keras. Apa yang terjadi saat Anda meneleponsumInts(5, 5)
? Nah, inilah yang terjadi:sumInts(5, 5)
. Kami jatuh keelse
cabang, yang mengembalikan nilai `a + sumInts (6, 5).sumInts(5, 5)
menentukan apasumInts(6, 5)
itu, kita perlu menghentikan apa yang sedang kita lakukan dan meneleponsumInts(6, 5)
.sumInts(6, 5)
dipanggil. Ini memasukiif
cabang dan kembali0
. Namun, contohsumInts
ini dipanggil olehsumInts(5, 5)
, sehingga nilai yang dikembalikan dikomunikasikan kembali kesumInts(5, 5)
, bukan ke pemanggil tingkat atas.sumInts(5, 5)
sekarang dapat menghitung5 + sumInts(6, 5)
untuk kembali5
. Kemudian mengembalikannya ke pemanggil tingkat atas.Perhatikan bagaimana nilai 5 dibentuk di sini. Kami memulai dengan satu panggilan aktif ke
sumInts
. Itu memicu panggilan rekursif lain, dan nilai yang dikembalikan oleh panggilan itu mengkomunikasikan informasi kembalisumInts(5, 5)
. Panggilan kesumInts(5, 5)
kemudian pada gilirannya melakukan beberapa perhitungan dan mengembalikan nilai ke pemanggil.Jika Anda mencoba ini dengan
sumInts(4, 5)
, inilah yang akan terjadi:sumInts(4, 5)
mencoba untuk kembali4 + sumInts(5, 5)
. Untuk melakukan itu, ia memanggilsumInts(5, 5)
.sumInts(5, 5)
mencoba untuk kembali5 + sumInts(6, 5)
. Untuk melakukan itu, ia memanggilsumInts(6, 5)
.sumInts(6, 5)
mengembalikan 0 kembali kesumInts(5, 5).</li> <li>
sumInts (5, 5)now has a value for
sumInts (6, 5), namely 0. It then returns
5 + 0 = 5`.sumInts(4, 5)
sekarang memiliki nilai untuksumInts(5, 5)
, yaitu 5. Kemudian kembali4 + 5 = 9
.Dengan kata lain, nilai yang dikembalikan dibentuk dengan menjumlahkan nilai satu per satu, setiap kali mengambil satu nilai yang dikembalikan oleh panggilan rekursif tertentu ke
sumInts
dan menambahkan nilai saat ini daria
. Ketika rekursi berhenti, panggilan terdalam mengembalikan 0. Namun, nilai itu tidak segera keluar dari rantai panggilan rekursif; sebagai gantinya, itu hanya menyerahkan nilai kembali ke panggilan rekursif satu lapisan di atasnya. Dengan cara itu, setiap panggilan rekursif hanya menambahkan satu angka lagi dan mengembalikannya lebih tinggi dalam rangkaian, yang berpuncak dengan penjumlahan keseluruhan. Sebagai latihan, cobalah menelusuri inisumInts(2, 5)
, yang Anda ingin mulai dengan ini.Semoga ini membantu!
sumber
Sejauh ini Anda mendapatkan beberapa jawaban bagus, tetapi saya akan menambahkan satu jawaban lagi yang menggunakan cara berbeda.
Pertama, saya telah menulis banyak artikel tentang algoritma rekursif sederhana yang mungkin menarik bagi Anda; Lihat
http://ericlippert.com/tag/recursion/
http://blogs.msdn.com/b/ericlippert/archive/tags/recursion/
Itu dalam urutan terbaru di atas, jadi mulailah dari bawah.
Kedua, sejauh ini semua jawaban telah mendeskripsikan semantik rekursif dengan mempertimbangkan aktivasi fungsi . Setiap panggilan membuat aktivasi baru , dan panggilan rekursif dijalankan dalam konteks aktivasi ini. Itu adalah cara yang baik untuk memikirkannya, tetapi ada cara lain yang setara: teks pintar mencari dan mengganti .
Izinkan saya menulis ulang fungsi Anda menjadi bentuk yang sedikit lebih ringkas; jangan menganggap ini sebagai bahasa tertentu.
Saya harap itu masuk akal. Jika Anda tidak terbiasa dengan operator bersyarat, ini adalah bentuknya
condition ? consequence : alternative
dan artinya akan menjadi jelas.Sekarang kami ingin mengevaluasi.
s(2,5)
Kami melakukannya dengan melakukan penggantian panggilan secara tekstual dengan badan fungsi, lalu gantia
dengan2
danb
dengan5
:Sekarang evaluasi kondisional. Kami mengganti secara tekstual
2 > 5
denganfalse
.Sekarang secara tekstual mengganti semua kondisi palsu dengan alternatif dan semua kondisi benar dengan konsekuensi. Kami hanya memiliki kondisi palsu, jadi kami mengganti ekspresi itu secara tekstual dengan alternatif:
Sekarang, untuk menyelamatkan saya karena harus mengetik semua
+
tanda itu, secara tekstual mengganti aritmatika konstan dengan nilainya. (Ini sedikit curang, tapi saya tidak ingin mengingat semua tanda kurung!)Sekarang cari-dan-ganti, kali ini dengan isi untuk panggilan,
3
untuka
dan5
untuk b. Kami akan mengganti panggilan dalam tanda kurung:Dan sekarang kami terus melakukan langkah-langkah substitusi tekstual yang sama:
Yang kami lakukan di sini hanyalah substitusi tekstual langsung . Sungguh saya seharusnya tidak mengganti "3" untuk "2 + 1" dan seterusnya sampai saya harus melakukannya, tetapi secara pedagogis itu akan menjadi sulit untuk dibaca.
Aktivasi fungsi tidak lebih dari mengganti pemanggilan fungsi dengan tubuh pemanggilan, dan mengganti parameter formal dengan argumen yang sesuai. Anda harus berhati-hati memasukkan tanda kurung dengan cerdas, tetapi selain itu, itu hanya penggantian teks.
Tentu saja, sebagian besar bahasa tidak benar-benar mengimplementasikan aktivasi sebagai pengganti teks, tetapi secara logis memang begitu.
Jadi apa yang dimaksud dengan rekursi tak terbatas? Rekursi di mana substitusi tekstual tidak berhenti! Perhatikan bagaimana akhirnya kita sampai pada langkah di mana tidak ada lagi yang
s
harus diganti, dan kita kemudian bisa menerapkan aturan untuk aritmatika.sumber
Cara saya biasanya mengetahui cara kerja fungsi rekursif adalah dengan melihat kasus dasar dan bekerja mundur. Inilah teknik yang diterapkan pada fungsi ini.
Pertama kasus dasar:
Kemudian panggilan tepat di atasnya di tumpukan panggilan :
Kemudian panggilan tepat di atasnya di tumpukan panggilan:
Dan seterusnya:
Dan seterusnya:
Perhatikan bahwa kita telah sampai pada panggilan awal kita ke fungsi tersebut
sumInts(2, 5) == 14
Urutan eksekusi panggilan ini:
Urutan panggilan ini kembali:
Perhatikan bahwa kami sampai pada kesimpulan tentang bagaimana fungsi beroperasi dengan menelusuri panggilan dalam urutan yang mereka kembalikan .
sumber
Saya akan mencobanya.
Dengan mengeksekusi persamaan a + sumInts (a + 1, b), saya akan menunjukkan bagaimana jawaban akhirnya adalah 14.
Beri tahu kami jika Anda memiliki pertanyaan lebih lanjut.
Berikut contoh lain dari fungsi rekursif pada contoh berikut.
Seorang pria baru saja lulus kuliah.
t adalah jumlah waktu dalam beberapa tahun.
Jumlah tahun kerja sebenarnya sebelum pensiun, dapat dihitung sebagai berikut:
Dan itu seharusnya cukup untuk membuat siapapun tertekan, lol. ;-P
sumber
Pengulangan. Dalam rekursi Ilmu Komputer dibahas secara mendalam di bawah topik Finite Automata.
Dalam bentuknya yang paling sederhana, ini adalah referensi diri. Misalnya, mengatakan bahwa "mobil saya adalah mobil" adalah pernyataan rekursif. Masalahnya adalah bahwa pernyataan tersebut adalah rekursi tak terbatas yang tidak akan pernah berakhir. Definisi dalam pernyataan "mobil" adalah bahwa itu adalah "mobil" jadi bisa diganti. Namun, tidak ada habisnya karena dalam kasus pergantian pemain tetap menjadi "mobil saya adalah mobil".
Ini bisa berbeda jika pernyataannya adalah "mobil saya adalah bentley. Mobil saya berwarna biru." Dalam hal ini substitusi dalam situasi kedua untuk mobil dapat berupa "bentley" yang menghasilkan "bentley saya berwarna biru". Jenis substitusi ini secara matematis dijelaskan dalam Ilmu Komputer melalui Tata Bahasa Bebas Konteks .
Substitusi sebenarnya adalah aturan produksi. Mengingat bahwa pernyataan tersebut diwakili oleh S dan mobil itu adalah variabel yang dapat menjadi "bentley", pernyataan ini dapat direkonstruksi secara rekursif.
Ini dapat dibangun dengan berbagai cara, karena setiap
|
cara ada pilihan.S
dapat diganti dengan salah satu dari pilihan tersebut, dan S selalu dimulai dengan kosong. Caraε
untuk menghentikan produksi. Sama seperti yangS
bisa diganti, begitu pula variabel lain (hanya ada satu dan ituC
yang akan mewakili "bentley").Jadi memulai dengan
S
menjadi kosong, dan menggantinya dengan pilihan pertama"my"S
S
menjadiS
masih bisa diganti karena mewakili variabel. Kita bisa memilih "saya" lagi, atau ε untuk mengakhirinya, tapi mari kita lanjutkan membuat pernyataan asli kita. Kami memilih ruang yang artinyaS
diganti" "S
Selanjutnya mari kita pilih C.
Dan C hanya punya satu pilihan pengganti
Dan ruang lagi untuk S
Dan sebagainya
"my bentley is"S
,"my bentley is "S
,"my bentley is blue"S
,"my bentley is blue"
(menggantikan S untuk ε berakhir produksi) dan kami telah rekursif membangun pernyataan kami "bentley saya adalah biru".Pikirkan rekursi sebagai produksi dan penggantian ini. Setiap langkah dalam proses menggantikan pendahulunya untuk menghasilkan hasil akhir. Dalam contoh yang tepat dari jumlah rekursif dari 2 sampai 5, Anda akan mendapatkan produksi
Ini menjadi
sumber
Saya pikir cara terbaik untuk memahami fungsi rekursif adalah menyadari bahwa mereka dibuat untuk memproses struktur data rekursif. Tetapi dalam fungsi asli Anda
sumInts(a: Int, b: Int)
yang menghitung secara rekursif jumlah angka daria
sampaib
, ini tampaknya bukan struktur data rekursif ... Mari kita coba versi yang sedikit dimodifikasi disumInts(a: Int, n: Int)
manan
adalah berapa banyak angka yang akan Anda tambahkan.Sekarang, penjumlahan adalah rekursif
n
, bilangan asli. Masih bukan data rekursif, bukan? Nah, bilangan asli dapat dianggap sebagai struktur data rekursif menggunakan aksioma Peano:Jadi, 0 = Nol, 1 = Succesor (Nol), 2 = Succesor (Succesor (Nol)), dan seterusnya.
Setelah Anda memiliki struktur data rekursif, Anda memiliki templat untuk fungsi tersebut. Untuk setiap kasus non rekursif, Anda dapat menghitung nilainya secara langsung. Untuk kasus rekursif, Anda mengasumsikan bahwa fungsi rekursif sudah berfungsi dan menggunakannya untuk menghitung kasus, tetapi mendekonstruksi argumennya. Dalam kasus Natural, itu berarti bahwa alih-alih
Succesor(n)
kami akan menggunakann
, atau yang setara, alih-alihn
kami akan menggunakann - 1
.Sekarang fungsi rekursif lebih sederhana untuk diprogram. Pertama, kasus dasar
n=0
,. Apa yang harus kita kembalikan jika kita tidak ingin menambahkan angka? Jawabannya tentu saja 0.Bagaimana dengan kasus rekursif? Jika kita ingin menambahkan
n
angka yang dimulai dengana
dan kita sudah memilikisumInts
fungsi yang berfungsin-1
? Nah, kita perlu menambahkana
dan kemudian memanggilsumInts
dengana + 1
, jadi kami berakhir dengan:Hal yang menyenangkan adalah sekarang Anda tidak perlu berpikir dalam rekursi tingkat rendah. Anda hanya perlu memverifikasi bahwa:
sumber
Anda mungkin tertarik dengan implementasi fungsi Nisan dan Schocken . PDF yang ditautkan adalah bagian dari kursus online gratis. Ini menjelaskan bagian kedua dari implementasi mesin virtual di mana siswa harus menulis kompilator virtual-mesin-bahasa-ke-mesin-bahasa. Implementasi fungsi yang mereka usulkan mampu rekursi karena berbasis stack.
Untuk memperkenalkan Anda pada implementasi fungsi: Pertimbangkan kode mesin virtual berikut:
Jika Swift dikompilasi ke bahasa mesin virtual ini, maka blok kode Swift berikut:
akan dikompilasi menjadi
Bahasa mesin virtual dirancang di sekitar tumpukan global .
push constant n
mendorong integer ke tumpukan global ini.Setelah menjalankan baris 1 dan 2, tumpukan terlihat seperti:
256
dan257
merupakan alamat memori.call mult
mendorong nomor baris kembali (3) ke tumpukan dan mengalokasikan ruang untuk variabel lokal fungsi.... dan itu masuk ke label
function mult
. Kode di dalamnyamult
dijalankan. Sebagai hasil dari mengeksekusi kode itu, kami menghitung produk dari 2 dan 3, yang disimpan dalam variabel lokal ke-0 fungsi.Tepat sebelum
return
dari mult, Anda akan melihat baris:Kami akan mendorong produk ke tumpukan.
Ketika kami kembali, hal berikut terjadi:
Setelah kembali kami siap untuk mengeksekusi baris 4, dan tumpukan kami terlihat seperti ini:
Sekarang kami mendorong 4 ke tumpukan.
sub
adalah fungsi primitif dari bahasa mesin virtual. Dibutuhkan dua argumen dan mengembalikan hasilnya ke alamat biasa: argumen ke-0.Sekarang kita punya
Sekarang setelah Anda mengetahui cara kerja pemanggilan fungsi, memahami cara kerja rekursi relatif sederhana. Tidak ada sihir , hanya setumpuk.
Saya telah mengimplementasikan
sumInts
fungsi Anda dalam bahasa mesin virtual ini:Sekarang saya akan menyebutnya:
Kode dijalankan dan kami sampai ke titik berhenti di mana
lte
pengembalianfalse
. Seperti inilah tampilan tumpukan pada saat ini:Sekarang mari kita "melepas" rekursi kita.
return
0 dan kebagian baris 15 dan maju.Baris 16:
add
Baris 17:
return
5 dan kebagian baris 15 dan maju.Baris 16:
add
Baris 17:
return
9 dan kebagian baris 15 dan maju.Baris 16:
add
Baris
return
17:12 dan pergi ke baris 15 dan maju.Baris 16:
add
Baris
return
17:14 dan pergi ke baris 21 dan maju.Itu dia. Rekursi: Dimuliakan
goto
.sumber
Satu tip yang sangat bagus yang saya temui dalam belajar dan benar-benar memahami rekursi adalah meluangkan waktu untuk mempelajari bahasa yang tidak memiliki bentuk konstruksi lingkaran apa pun selain melalui rekursi. Dengan begitu Anda akan mendapatkan perasaan yang luar biasa tentang bagaimana MENGGUNAKAN rekursi melalui latihan.
Saya mengikuti http://www.htdp.org/ yang, selain menjadi tutorial Skema, juga merupakan pengantar yang bagus tentang cara merancang program dalam kaitannya dengan arsitektur dan desain.
Tetapi pada dasarnya, Anda perlu menginvestasikan waktu. Tanpa pemahaman yang 'tegas' tentang rekursi, algoritme tertentu, seperti penelusuran mundur, akan selalu tampak 'sulit' atau bahkan 'ajaib' bagi Anda. Jadi, bertahanlah. :-D
Saya harap ini membantu dan Semoga Sukses!
sumber
Sudah ada banyak jawaban bagus. Masih saya coba.
Saat dipanggil, fungsi mendapatkan alokasi ruang memori , yang ditumpuk di atas ruang memori fungsi pemanggil. Dalam ruang memori ini, fungsi menyimpan parameter yang diteruskan, variabel, dan nilainya. Ruang memori ini menghilang bersama dengan panggilan balik akhir dari fungsi tersebut. Saat ide stack berjalan, ruang memori dari fungsi pemanggil sekarang menjadi aktif.
Untuk panggilan rekursif, fungsi yang sama mendapatkan banyak ruang memori yang ditumpuk satu sama lain. Itu saja. Ide sederhana tentang bagaimana stack bekerja dalam memori komputer akan membantu Anda memahami bagaimana rekursi terjadi dalam implementasi.
sumber
Sedikit keluar dari topik, saya tahu, tapi ... coba cari rekursi di Google ... Anda akan melihat dengan contoh apa artinya :-)
Versi Google sebelumnya menampilkan teks berikut (dikutip dari memori):
Pada 10 September 2014, lelucon tentang rekursi telah diperbarui:
Untuk balasan lainnya, lihat jawaban ini .
sumber
Pikirkan rekursi sebagai beberapa klon melakukan hal yang sama ...
Anda meminta untuk menggandakan [1]: "jumlahkan angka antara 2 dan 5"
dan voilá !!
sumber
Banyak dari jawaban di atas sangat bagus. Sebuah teknik yang berguna untuk memecahkan rekursi adalah dengan menguraikan terlebih dahulu apa yang ingin kita lakukan dan kode sebagai manusia akan menyelesaikannya. Dalam kasus di atas, kami ingin meringkas urutan bilangan bulat berurutan (menggunakan angka dari atas):
Sekarang, perhatikan bahwa baris-baris ini membingungkan (tidak salah, tapi membingungkan).
Kenapa di tes
a>b
?, Dan kenapareturn 0
Mari kita ubah kodenya untuk mencerminkan lebih dekat apa yang dilakukan manusia
Bisakah kita melakukannya lebih seperti manusia? Iya! Biasanya kami menjumlahkan dari kiri ke kanan (2 + 3 + ...). Tetapi rekursi di atas menjumlahkan dari kanan ke kiri (... + 4 + 5). Ubah kode untuk mencerminkannya (
-
Bisa sedikit mengintimidasi, tapi tidak banyak)Beberapa orang mungkin menganggap fungsi ini lebih membingungkan karena kita mulai dari ujung 'jauh', tetapi berlatih bisa membuatnya terasa alami (dan ini adalah teknik 'berpikir' bagus lainnya: Mencoba 'kedua' sisi saat menyelesaikan rekursi). Dan lagi, fungsi tersebut mencerminkan apa yang dilakukan oleh manusia (paling?): Mengambil jumlah semua bilangan bulat kiri dan menambahkan bilangan bulat kanan 'berikutnya'.
sumber
Saya mengalami kesulitan untuk memahami rekursi kemudian saya menemukan blog ini dan saya sudah melihat pertanyaan ini jadi saya pikir saya harus berbagi. Anda harus membaca blog ini. Saya menemukan ini sangat membantu menjelaskannya dengan tumpukan dan bahkan menjelaskan bagaimana dua rekursi bekerja dengan tumpukan langkah demi langkah. Saya sarankan Anda terlebih dahulu memahami cara kerja tumpukan yang dijelaskannya dengan sangat baik di sini: perjalanan-ke-tumpukan
then now you will understand how recursion works now take a look of this post
: Pahami rekursi langkah demi langkahIni adalah program:
sumber
Rekursi mulai masuk akal bagi saya ketika saya berhenti membaca apa yang orang lain katakan tentangnya atau melihatnya sebagai sesuatu yang dapat saya hindari dan hanya menulis kode. Saya menemukan masalah dengan solusi dan mencoba menduplikasi solusi tanpa melihat. Saya hanya melihat solusinya ketika saya terjebak tanpa daya. Kemudian saya kembali mencoba menduplikasinya. Saya melakukan ini lagi pada banyak masalah sampai saya mengembangkan pemahaman dan pemahaman saya sendiri tentang bagaimana mengidentifikasi masalah yang berulang dan menyelesaikannya. Ketika saya mencapai level ini, saya mulai mengarang masalah dan menyelesaikannya. Itu lebih membantu saya. Kadang-kadang, hal-hal hanya bisa dipelajari dengan mencobanya sendiri dan berjuang; sampai Anda "mendapatkannya".
sumber
Izinkan saya memberi tahu Anda dengan contoh deret Fibonacci, Fibonacci adalah
jadi mari kita lihat cara kerja rekursi, saya hanya mengganti
n
dit(n)
dengann-1
dan sebagainya. terlihat:kita tahu jika
t(0)=(n-k)
sederajat untuk1
kemudiann-k=0
jadin=k
kita gantik
dengann
:jika kita menghilangkan
n-n
maka:begitu
3+2+1+(n-1)+n
juga bilangan asli. itu dihitung sebagaiΣ3+2+1+(n-1)+n = n(n+1)/2 => n²+n/2
hasil untuk fib adalah:
O(1 + n²) = O(n²)
Ini cara terbaik untuk memahami hubungan rekursif
sumber