rekursi versus iterasi

109

Apakah benar untuk mengatakan bahwa di mana pun rekursi digunakan, sebuah forloop dapat digunakan? Dan jika rekursi biasanya lebih lambat, apa alasan teknis untuk menggunakannya forberulang kali?

Dan jika selalu memungkinkan untuk mengubah rekursi menjadi forloop, apakah ada aturan praktis untuk melakukannya?

Breako Breako
sumber
3
recursionvs iteration? iteration = for loopKupikir.
gongzhitaao
4
Blog Tom Moertel memiliki empat posting yang sangat baik tentang mengubah kode rekursif menjadi kode berulang: blog.moertel.com/tags/recursion.html
cjohnson318

Jawaban:

147

Rekursi biasanya jauh lebih lambat karena semua panggilan fungsi harus disimpan dalam tumpukan untuk memungkinkan kembali ke fungsi pemanggil. Dalam banyak kasus, memori harus dialokasikan dan disalin untuk mengimplementasikan isolasi cakupan.

Beberapa pengoptimalan, seperti pengoptimalan panggilan ekor , membuat rekursi lebih cepat tetapi tidak selalu memungkinkan, dan tidak diterapkan dalam semua bahasa.

Alasan utama untuk menggunakan rekursi adalah

  • yang lebih intuitif dalam banyak kasus saat meniru pendekatan kita terhadap masalah
  • bahwa beberapa struktur data seperti pohon lebih mudah untuk dijelajahi menggunakan rekursi (atau akan membutuhkan tumpukan dalam hal apapun)

Tentu saja setiap rekursi dapat dimodelkan sebagai semacam loop: itulah yang pada akhirnya akan dilakukan oleh CPU. Dan rekursi itu sendiri, secara lebih langsung, berarti menempatkan pemanggilan fungsi dan cakupan dalam tumpukan. Tetapi mengubah algoritme rekursif Anda menjadi perulangan mungkin memerlukan banyak pekerjaan dan membuat kode Anda kurang dapat dipelihara: seperti untuk setiap pengoptimalan, itu hanya boleh dicoba ketika beberapa profil atau bukti menunjukkan itu diperlukan.

Denys Séguret
sumber
10
Untuk menambahkannya - rekursi terkait erat dengan istilah reduksi yang memainkan peran sentral dalam banyak algoritma dan CS pada umumnya.
SomeWittyUsername
3
Bisakah Anda memberi saya contoh di mana rekursi membuat kode lebih mudah dipelihara? Menurut pengalaman saya, selalu sebaliknya. Terima kasih
Yeikel
@Yeikel Menulis fungsi f(n)yang mengembalikan angka Fibonacci ke-n .
Matt
54

Apakah benar untuk mengatakan bahwa di mana pun rekursi digunakan, loop for dapat digunakan?

Ya, karena rekursi di sebagian besar CPU dimodelkan dengan loop dan struktur data tumpukan.

Dan jika rekursi biasanya lebih lambat, apa alasan teknis untuk menggunakannya?

Ini tidak "biasanya lebih lambat": itu rekursi yang diterapkan secara tidak benar yang lebih lambat. Selain itu, kompiler modern pandai mengubah beberapa rekursi menjadi loop bahkan tanpa bertanya.

Dan jika selalu memungkinkan untuk mengubah rekursi menjadi loop for, apakah ada aturan praktis untuk melakukannya?

Tulis program iteratif untuk algoritme yang paling dipahami saat dijelaskan secara berulang; tulis program rekursif untuk algoritme yang paling baik dijelaskan secara rekursif.

Misalnya, mencari pohon biner, menjalankan quicksort, dan ekspresi parsing dalam banyak bahasa pemrograman sering dijelaskan secara rekursif. Ini juga paling baik dikodekan secara rekursif. Di sisi lain, menghitung faktorial dan menghitung angka Fibonacci jauh lebih mudah dijelaskan dalam hal iterasi. Menggunakan rekursi untuk mereka seperti menampar lalat dengan palu godam: itu bukan ide yang baik, bahkan ketika palu godam melakukan pekerjaan yang sangat bagus + .


+ Saya meminjam analogi palu godam dari "Disiplin Pemrograman" Dijkstra.

