Tuladha kode rekursif tanpa syarat metu

Ayo goleki maneh masalah rekursif. Contone, nimbang ngetung angka Fibonacci. Saben uwong bakal ngelingi yen urutan Fibonacci minangka urutan numerik sing rong nomer pisanan yaiku 0 lan 1, lan saben nomer sabanjure padha karo jumlah rong nomer sadurunge.

Ayo nulis kode kanggo ngetung lan nampilake nomer kasebut:

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
        printFibonacci(0, 1);
    }

    private static void printFibonacci(long penultimate, long previous) {
        long current = penultimate + previous;
        System.out.println(current);
        printFibonacci(previous, current);
    }
}

Sadurunge telpon pisanan saka cara printFibonacci rekursif , print rong nomer pisanan saka urutan: nul lan siji. Kita kudu nindakake iki amarga cara rekursif mung nampilake jumlah paramèter input, dudu paramèter kasebut.

Kode katon oke: kita njaluk rong nomer, ngetung jumlah sing, print ing console, lan nelpon cara printFibonacci recursively maneh. Kita ngliwati nomer sadurunge (sadurunge) lan nomer saiki (saiki) minangka argumen.

Bener, kode kasebut duwe rong kesalahan. Sampeyan bisa ndeleng yen sampeyan mbukak kode.

Kesalahan pisanan yaiku overflowing jinis dawa . Nomer 104 ing urutan kita negatif, nuduhake yen jinis dawa overflowed.

Kesalahan kapindho beda. Sawise ngitung kira-kira 12.000 nomer, kita entuk:

Pangecualian ing thread "utama" java.lang.StackOverflowError

Saiki iki wektu sing tepat kanggo ngelingi apa stack panggilan metode ing Jawa. Mesin Java nyimpen rekaman kabeh panggilan fungsi. Kanggo nindakake iki, nggunakake koleksi khusus sing diarani tumpukan. Nalika siji fungsi nelpon liyane, mesin Java nyurung StackTraceElement anyar menyang tumpukan. Nalika fungsi rampung, unsur iki dibusak saka tumpukan. Patut, tumpukan tansah nyimpen informasi anyar babagan kahanan saiki tumpukan telpon fungsi. Dokumentasi kanggo StackTraceElement ujar manawa "Dibuwang nalika tumpukan tumpukan kedadeyan amarga aplikasi recurses banget." JVM sing mlaku nduweni area memori khusus kanggo nyimpen tumpukan panggilan metode. Ukuran area memori iki gumantung ing setelan OS lan JVM. Saliyane tumpukan panggilan metode dhewe, variabel primitif (nilai spesifik paramèter metode) lan alamat variabel referensi (ing area memori sing disebut "tumpukan") disimpen ing area memori khusus iki. Akses menyang tumpukan telpon ngetutake prinsip LIFO.

Conto sing didandani kanthi kondisi metu

Ayo dadi miwiti kanthi ndandani masalah kapindho ing kode.

Ayo nyoba ngatasi masalah kasebut kanthi cepet: yen tumpukan kasebut cilik banget, mula dadi luwih gedhe. Kanggo nindakake iki, miwiti JVM karo "-Xss" flag lan nemtokake jumlah memori kanggo nyedhiakke kanggo tumpukan. Coba alokasi 5 megabyte. Iki bakal katon kaya ing IDEA:

Kita bisa nambah dawa output. Saiki bisa ngetung luwih saka 49.000 nomer saka urutan tinimbang diwatesi nganti udakara 12.000 nomer. Nanging ing sawetara titik, kita isih entuk StackOverflowError .

Sampeyan bisa nyoba kanggo nambah ukuran tumpukan, nanging ora ndandani masalah dhasar. Dadi, ayo goleki masalah ing logika. Mesthi ana titik nalika rekursi mandheg. Ing tembung liyane, kudu ana sawetara kondisi sing nemtokake nalika cara rekursif ora bakal disebut maneh, saéngga tumpukan telpon kanggo unwind. Kanggo nemtokake kondisi kasebut, ayo nggawe obyektif kanthi jelas: nampilake seri Fibonacci anggere angka kasebut kurang saka Integer.MAX_VALUE .

Ayo nulis metode printFibonacciWithCondition anyar sing bakal nimbang kondisi kasebut. Lan kita bakal nelpon cara didandani anyar ing cara utama.

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
//        printFibonacci(0, 1);
        printFibonacciWithCondition(0, 1);
    }

    private static void printFibonacci(long penultimate, long previous) {
        long current = penultimate + previous;
        System.out.println(current);
        printFibonacci(previous, current);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        System.out.println(current);
        printFibonacciWithCondition(previous, current);
    }
}

Sawise mbukak kode, kita weruh yen output rampung karo nomer 1836311903. Nomer sadurunge iki 1134903170. Jumlah nomer kasebut yaiku 2_971_215_073, sing luwih gedhe tinimbang Integer.MAX_VALUE (2_147_483_647 ) .

