JavaRush /Java Blog /Random-KO /Java의 재귀

Java의 재귀

Random-KO 그룹에 게시되었습니다
재귀가 무엇인지 이해하려면 재귀가 무엇인지 이해해야 합니다. 사실 이러한 기능을 이해하는 데는 어려운 것이 없으며 한 번만 잘 이해하면 됩니다. 그리고 프로그래밍에 관해서는 연습하세요. Java의 재귀 - 1재귀는 프로그래밍뿐만 아니라 실제 생활에서도 발생합니다. 거울을 손에 들고 다른 거울 앞에 섭니다. 반사의 반사는 반사 등에 반영됩니다. 무한대로 들어가는 무한한 수의 반사를 볼 수 있습니다. "실제" 재귀에 대한 자세한 내용은 " 성촉절 기념... " 기사에서 실제 세계에서 프로그래머의 일상 생활로 돌아가 보겠습니다. 간단한 정의: Java의 재귀 함수는 자신을 호출하는 함수입니다. 매우 간단하고 매우 해로운 예를 들어 보겠습니다.
public void recursionFucn() {
    System.out.println("Привет, JavaRush!");
    recursionFucn();
}
왜 해로운가요? 직접 확인해 보시는 걸 추천드려요! 이 모험을 하기로 결정했다면(프로그래머 여러분!) JVM이 어떤 오류를 던질지 주석에 적어주세요. =) 이 예와 다른 많은 예는 Java에서 재귀를 사용할 때 다음을 사용하여 접근해야 함을 보여줍니다. 주의. 문제 해결을 위한 그러한 접근 방식이 어디서, 언제, 왜 정당화되는지 이해하고, 귀하의 기능이 프로그램 실행을 "성촉절"로 바꾸지 않는지 확인해야 합니다. 재귀를 이해하기 위한 두 가지 더 중요한 정의가 있습니다.
  • 재귀 기준 - 재귀 호출 블록을 종료하기 위한 조건 - 재귀를 호출할 필요가 없는 조건에서 문제에 대한 기본 솔루션입니다.
  • 재귀 단계는 매개변수를 변경할 때 함수가 자신을 호출하는 단계입니다.
재귀 함수를 사용하는 전형적인 예는 숫자의 계승을 계산하는 것입니다. 잊어버린 경우를 대비해 상기시켜 드리겠습니다. 양의 정수 N(N으로 표시됨)의 계승은 의 숫자의 곱입니다 1 до N: N! = 1 * 2 * 3 * … (N - 1) * N. 그건 그렇고, 정의에 따라 0의 계승은 1과 같습니다. 따라서 양의 정수와 0에 대해 계승을 찾을 수 있습니다 (수학 언어에서-음이 아닌 정수 집합 또는 자연수 집합에 대해) 영). 루프를 사용하여 팩토리얼을 프로그래밍할 수 있다는 것을 이해하신 것 같습니다. 실제로 이 문제를 해결하기 위한 비재귀적 방법은 다음과 같습니다.
private int fact(int n) {
    int result = 1;
    for (int i = 1; i <= n; i++) {
        result = result * i;
    }
    return result;
}
숫자가 양수이고 정수인지 확인하는 검사와 0에 대한 별도의 검사를 추가해 보겠습니다.
private int fact(int n) {
    if (n < 0) {
        System.out.println("Зачем тебе факториал из отрицательного числа?");
         return null;
    }
    int result = 1;
    if (n == 0) {
        return result;
    }
    for (int i = 1; i <= n; i++) {
        result = result * i;
    }
    return result;
}
이제 이 문제를 재귀적으로 해결하기 위한 메소드 코드를 제공하겠습니다.
private int factorial(int n) {
    int result = 1;
    if (n == 1 || n == 0) {
        return result;
    }
    result = n * factorial(n-1);
    return result;
}
호출에 대한 출력 결과를 살펴보겠습니다.
System.out.println(factorial(0));
System.out.println(factorial(1));
System.out.println(factorial(2));
System.out.println(factorial(3));
System.out.println(factorial(4));
System.out.println(factorial(5));
System.out.println(factorial(6));
우리는 기대값을 얻습니다:
1
1
2
6
24
120
720
좋은 출력을 추가하고 더 큰 숫자에 대한 계승을 계산해 보겠습니다.
private int factorial(int n) {
    int result = 1;

    if (n == 0) {
        System.out.print(" = ");
        return result;
    }
    if (n == 1) {
        System.out.print(" * 1 = ");
        return result;
    }

    System.out.print(n);
    if (n != 2) {
        System.out.print(" * ");
    }

    result = n * factorial(n-1);
    return result;
}


