Apa pertimbangan untuk menentukan apakah Anda dapat menggunakan rekursi untuk menyelesaikan masalah?


10

Kadang-kadang dalam wawancara, saya dapat menggunakan rekursi untuk menyelesaikan masalah (seperti menambahkan 1ke integer presisi tak terbatas), atau ketika masalah muncul dengan sendirinya cocok untuk menggunakan rekursi. Terkadang, itu mungkin hanya karena menggunakan rekursi banyak untuk pemecahan masalah, jadi tanpa banyak berpikir, rekursi digunakan untuk menyelesaikan masalah.

Namun, apa pertimbangannya sebelum Anda bisa memutuskan apakah cocok untuk menggunakan rekursi untuk menyelesaikan masalah?


Beberapa pemikiran yang saya miliki:

Jika kita menggunakan rekursi pada data yang dibelah dua setiap kali, sepertinya tidak ada masalah menggunakan rekursi, karena semua data yang dapat masuk ke dalam 16GB RAM, atau bahkan hard drive 8TB, dapat ditangani dengan rekursi hanya sedalam 42 level. (jadi tidak ada stack overflow (saya pikir di beberapa lingkungan, stack bisa mencapai level 4000, lebih dari 42, tetapi pada saat yang sama, itu juga tergantung pada berapa banyak variabel lokal yang Anda miliki, karena setiap panggilan stack, menempati lebih banyak memori) jika ada banyak variabel lokal, dan itu adalah ukuran memori, bukan level, yang menentukan stack overflow)).

Jika Anda menghitung angka Fibonacci menggunakan rekursi murni, Anda benar-benar harus khawatir tentang kerumitan waktu, kecuali jika Anda menyimpan hasil antara.

Dan bagaimana kalau menambahkan 1ke integer presisi tak terbatas? Mungkin masih bisa diperdebatkan, karena, apakah Anda akan bekerja dengan angka yang panjangnya 3000 digit atau 4000 digit, begitu besar sehingga bisa menyebabkan stack overflow? Saya tidak memikirkannya, tapi mungkin jawabannya tidak, kita tidak boleh menggunakan rekursi, tapi gunakan saja perulangan, karena bagaimana jika dalam beberapa aplikasi, angkanya harus panjang 4.000 digit, untuk memeriksa beberapa properti nomor, seperti apakah nomornya prima atau tidak.

Pertanyaan pamungkasnya adalah: apa pertimbangannya sebelum Anda dapat memutuskan untuk menggunakan rekursi untuk menyelesaikan masalah?


7
Sebenarnya agak sederhana: "Apakah solusinya sepele jika saya dapat berasumsi bahwa solusi untuk masalah yang sedikit lebih kecil diketahui?"
Kilian Foth

tapi bagaimana dengan angka Fibonacci atau menambahkan 1ke integer presisi tak terbatas? Anda bisa mengatakan, ya, mereka mengecil menjadi masalah yang lebih kecil, tetapi rekursi murni tidak cocok untuk itu
nonopolaritas

Anda mungkin menemukan ini membantu - stackoverflow.com/questions/3021/…
Kishor Kundan

Jawaban:


15

Satu pertimbangan adalah apakah algoritma Anda dimaksudkan untuk menjadi solusi abstrak, atau solusi yang dapat dieksekusi praktis. Dalam kasus sebelumnya, atribut yang Anda cari adalah kebenaran, dan kemudahan pemahaman untuk audiens target Anda 1 . Dalam kasus terakhir, kinerja juga merupakan masalah. Pertimbangan ini dapat memengaruhi pilihan Anda.

Pertimbangan kedua (untuk solusi praktis) adalah apakah bahasa pemrograman (atau lebih tepatnya, implementasinya) yang Anda gunakan lakukan eliminasi tail-call? Tanpa eliminasi panggilan ekor, rekursi lebih lambat dari iterasi, dan rekursi yang dalam dapat menyebabkan masalah stack overflow.

Perhatikan bahwa solusi rekursif (benar) dapat diubah menjadi solusi non-rekursif yang setara, sehingga Anda tidak perlu membuat pilihan sulit antara kedua pendekatan.

Akhirnya, kadang-kadang pilihan antara formulasi rekursif dan non-rekursif dimotivasi oleh kebutuhan untuk membuktikan (dalam pengertian formal) properti tentang suatu algoritma. Formulasi rekursif lebih langsung memungkinkan bukti-oleh-induksi.


1 - Ini termasuk pertimbangan seperti apakah target audiens ... dan ini mungkin termasuk programmer membaca kode praktis ... akan melihat satu gaya solusi sebagai "lebih alami" daripada yang lain. Gagasan "alami" akan bervariasi dari orang ke orang, tergantung pada bagaimana mereka belajar pemrograman atau algoritme. (Saya menantang siapa pun yang mengusulkan "kealamian" sebagai kriteria utama untuk memutuskan untuk menggunakan rekursi (atau tidak) untuk mendefinisikan "kealamian" dalam istilah obyektif; yaitu bagaimana Anda mengukurnya.)


