প্রস্থান শর্ত ছাড়াই পুনরাবৃত্ত কোডের উদাহরণ

আসুন একটি রিকার্সিভ সমস্যা আরেকবার দেখি। একটি উদাহরণ হিসাবে, ফিবোনাচি সংখ্যা গণনা বিবেচনা করুন. সবাই স্মরণ করবে যে ফিবোনাচি ক্রম হল একটি সংখ্যাসূচক ক্রম যেখানে প্রথম দুটি সংখ্যা 0 এবং 1 এবং প্রতিটি পরবর্তী সংখ্যা পূর্ববর্তী দুটি সংখ্যার যোগফলের সমান।

আসুন এই সংখ্যাগুলি গণনা এবং প্রদর্শন করার জন্য কোড লিখি:

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);
    }
}

পুনরাবৃত্ত প্রিন্ট ফিবোনাচি পদ্ধতির প্রথম কলের আগে , ক্রমটির প্রথম দুটি সংখ্যা প্রিন্ট করুন: শূন্য এবং একটি। আমাদের এটি করতে হবে কারণ পুনরাবৃত্ত পদ্ধতিটি শুধুমাত্র ইনপুট প্যারামিটারের যোগফল প্রদর্শন করে, প্যারামিটারগুলিকে নয়।

কোডটি ঠিক আছে বলে মনে হচ্ছে: আমরা দুটি সংখ্যা পেয়েছি, তাদের যোগফল গণনা করি, এটি কনসোলে প্রিন্ট করি এবং প্রিন্ট ফিবোনাচি পদ্ধতিটিকে আবার পুনরাবৃত্তি করে কল করি। আমরা আর্গুমেন্ট হিসাবে পূর্ববর্তী সংখ্যা (পূর্ববর্তী) এবং বর্তমান সংখ্যা (বর্তমান) পাস করি।

আসলে, কোড দুটি ত্রুটি আছে. আপনি কোড চালালে আপনি তাদের দেখতে পারেন.

প্রথম ত্রুটি দীর্ঘ টাইপ উপচে পড়া হয়. আমাদের অনুক্রমের 104 তম সংখ্যাটি নেতিবাচক, এটি নির্দেশ করে যে দীর্ঘ টাইপটি ওভারফ্লো হয়েছে৷

দ্বিতীয় ত্রুটি ভিন্ন। মোটামুটি 12,000 সংখ্যা গণনা করার পরে, আমরা পাই:

থ্রেড "প্রধান" java.lang.StackOverflowError-এ ব্যতিক্রম

এখন জাভাতে একটি মেথড কল স্ট্যাক কী তা মনে করার উপযুক্ত সময়। জাভা মেশিন সমস্ত ফাংশন কলের রেকর্ড রাখে। এটি করার জন্য, এটি স্ট্যাক নামে একটি বিশেষ ধরনের সংগ্রহ ব্যবহার করে। যখন একটি ফাংশন অন্যটিকে কল করে, তখন জাভা মেশিন স্ট্যাকের উপর একটি নতুন স্ট্যাকট্রেস এলিমেন্ট পুশ করে। ফাংশন শেষ হলে, এই উপাদানটি স্ট্যাক থেকে সরানো হয়। তদনুসারে, স্ট্যাক সর্বদা ফাংশন কল স্ট্যাকের বর্তমান অবস্থা সম্পর্কে আপ-টু-ডেট তথ্য সংরক্ষণ করে। StackTraceElement-এর জন্য ডকুমেন্টেশন বলে যে "একটি স্ট্যাক ওভারফ্লো ঘটলে এটি নিক্ষেপ করা হয় কারণ একটি অ্যাপ্লিকেশন খুব গভীরভাবে পুনরাবৃত্তি হয়।" একটি চলমান JVM মেথড কল স্ট্যাক সংরক্ষণ করার জন্য মেমরির একটি বিশেষ ক্ষেত্র রয়েছে। এই মেমরি এলাকার আকার OS এবং JVM সেটিংসের উপর নির্ভর করে। পদ্ধতি কল স্ট্যাক নিজেই ছাড়াও, আদিম ভেরিয়েবল (পদ্ধতি প্যারামিটারের নির্দিষ্ট মান) এবং রেফারেন্স ভেরিয়েবলের ঠিকানা (একটি মেমরি এলাকায় যাকে "হিপ" বলা হয়) এই বিশেষ মেমরি এলাকায় সংরক্ষণ করা হয়। কল স্ট্যাকের অ্যাক্সেস LIFO নীতি অনুসরণ করে।

প্রস্থান শর্ত সহ সঠিক উদাহরণ

কোডের দ্বিতীয় সমস্যাটি ঠিক করে শুরু করা যাক।