Owah-owahan iki kanthi otomatis ndandani bug kebanjiran dawa . Yen sampeyan kudu ngetung luwih akeh seri, sampeyan kudu nggunakake jinis data liyane, kayata BigInteger .

Turunan rekursif lan unwinding

Ayo analisa langkah demi langkah carane kode dieksekusi. Kanggo nindakake iki, kita bakal nambah cara gema lan nelpon sadurunge lan sawise telpon rekursif saka printFibonacciWithCondition .

public class Fibonacci {
    public static void main(String[] args) {
        System.out.println(0);
        System.out.println(1);
        printFibonacciWithCondition(0, 1);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        echo(true, penultimate, previous);
        System.out.println(current);
        printFibonacciWithCondition(previous, current);
        echo(false, penultimate, previous);
    }

    private static void echo(boolean isBeforeRecursiveCall, long penultimate, long previous) {
        if (isBeforeRecursiveCall) {
            System.out.printf("Before method call with args: %d, %d. Current number = ", penultimate, previous);
        } else {
            System.out.printf("After method call with args: %d, %d\n", penultimate, previous);
        }
    }
}

Program menehi output iki:

0
1
Sadurunge nelpon metode nganggo args: 0, 1. Nomer saiki = 1
Sadurunge nelpon metode nganggo args: 1, 1. Nomer saiki = 2
Sadurunge nelpon metode nganggo args: 1, 2. Nomer saiki = 3
Sadurunge nelpon metode karo args: 2, 3. Nomer saiki = 5
Sadurunge metode nelpon nganggo args: 3, 5. Nomer saiki = 8
Sadurunge metode nelpon nganggo args: 5, 8. Nomer saiki = 13
Sadurunge metode nelpon nganggo args: 8, 13. Nomer saiki = 21
Sadurunge nelpon metode nganggo args: 13, 21. Nomer saiki = 34
Sadurunge nelpon metode karo args: 21, 34. Nomer saiki = 55
Sadurunge nelpon metode karo args: 34, 55. Nomer saiki = 89
Sadurunge nelpon metode karo args: 55, 89. Nomer saiki = 144
Sadurunge nelpon metode karo args: 89, 144. Nomer saiki = 233
Sadurunge nelpon metode karo args: 144, 233. Nomer saiki = 377
Sadurunge nelpon metode karo args: 233, 377. Nomer saiki = 610
Sadurunge nelpon metode karo args: 377, 610. Nomer saiki = 987
Sadurunge metode nelpon nganggo args: 610, 987. Nomer saiki = 1597
Sadurunge metode nelpon nganggo args: 987, 1597. Nomer saiki = 2584
Sadurunge metode nelpon nganggo args: 1597, 2584. Nomer saiki = 4181
Sadurunge metode nelpon karo args: 2584, 4181. Nomer saiki = 6765
Sadurunge cara nelpon karo args: 4181, 6765. Nomer saiki = 10946
Sadurunge cara nelpon karo args: 6765, 10946. Nomer saiki = 17711
Sadurunge cara nelpon karo args: 10946. Nomer saiki = 28657
Sadurunge nelpon metode karo args: 17711, 28657. Nomer saiki = 46368
Sadurunge nelpon metode karo args: 28657, 46368. Nomer saiki = 75025
Sadurunge cara nelpon karo args: 46368, 75025. Nomer saiki = 121393
Sadurunge cara telpon = 121393 121393. Nomer saiki = 196418
Sadurunge cara nelpon karo args: 121393, 196418. Nomer saiki = 317811
Sadurunge cara nelpon karo args: 196418, 317811. Nomer saiki = 514221
Sadurunge nomer telpon karo args: 317811 Sadurunge metode telpon karo args: 317811.
cara nelpon karo args: 514229, 832040. Nomer saiki = 1346269
Sadurunge cara nelpon karo args: 832040, 1346269. Nomer saiki = 2178309
Sadurunge cara nelpon karo args: 1346269, 2178309.
Sadurunge metode nelpon karo args: 2178309, 3524578. Nomer saiki = 5702887
Sadurunge cara nelpon karo args: 3524578, 5702887. Nomer saiki = 9227465
Sadurunge cara nelpon karo args: 5702887, 922746 nomer
saiki: 5702887, 922746. 27465, 14930352. Nomer saiki = 24157817
Sadurunge cara nelpon karo args: 14930352, 24157817. Nomer saiki = 39088169
Sadurunge cara nelpon karo args: 24157817, 39088169. Nomer saiki = 690845 args 6984.
3245986. Nomer saiki = 102334155
Sadurunge metode nelpon karo args: 63245986, 102334155. Nomer saiki = 165580141
Sadurunge cara nelpon karo args: 102334155, 165580141. Nomer saiki = 267914296
Sadurunge cara nelpon karo args: 165580141, 267914296. Nomer saiki = 433494437
Sadurunge cara nelpon karo args: 267914296, 433494437. Nomer saiki = 701408733 Sadurunge cara
nelpon karo args: 437,149. 34903170
Sadurunge metode nelpon karo args: 701408733, 1134903170. Nomer saiki = 1836311903
Sawise cara nelpon karo args: 701408733, 113490317
Sawise cara nelpon karo args: 433494437, 701408733 Sawise cara nelpon karo args: 267 4143 karo
args
: 267 453 580141, 267914296
Sawise cara nelpon karo args: 102334155, 165580141
Sawise cara nelpon karo args: 63245986, 102334155
Sawise cara nelpon karo args: 39088169, 63245986
Sawise nelpon metode karo args: 24157817, 39088169
Sawise nelpon metode karo args: 14930352, 24157817
Sawise nelpon metode karo args: 9227465, 14930352 Sawise nelpon metode karo args: 5702887, 9228
sawise
nelpon args: 5702887, 9228 887
Sawise cara nelpon karo args : 2178309, 3524578
Sawise cara nelpon karo args: 1346269, 2178309
Sawise cara nelpon karo args: 832040, 1346269 Sawise
cara nelpon karo args: 514229, 832040 Sawise cara nelpon karo args: 917 Sawise cara nelpon karo args: 3417 karo cara
args: 3417
karo args: 8, 317811
Sawise cara nelpon karo args: 121393, 196418
Sawise cara nelpon karo args: 75025, 121393
Sawise cara nelpon karo args: 46368, 75025
Sawise nelpon metode karo args: 28657, 46368
Sawise nelpon metode karo args: 17711, 28657
Sawise nelpon metode karo args: 10946,
17711 Sawise nelpon metode karo args: 6765, 10946 Sawise nelpon metode karo
args: 4181, 6765
: 2584, 4181
Sawise nelpon metode karo args: 1597, 2584
Sawise nelpon metode karo args: 987, 1597
Sawise nelpon metode karo args: 610, 987
Sawise nelpon metode karo args: 377, 610
Sawise nelpon metode karo args: 233, 377
Sawise cara nelpon karo args: 144, 233
Sawise cara nelpon karo args: 89, 144
Sawise cara nelpon karo args: 55, 89
Sawise cara nelpon karo args: 34, 55
Sawise cara nelpon karo args: 21, 34
Sawise nelpon metode karo args: 13, 21
Sawise nelpon metode karo args: 8, 13
Sawise nelpon metode karo args: 5, 8
Sawise nelpon metode karo args: 3, 5
Sawise nelpon metode karo args: 2, 3
Sawise nelpon metode karo args : 1, 2
Sawise nelpon metode karo args: 1, 1
Sawise nelpon metode karo args: 0, 1