2
Beberapa masalah hanya diekspresikan secara alami menggunakan rekursi. Traversal pohon, misalnya.
Frank Hileman

Memperbarui jawaban saya untuk mengatasinya,
Stephen C

1
Berkenaan dengan "kealamian": traversal pohon tanpa rekursi, misalnya, cenderung menghasilkan kode tujuan yang lebih besar dan kurang umum. Pertimbangkan misalnya menggunakan panggilan polimorfik untuk melintasi pohon, dengan perilaku berbeda untuk simpul daun dan komposit. Ini tidak mungkin tanpa rekursi.
Frank Hileman

1) Apakah Anda menerima tantangan saya untuk mendefinisikan "alami"? 2) Karena dimungkinkan untuk mensimulasikan rekursi menggunakan struktur data tumpukan, dimungkinkan untuk mengimplementasikan traversal pohon juga. Ini mungkin bukan cara yang paling efisien ... dan itu tidak akan memberi Anda kode yang paling mudah dibaca ... tapi itu pasti mungkin, dan praktis untuk melakukannya.
Stephen C

Omong-omong, bahasa pemrograman pertama yang saya pelajari (FORTRAN 4) tidak mendukung rekursi sama sekali.
Stephen C

1

Sebagai seorang programmer C / C ++, pertimbangan utama saya adalah kinerja. Proses keputusan saya adalah seperti:

  1. Berapa kedalaman maksimal tumpukan panggilan? Jika terlalu dalam, singkirkan rekursi. Jika dangkal, lanjutkan ke 2.

  2. Apakah fungsi ini cenderung menjadi penghambat program saya? Jika ya, lanjutkan ke 3. Jika tidak, pertahankan rekursi. Jika tidak yakin, jalankan profiler.

  3. Berapa fraksi waktu CPU yang dihabiskan untuk panggilan fungsi rekursif? Jika pemanggilan fungsi memakan waktu jauh lebih sedikit daripada bagian fungsi lainnya, tidak apa-apa untuk menggunakan rekursi.


0

Namun, apa pertimbangannya sebelum Anda bisa memutuskan apakah cocok untuk menggunakan rekursi untuk menyelesaikan masalah?

Ketika menulis fungsi dalam Skema, saya merasa wajar untuk menulis fungsi rekursif ekor tanpa berpikir terlalu banyak.

Saat menulis fungsi dalam C ++, saya menemukan diri saya berdebat sebelum saya menggunakan fungsi rekursif. Pertanyaan yang saya tanyakan pada diri saya adalah:

  • Bisakah perhitungan dilakukan dengan menggunakan algoritma iteratif? Jika ya, gunakan pendekatan berulang.

  • Bisakah kedalaman rekursi tumbuh dengan ukuran model? Baru-baru ini saya menemukan sebuah kasus di mana kedalaman rekursi tumbuh hampir 13.000 karena ukuran model. Saya harus mengonversi fungsi untuk menggunakan algoritma iterative post-haste.

    Untuk alasan ini, saya tidak akan merekomendasikan menulis algoritma pohon traversal menggunakan fungsi rekursif. Anda tidak pernah tahu kapan pohon itu menjadi terlalu dalam untuk lingkungan waktu lari Anda.

  • Bisakah fungsi menjadi terlalu berbelit-belit dengan menggunakan algoritma iteratif? Jika ya, gunakan fungsi rekursif. Saya belum mencoba menulis qsortmenggunakan pendekatan berulang tapi saya merasa menggunakan fungsi rekursif lebih alami untuk itu.


0

Untuk angka Fibonacci, "rekursi" naif itu benar-benar bodoh. Itu karena itu mengarah ke subproblem yang sama yang dipecahkan berulang kali.

Sebenarnya ada variasi sepele dari angka-angka Fibonacci di mana rekursi sangat efisien: Diberi angka n ≥ 1, hitung baik fib (n) dan fib (n-1). Jadi Anda memerlukan fungsi yang mengembalikan dua hasil, sebut saja fungsi ini fib2.

Implementasinya cukup sederhana:

function fib2 (n) -> (fibn, fibnm1) {
    if n ≤ 1 { return (1, 1) }
    let (fibn, fibnm1) = fib2 (n-1)
    return (fibn + fibnm1, fibn)
}

apakah Anda pikir Anda bisa menulis program dalam bahasa yang sama? dan Anda fib2mengembalikan sepasang angka, dan Anda fib2()tidak cocok dengan antarmuka fib(), yang diberi nomor, mengembalikan angka. Tampaknya Anda fib(n)akan kembali fib2(n)[0]tetapi harap spesifik
nonopolaritas
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.