আসুন সমস্যাটি সমাধান করার চেষ্টা করি: যদি স্ট্যাকটি খুব ছোট হয়, তাহলে আসুন এটিকে আরও বড় করা যাক। এটি করার জন্য, "-Xss" পতাকা দিয়ে JVM শুরু করুন এবং স্ট্যাকের জন্য কত মেমরি বরাদ্দ করতে হবে তা নির্দিষ্ট করুন। আসুন 5 মেগাবাইট বরাদ্দ করার চেষ্টা করি। আইডিইএতে এটি দেখতে কেমন হবে:

আমরা আউটপুট দৈর্ঘ্য বৃদ্ধি পরিচালিত. এখন প্রায় 12,000 সংখ্যার মধ্যে সীমাবদ্ধ না থেকে অনুক্রমের 49,000 এর বেশি সংখ্যা গণনা করতে পারে। কিন্তু কিছু সময়ে, আমরা এখনও একটি StackOverflowError পাই ।

আপনি স্ট্যাকের আকার বাড়ানোর চেষ্টা করতে পারেন, কিন্তু এটি মৌলিক সমস্যার সমাধান করে না। সুতরাং, আসুন যুক্তিতে একটি সমস্যা সন্ধান করি। পুনরাবৃত্তি বন্ধ হলে একটি বিন্দু থাকা আবশ্যক। অন্য কথায়, এমন কিছু শর্ত থাকতে হবে যা নির্ধারণ করে কখন পুনরাবৃত্ত পদ্ধতিটি আর কল করা হবে না, কল স্ট্যাকটিকে খোলার অনুমতি দেয়। এই ধরনের একটি শর্ত সংজ্ঞায়িত করার জন্য, আসুন আমাদের উদ্দেশ্যটি স্পষ্ট করা যাক: ফিবোনাচি সিরিজটি প্রদর্শন করুন যতক্ষণ না এর সংখ্যাগুলি Integer.MAX_VALUE থেকে কম হয় ।

আসুন একটি নতুন printFibonacciWithCondition পদ্ধতি লিখি যা এই শর্তটি বিবেচনা করবে। এবং আমরা মূল পদ্ধতিতে নতুন সংশোধন পদ্ধতি বলব।

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);
    }
}

কোডটি চালানোর পরে, আমরা দেখতে পাচ্ছি যে আউটপুটটি 1836311903 নম্বর দিয়ে শেষ হয়েছে। এর আগের সংখ্যাটি হল 1134903170। এই সংখ্যার যোগফল হল 2_971_215_073, যা প্রকৃতপক্ষে Integer.MAX_VALUE (2_147_483_47) এর চেয়ে বেশি

এই পরিবর্তনটি স্বয়ংক্রিয়ভাবে দীর্ঘ ওভারফ্লো বাগ সংশোধন করেছে। আপনি যদি সিরিজের আরও গণনা করতে চান তবে আপনাকে অন্যান্য ডেটা প্রকার ব্যবহার করতে হবে, যেমন BigInteger

পুনরাবৃত্ত বংশদ্ভুত এবং unwinding

আসুন ধাপে ধাপে বিশ্লেষণ করি কিভাবে আমাদের কোড নির্বাহ করা হয়। এটি করার জন্য, আমরা একটি ইকো পদ্ধতি যোগ করব এবং 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);
        }
    }
}

প্রোগ্রাম আমাদের এই আউটপুট দেয়:

0
1
আর্গস সহ মেথড কল করার আগে: 0, 1. বর্তমান নম্বর = 1
আর্গস সহ মেথড কলের আগে: 1, 1. বর্তমান নম্বর = 2
আর্গস সহ মেথড কলের আগে: 1, 2. বর্তমান নম্বর = 3
আর্গসের সাথে মেথড কলের আগে: 2, 3. বর্তমান নম্বর = 5
আর্গস সহ মেথড কলের আগে: 3, 5. বর্তমান নম্বর = 8
আর্গস সহ মেথড কলের আগে: 5, 8. বর্তমান নম্বর = 13
আর্গস সহ মেথড কলের আগে: 8, 13। বর্তমান নম্বর = 21
আর্গস সহ মেথড কল করার আগে: 13, 21. বর্তমান নম্বর = 34
আর্গস সহ মেথড কলের আগে: 21, 34. বর্তমান নম্বর = 55
আর্গস সহ মেথড কলের আগে: 34, 55. বর্তমান নম্বর = 89
আর্গসের সাথে মেথড কলের আগে: 55, 89. বর্তমান সংখ্যা = 144
আর্গসের সাথে মেথড কল করার আগে: 89, 144। বর্তমান নম্বর = 233
আর্গসের সাথে মেথড কল করার আগে: 144, 233। বর্তমান নম্বর = 377
আর্গসের সাথে মেথড কল করার আগে: 233, 377। বর্তমান নম্বর = 610
আর্গসের সাথে মেথড কলের আগে: 377, 610. বর্তমান নম্বর = 987
আগে পদ্ধতিতে আর্গস সহ কল ​​করুন: 610, 987। বর্তমান নম্বর = 1597
আগে পদ্ধতিতে আর্গস সহ কল ​​করুন: 987, 1597। বর্তমান নম্বর = 2584
আর্গস সহ মেথড কলের আগে: 1597, 2584। বর্তমান নম্বর = 4181
এর আগে আর্গস সহ কল ​​করুন: 2584, 4181। বর্তমান নম্বর = 6765
আগে পদ্ধতিতে আর্গস দিয়ে কল করুন: 4181, 6765। বর্তমান নম্বর = 10946
আর্গস সহ মেথড কল করার আগে: 6765, 10946। বর্তমান নম্বর = 17711
মেথড কল করার আগে আর্গস: 41717। বর্তমান সংখ্যা = 28657
আর্গসের সাথে মেথড কল করার আগে: 17711, 28657। বর্তমান নম্বর = 46368
আর্গসের সাথে মেথড কল করার আগে: 28657, 46368। বর্তমান নম্বর = 75025
আর্গসের সাথে মেথড কল করার আগে: 46368, 75025। বর্তমান নম্বর = 121368, 75025 এর আগে,
args-এর সাথে কল করুন 1213525 121393. বর্তমান নম্বর = 196418
আর্গস সহ মেথড কল করার আগে: 121393, 196418। বর্তমান নম্বর = 317811
আর্গসের সাথে মেথড কলের আগে: 196418, 317811। বর্তমান নম্বর
= 514229 পদ্ধতিতে কল করার আগে, C1218 3528 আরগস নম্বর দিয়ে কল করুন। 040
এর আগে পদ্ধতি আর্গস দিয়ে কল করুন: 514229, 832040। বর্তমান নম্বর = 1346269
আগে পদ্ধতিতে আর্গস দিয়ে কল করুন: 832040, 1346269।
বর্তমান নম্বর = 2178309 আর্গসের সাথে মেথড কল করার আগে: 1346269, 2178309, 2178309 = 4257 C
আর্গস এর সাথে মেথড কল করার আগে: 2178309, 3524578। বর্তমান নম্বর = 5702887
আর্গসের সাথে মেথড কল করার আগে: 3524578, 5702887। বর্তমান নম্বর = 9227465 আর্গসের সাথে মেথড কল করার আগে: 5702887, 922887 এর আগে, 922887 এর আগে, 922887,
922537
নম্বরের আগে 9227465, 14930352. বর্তমান নম্বর = 24157817
আর্গসের সাথে মেথড কল করার আগে: 14930352, 24157817। বর্তমান নম্বর = 39088169
আর্গসের সাথে মেথড কলের আগে: 24157817, 39088169। বর্তমান নম্বর 36188368 এর আগে বর্তমান
কল , 63245986. বর্তমান সংখ্যা = 102334155
পদ্ধতির আগে args দিয়ে কল করুন: 63245986, 102334155। বর্তমান নম্বর = 165580141
পদ্ধতিতে কল করার আগে args: 102334155, 165580141। বর্তমান নম্বর = 267914296
মেথড কল করার আগে আর্গস দিয়ে: 165580141, 267914296। বর্তমান নম্বর = 433494437
আর্গসের সাথে মেথড কল করার আগে: 267914296, 433494437। বর্তমান নম্বর = 701408733
মেথড কলের আগে আর্গস নম্বর: 4347ur = 4347। 1134903170
মেথড কল করার আগে args সহ: 701408733, 1134903170. বর্তমান নম্বর = 1836311903
আফটার মেথড কল এর সাথে args: 701408733, 113490317
আফটার মেথড কল এর সাথে args: 433494437, 701408733 আফটার মেথড কল এর সাথে args: 264743431 এর সাথে কল করুন
5580141
, 267914296
পদ্ধতিতে কল করার পরে আর্গস সহ: 102334155, 165580141
আফটার মেথড কল এর সাথে আর্গস: 63245986, 102334155
আফটার মেথড কল এর সাথে আর্গস: 39088169, 63245986
আফটার মেথড কল এর সাথে আর্গস: 24157817, 39088169
আফটার মেথড কল এর সাথে আর্গস: 14930352, 24157817 আর্গস এর সাথে মেথড কল করার পর: 9227465,
14930352 আর্গস এর সাথে মেথড কলের পর: 5702887, 5702887, 5702887
,
67527 67520 পরে মেথড কল 887 পদ্ধতির
পরে args দিয়ে কল করুন : 2178309, 3524578
আফটার মেথড কল এর সাথে আর্গস: 1346269, 2178309
আফটার মেথড কল এর সাথে আর্গস: 832040, 1346269 আফটার মেথড কল উইথ আর্গস: 514229
, 832040 মেথড কল এর পরে আর্গস এর সাথে কল করুন: 31941
এর
সাথে কল করুন। 8, 317811 এর
পর args সহ মেথড কল: 121393, 196418
আফটার মেথড কল উইথ আর্গস: 75025, 121393
আর্গস এর সাথে মেথড কলের পর: 46368, 75025
আফটার মেথড কল এর সাথে আর্গস: 28657, 46368
আফটার মেথড কল এর সাথে আর্গস: 17711, 28657 আফটার
মেথড কল উইথ আর্গস: 10946, 17711 আর্গস এর সাথে মেথড কলের
পর: 6765, 10946
আর্গস এর সাথে মেথড কলের পর: 4181, 6765
এর সাথে মেথড কল : 2584, 4181
আফটার মেথড কল এর সাথে আর্গস: 1597, 2584
আফটার মেথড কল এর সাথে আর্গস: 987, 1597
আফটার মেথড কল এর সাথে আর্গস: 610, 987
আর্গস এর সাথে মেথড কলের পর: 377, 610 আর্গস এর সাথে
মেথড কলের পরে: 233, 377
এর পরে args সহ মেথড কল: 144, 233
আফটার মেথড কল with args: 89, 144
after method call with args: 55, 89
after method call with args: 34, 55
after method call with args: 21, 34
আফটার মেথড কল এর সাথে আর্গস: 13, 21
আফটার মেথড কল এর সাথে আর্গস: 8, 13
আফটার মেথড কল এর সাথে আর্গস: 5, 8
আর্গস এর সাথে মেথড কলের পর: 3, 5
আর্গস এর সাথে মেথড কলের পরে: 2, 3 আর্গস
দিয়ে মেথড কলের পরে : 1, 2
আফটার মেথড কল এর সাথে আর্গস: 1, 1
আর্গস এর সাথে মেথড কলের পর: 0, 1