dasblinkenlight
sumber
7
Rekursi biasanya lebih mahal (lebih lambat / lebih banyak memori), karena membuat bingkai tumpukan dan semacamnya. Perbedaannya mungkin kecil jika diterapkan dengan benar untuk masalah yang cukup kompleks, tetapi biayanya masih lebih mahal. Ada kemungkinan pengecualian seperti pengoptimalan rekursi ekor.
Bernhard Barker
Saya tidak yakin tentang satu loop untuk dalam setiap kasus. Pertimbangkan rekursi yang lebih kompleks atau rekursi dengan lebih dari satu variabel
SomeWittyUsername
@dasblinkenlight Secara teoritis mungkin untuk mengurangi beberapa loop menjadi satu, tetapi tidak yakin tentang ini.
SomeWittyUsername
@icepack Ya, itu mungkin. Mungkin tidak cantik, tapi mungkin saja.
Bernhard Barker
Saya tidak yakin saya setuju dengan pernyataan pertama Anda. CPU sendiri tidak benar-benar memodelkan rekursi sama sekali, itu adalah instruksi yang dijalankan pada CPU yang memodelkan rekursi. kedua, struktur loop tidak (harus) memiliki kumpulan data yang tumbuh dan menyusut secara dinamis, di mana algoritme rekursif biasanya akan untuk setiap level dalam rekursi harus melangkah.
terompet
28

Pertanyaan :

Dan jika rekursi biasanya lebih lambat apa alasan teknis untuk menggunakannya selama iterasi loop?

Jawaban:

Karena pada beberapa algoritma sulit untuk menyelesaikannya secara iteratif. Cobalah untuk memecahkan pencarian kedalaman-pertama secara rekursif dan iteratif. Anda akan mendapatkan gagasan bahwa sulit untuk menyelesaikan DFS dengan iterasi.

Hal baik lainnya untuk dicoba: Cobalah untuk menulis Merge sort secara iteratif. Ini akan memakan waktu cukup lama.

Pertanyaan :

Apakah benar untuk mengatakan bahwa di mana pun rekursi digunakan, loop for dapat digunakan?

Jawaban:

Iya. Utas ini memiliki jawaban yang sangat bagus untuk ini.

Pertanyaan :

Dan jika selalu memungkinkan untuk mengubah rekursi menjadi loop for, apakah ada aturan praktis untuk melakukannya?

Jawaban:

Percayalah kepadaku. Cobalah untuk menulis versi Anda sendiri untuk menyelesaikan pencarian kedalaman-pertama secara berulang. Anda akan melihat bahwa beberapa masalah lebih mudah diselesaikan secara rekursif.

Petunjuk: Rekursi bagus saat Anda memecahkan masalah yang dapat diselesaikan dengan teknik bagi dan taklukkan .

Thanakron Tandavas
sumber
3
Saya menghargai upaya memberikan jawaban yang berwibawa dan saya yakin penulisnya cerdas, tetapi "percayalah" bukanlah tanggapan yang membantu untuk pertanyaan yang bermakna yang jawabannya tidak segera jelas. Ada algoritma yang sangat mudah untuk melakukan pencarian kedalaman pertama yang berulang. Lihat contoh di bagian bawah halaman ini untuk deskripsi algoritma dalam pseudocode: csl.mtu.edu/cs2321/www/newLectures/26_Depth_First_Search.html
jdelman
3

Selain lebih lambat, rekursi juga dapat mengakibatkan kesalahan stack overflow tergantung seberapa dalam prosesnya.

G. Steigert
sumber
3

Untuk menulis metode yang setara menggunakan iterasi, kita harus secara eksplisit menggunakan tumpukan. Fakta bahwa versi iteratif memerlukan tumpukan untuk solusinya menunjukkan bahwa masalahnya cukup sulit sehingga dapat diuntungkan dari rekursi. Sebagai aturan umum, rekursi paling cocok untuk masalah yang tidak dapat diselesaikan dengan jumlah memori tetap dan akibatnya memerlukan tumpukan saat diselesaikan secara berulang. Karena itu, rekursi dan iterasi dapat menunjukkan hasil yang sama sementara mereka mengikuti pola yang berbeda. Untuk memutuskan metode mana yang bekerja lebih baik adalah kasus per kasus dan praktik terbaik adalah memilih berdasarkan pola yang diikuti oleh masalah.

Misalnya, untuk mencari bilangan segitiga ke-n dari barisan Segitiga: 1 3 6 10 15… Program yang menggunakan algoritma iteratif untuk mencari bilangan segitiga ke-n:

Menggunakan algoritma iteratif:

//Triangular.java
import java.util.*;
class Triangular {
   public static int iterativeTriangular(int n) {
      int sum = 0;
      for (int i = 1; i <= n; i ++)
         sum += i;
      return sum;
   }
   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                            iterativeTriangular(n));
   }
}//enter code here

