Программа, которая считает числа Леонарда в диапазоне от A до B

Почему-то не получается вывести результат.

import java.util.Scanner;

public class Leonardo {

    private static final String NUMBER_A = "Введите число а: ";

    private static final String NUMBER_B = "Введите число b: ";

    private static final String ANSVER = "Количество чисел: ";


    public static void main(String[] args){

        Scanner console = new Scanner(System.in);

        int a = readDoubleValueFromConsole(console, NUMBER_A);

        int b = readDoubleValueFromConsole(console,NUMBER_B);

        printResult(countFromAtoB(a, b), ANSVER);


    }
    public static int countFromAtoB(int a, int b) {
        if (a > b) {
            return 0; // Некорректный ввод: a должно быть меньше или равно b
        } else {
            int count = 0;
            int leonardoNumber = 1;

            while (leonardoNumber <= b) {
                if (leonardoNumber >= a) {
                    count += 1;
                }
                leonardoSubsequence(leonardoNumber);
            }
            return count;
        }
    }
    public static int leonardoSubsequence(int n) {
        if (n == 0) return 1; // L(0) = 1
        if (n == 1) return 1; // L(1) = 1
        return leonardoSubsequence(n - 1) + leonardoSubsequence(n - 2) + 1; // L(n) = L(n-1) + L(n-2) + 1
    }
    //ввод
    public static int readDoubleValueFromConsole(Scanner console, String hint) {
        while (true) {
            System.out.printf(" %s = ", hint);
            String line = console.nextLine();
            try {
                return (int) Double.parseDouble(line);
            } catch (Exception e) {
                System.out.printf("  значение %s - некорректно!%n", line);
            }
        }
    }
    //вывод
    public static void printResult(int result, String hint){
        System.out.print(hint);
        System.out.print(result);
    }

}

Ответы (1 шт):

Автор решения: Nowhere Man

В представленном коде результат может быть выведен, если НЕ будет выполнено начальное условие в цикле while (leonardoNumber <= b) метода countFromAtoB, то есть, если введённая пользователем верхняя граница диапазона b окажется меньше минимального числа Леонардо L(0) = 1.

Если же данный цикл начнёт выполняться, он окажется бесконечным, так как ни одна из переменных, входящих в его условие, НЕ изменяется внутри самого цикла, и программа просто зависнет.

При этом, не поможет и присваивание переменной leonardoNumber результата, возвращаемого из метода leonardoSubsequence, так как в начале он будет всегда возвращать 1, т.е. опять-таки leonardoNumber будет сохранять значение.

Поэтому необходимо ввести переменную n для индекса числа Леонардо, и исправить логику в цикле:

public static int countFromAtoB(int a, int b) {
    if (a > b || b < 1) {
        return 0;
    }
    int count = 0;
    int n = 0;

    while (true) {
        int leonardoNumber = leonardoSubsequence(n++);
        if (leonardoNumber > b) {
            break;
        }
        if (leonardoNumber >= a) {
            count++;
        }
    }
    return count;
}

Однако следует заметить, что такое исправленное решение не является оптимальным, так как при рекурсивном вычислении чисел Леонардо для N > 1 каждый раз теряется информация о ранее вычисленных значениях.

Можно либо заменить рекурсию обычным циклом с переприсваиванием ранее вычисленных значений, либо накапливать такие значения в списке или мапе при рекурсивных вызовах для их повторного использования (мемоизация).

// список для хранения чисел Леонардо
// инициализируется двумя первыми числами Леонардо с индексами 0 и 1
private static List<Integer> leos = new ArrayList<>(Arrays.asList(1, 1));

public static int leonardoSubsequence(int n) {
    if (n < leos.size()) {
        return leos.get(n); // возвращаем известное N-е число
    }
    int ln = leonardoSubsequence(n - 1) + leonardoSubsequence(n - 2) + 1;
    leos.add(n, ln); // запоминаем только что вычисленное "новое" значение
    return ln;
}
→ Ссылка