এখানে যা ঘটছে তার একটি ভিজ্যুয়ালাইজেশন।

আবার বলা যাক: printFibonacciWithCondition পদ্ধতি বলা হয়। এটি বর্তমান সংখ্যা গণনা করে। যদি এটি আমাদের জন্য উপযুক্ত হয়, তাহলে আমরা এটি প্রদর্শন করি এবং নতুন আর্গুমেন্ট সহ আবার printFibonacciWithCondition পদ্ধতিতে কল করি।

যতক্ষণ রিকারসিভ মেথড বলা হচ্ছে, একে "রিকারসিভ ডিসেন্ট" বলা হয়। যখন রিকার্সিভ টার্মিনেট হয় এবং মেথড কল রিটার্ন হয়, আমরা বলি যে কল স্ট্যাক "আনওয়াইন্ডিং"।

Recursion প্রোগ্রামিং একটি আকর্ষণীয় বিষয়. উপাদানের উপর একটি ভাল হ্যান্ডেল পেতে, আসুন আমাদের কাজটি সামান্য পরিবর্তন করি। নতুন কাজ হল ফিবোনাচ্চি সিরিজের সমস্ত সংখ্যাকে অবরোহ ক্রমে আউটপুট করা যা Integer.MAX_VALUE এর বেশি নয় । আমরা ইতিমধ্যে এই কাজের জন্য প্রয়োজনীয় সমস্ত কোড লিখেছি। যা অবশিষ্ট থাকে তা হল বর্তমান নম্বর প্রদর্শন এবং রিকার্সিভ পদ্ধতিতে কল করার ক্রম অদলবদল করা। অর্থাৎ, প্রথম উদাহরণে, গণনা করা সংখ্যাটি "অন্তর্ভুক্ত" এর সময় প্রদর্শিত হয়েছিল, কিন্তু এখন আমাদের "খুব নীচে নামতে হবে" এবং তারপরে "ব্যাক আপের পথে" সংখ্যাগুলি প্রদর্শন করতে হবে। এবং অবশ্যই, মূল পদ্ধতিতে, আমরা ক্রমানুসারের দুটি প্রাথমিক সংখ্যা (শূন্য এবং এক) অদলবদল করি এবং তাদের প্রদর্শন করার পরে আমরা রিকার্সিভ পদ্ধতি বলি। পঠনযোগ্যতার জন্য,পদ্ধতি

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);
    }
}

আউটপুট হবে:

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
1
0