Menggunakan algoritma rekursif:

//Triangular.java
import java.util.*;
class Triangular {
   public static int recursiveTriangular(int n) {
      if (n == 1)
     return 1;  
      return recursiveTriangular(n-1) + n; 
   }

   public static void main(String args[]) {
      Scanner stdin = new Scanner(System.in);
      System.out.print("Please enter a number: ");
      int n = stdin.nextInt();
      System.out.println("The " + n + "-th triangular number is: " + 
                             recursiveTriangular(n)); 
   }
}
shirin
sumber
1

Sebagian besar jawaban tampaknya berasumsi bahwa iterative= for loop. Jika perulangan for Anda tidak dibatasi ( a la C, Anda dapat melakukan apa pun yang Anda inginkan dengan penghitung perulangan Anda), maka itu benar. Jika ini adalah loop nyata for (katakanlah seperti di Python atau sebagian besar bahasa fungsional di mana Anda tidak dapat memodifikasi penghitung loop secara manual), maka itu tidak benar.

Semua fungsi (dapat dihitung) dapat diimplementasikan baik secara rekursif dan menggunakan whileloop (atau lompatan bersyarat, yang pada dasarnya adalah hal yang sama). Jika Anda benar-benar membatasi diri for loopsAnda, Anda hanya akan mendapatkan subset dari fungsi-fungsi tersebut (yang rekursif primitif, jika operasi dasar Anda masuk akal). Memang, ini adalah subset yang cukup besar yang kebetulan berisi setiap fungsi yang cenderung Anda encouter dalam praktiknya.

Yang jauh lebih penting adalah banyak fungsi yang sangat mudah diimplementasikan secara rekursif dan sangat sulit untuk diterapkan secara iteratif (mengelola tumpukan panggilan Anda secara manual tidak dihitung).

Jbeuh
sumber
1

Ya, seperti yang dikatakan oleh Thanakron Tandavas ,

Rekursi bagus saat Anda memecahkan masalah yang dapat diselesaikan dengan teknik bagi dan taklukkan.

Misalnya: Menara Hanoi

  1. N cincin dalam ukuran yang semakin besar
  2. 3 tiang
  3. Cincin mulai ditumpuk di tiang 1. Tujuannya adalah untuk memindahkan cincin agar bertumpuk di tiang 3 ... Tapi
    • Hanya bisa menggerakkan satu dering dalam satu waktu.
    • Tidak bisa meletakkan cincin yang lebih besar di atas yang lebih kecil.
  4. Solusi berulang adalah "kuat namun jelek"; solusi rekursif adalah "elegan".
Ramesh Mukkera
sumber
Contoh yang menarik. Saya kira Anda tahu makalah yang ditulis oleh MC Er "The Towers of Hanoi and Binary Numerals". Juga disuguhkan dalam video fantastis oleh 3brown1blue.
Andrestand
0

Sepertinya saya ingat profesor ilmu komputer saya mengatakan kembali pada hari itu bahwa semua masalah yang memiliki solusi rekursif juga memiliki solusi berulang. Dia mengatakan bahwa solusi rekursif biasanya lebih lambat, tetapi sering digunakan ketika lebih mudah untuk bernalar dan kode daripada solusi berulang.

Namun, dalam kasus solusi rekursif yang lebih maju, saya tidak percaya bahwa itu akan selalu dapat menerapkannya menggunakan forloop sederhana .

Sungai Vivian
sumber
Selalu memungkinkan untuk mengubah algoritme rekursif menjadi algoritme berulang (menggunakan tumpukan). Anda mungkin tidak berakhir dengan loop yang sangat sederhana, tetapi itu mungkin.
Bernhard Barker
-4

rekursi + menghafal dapat menghasilkan solusi yang lebih efisien dibandingkan dengan pendekatan iteratif murni, misalnya periksa ini: http://jsperf.com/fibonacci-memoized-vs-iterative-for-large-n

Reza Afzalan
sumber
3
Kode rekursif apa pun dapat diubah menjadi kode iteratif yang identik secara fungsional menggunakan tumpukan. Perbedaan yang Anda tunjukkan adalah perbedaan antara dua pendekatan untuk menyelesaikan masalah yang sama, bukan perbedaan antara rekursi dan iterasi.
Bernhard Barker
-6

Jawaban singkatnya: trade off adalah rekursi lebih cepat dan untuk loop memakan lebih sedikit memori di hampir semua kasus. Namun biasanya ada cara untuk mengubah loop atau rekursi agar berjalan lebih cepat

Jessica Shu
sumber