Mangkene visualisasi apa sing kedadeyan.

Ayo ngomong maneh: cara printFibonacciWithCondition diarani. Iku ngetung nomer saiki. Yen cocog karo kita, banjur kita nampilake lan nelpon metode printFibonacciWithCondition maneh kanthi argumen anyar.

Anggere cara rekursif diarani, iki diarani "turun rekursif". Nalika rekursif mungkasi lan cara telpon bali, kita ngomong yen tumpukan telpon "unwinding".

Rekursi minangka topik sing menarik ing pemrograman. Kanggo entuk pegangan sing luwih apik babagan materi, ayo ngganti tugas kita rada. Tugas anyar kanggo output, ing urutan mudhun, kabeh nomer ing seri Fibonacci sing ora ngluwihi Integer.MAX_VALUE . Kita wis nulis kabeh kode sing dibutuhake kanggo tugas iki. Kabeh sing isih ana yaiku ngganti urutan nampilake nomer saiki lan nelpon metode rekursif. Yaiku, ing conto pisanan, nomer sing diwilang ditampilake nalika "turun", nanging saiki kita kudu "mudhun menyang ngisor" lan banjur nampilake nomer "ing dalan bali". Lan mesthi, ing cara utama , kita ngganti rong nomer awal saka urutan (nol lan siji) sawise nampilake sawise kita nelpon cara rekursif. Kanggo maca,cara.

public class Fibonacci {
    public static void main(String[] args) {
        printFibonacciWithCondition(0, 1);
        System.out.println(1);
        System.out.println(0);
    }

    private static void printFibonacciWithCondition(long penultimate, long previous) {
        long current = penultimate + previous;
        if (current > Integer.MAX_VALUE) {
            return;
        }
        printFibonacciWithCondition(previous, current);
        System.out.println(current);
    }
}

Output bakal:

1836311903
1134903170
701408733
433494437
267914296
165580141
102334155
63245986
39088169
24157817
14930352
9227465
5702887
3524578
2178309
1346269
832040
514229
317811
196418
121393
75025
46368
28657
17711
10946
6765
4181
2584
1597
987
610
377
233
144
89
55
34
21
13
8
5
3
2
1
10
_