System.out.println(factorial(15) + "!");
우리는 다음을 얻습니다: 15 * 14 * 13 * 12 * 11 * 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 1 307 674 368 000! 이 경우 재귀 함수의 사용은 정당하고 안전합니다. 우리는 재귀 블록을 종료하기 위한 조건을 명확하게 정의했으며 그것이 달성 가능하다고 확신합니다. 음수가 아닌 정수를 입력하고 숫자가 0 또는 1과 같으면 숫자가 더 크면 결과를 반환합니다. , 우리는 결과에 숫자의 함수를 곱합니다 n-1. 3의 계승을 예로 사용하면 다음과 같습니다.
factorial(3) внутри себя выполнит следующее:
	result = 3 * factorial(2); (рекурсивный вызов)

	factorial(2) внутри себя выполнит следующее:
		result = 2 * factorial(1); (рекурсивный вызов)

		factorial(1) вернет 1 (базис рекурсии)

	factorial(2) вернет 2 * 1

factorial(3) вернет 3 * 2 * 1
사용 시 주의 사항: 이 기능의 취약점은 무엇입니까? 메소드에 매개변수로 음수를 제공하면 검사가 수행됩니다.
if (n == 1 || n == 0) {
    return result;
}
말도 안 되고 우리는 스스로를 부르는 끝없는 순환에 빠지게 될 것입니다. 음수가 아닌지 검사를 추가하는 것이 좋습니다.
if (n < 0) {
	System.out.println(«Зачем тебе факториал отрицательного числа?»);
	return null;
}
그리고 모든 것이 잘 될 것입니다. 한 방법이 다른 방법에 비해 어떤 장점이 있나요? 큰 차이는 없어 보이지만 실제로 많은 재귀 호출은 성능과 메모리 소비에 부정적인 영향을 미칩니다. 호출 스택은 거의 제어할 수 없는 리소스이며 동일한 재귀 함수를 호출하는 조건이 다릅니다. 이 리소스와 관련된 문제가 발생할 수도 있고 그렇지 않을 수도 있습니다. 반복(예: 순환)을 사용하여 해결된 거의 모든 문제는 for-each재귀적으로 해결될 수도 있습니다. 재귀의 장점은 가독성과 작성 용이성입니다. 위에서 단점에 대해 이야기했습니다. "발에 총을 쏘는" 기회는 환상이 아닙니다. 소위 "복잡한 재귀"를 사용할 때는 더욱 주의해야 합니다. 함수는 함수를 호출하는 A()함수를 호출합니다 . 이러한 문제를 해결하려면 재귀가 어떻게 작동하는지 완전히 이해해야 합니다. 이러한 작업의 예: 의 값을 계산합니다 . 위에서 논의한 것처럼 계승은 음수가 아닌 정수 집합에 대해 정의됩니다. 마지막으로 솔루션 코드를 알려드리겠습니다. 복합 재귀는 두 가지 방법으로 구성됩니다. B()A()x^n/(n!)
private double calculate(int x, int n) {
    return power(x, n) / n;
}
private double power(int x, int n) {
    if (n == 1) return x;
    return x * calculate(x, n - 1);
}
재귀를 입력하려면 메소드를 사용합니다. calculate이 메소드는 메소드를 호출하고 power, 메소드는 다시 메소드를 호출합니다 calculate. 우리는 거듭제곱법에서 재귀 기반을 정의했습니다.
if (n == 1) return x;
재귀 단계도 여기에 정의되어 있습니다.
return x * calculate(x, n - 1);
남은 것은 입력 데이터의 유효성에 대한 검사를 추가하는 것입니다.
  • 0이 아닌 모든 숫자는 0의 거듭제곱과 같습니다 1. 그렇다면 . n = 0_ n! = 1그것을 반환해야합니다 1.
  • 0의 거듭제곱은 0과 같습니다.
  • 우리는 유형의 불확실성을 고려하지 않으며 0^0그러한 입력 데이터를 유효하지 않은 것으로 받아들입니다.
모든 검사를 통해 방법은 다음과 같습니다.
private double calculate(int x, int n) throws ArithmeticException {
    if (x == 0 && n == 0) {
        throw new ArithmeticException("Невалидные входные данные: Неопределенность типа 0^0");
    }

    if (n < 0) {
        throw new ArithmeticException("Невалидные входные данные: Факториал из отрицательного числа!");
    }

    if (n == 0) {
        return 1;
    }

    if (x == 0) {
        return 0;
    }

    if (x == 0) {
        return 0;
    }
    return power(x, n) / n;
}

private double power(int x, int n) {
    if (n == 1) return x;
    return x * calculate(x, n - 1);
}
음, 함수를 호출할 때 오류를 잡아야 한다는 점을 기억해야 합니다.
try {
    System.out.println(calculate(x, n));
} catch (ArithmeticException e) {
    System.out.println(e.getMessage());
}
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION