우선 함수에 대해 알아보자
함수란 값을 입력받아 특정 연산을 수행하여 결과를 반환하는 것이다.

다음은 재귀 함수에 대해 알아보자
자기 자신을 부르는 함수
대표적인 예시는 팩토리얼이다.
import java.util.Scanner;
public class Main2 {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n =sc.nextInt();
System.out.println(getFactorial(n));
}
public static int getFactorial(int n){
if (n==0)return 1;
else return n*factory(n-1);
}
}
위 코드를 보면 계속해서 자기 자신을 부른다.
즉 아래 그림과 같이 계속해서 getFactorial이 불러진다.

n의 값이 0이 될 때까지 자기 자신을 부른다.
이후 다시 올라가면서 돌아간다.
재귀는 아래 부르는 재귀 호출이 잘 작동된다고 가정하고 코드를 작성한다.
즉 getFactorial(4)는 3,2,1,0이 잘 작동한다고 가정한다.
우리는 getFactorial이 n팩토리얼을 잘 반환하는지 궁금하다.
따라서 아래와 같은 증명을 할 수 있다.
수학적 귀납법
명제 P(n)이 모든 자연수 n에 대하여 성립함을 보이자.
*증명 순서
P(1)이 참임을 보인다.
P(k)가 성립한다고 가정한 후, P(k+1)이 성립함을 보인다.
따라서 모든 자연수 n에 대하여 P(n)이 성립한다.

1일 때 되면, 2일 때 된다. 2일 때 되면 3일 때 된다...... n이 된다.

즉 k가 된다면, k+1도 가능하다.
****재귀 함수 디자인****
1. 함수의 역할을 말로 정확하게 정의한다.
2. 기저 조건에서 함수가 제대로 동작함을 보인다.
3. 함수가 제대로 동작한다고 가정하고 함수를 완성한다.
재귀 함수를 이용한 Brute-Force
모든 경우를 시도해보는 코드를 짜기가 까다로운 경우
n 중 for문을 돌려야 한다??
ex) n개의 카드가 있고, 각각은 1부터 n까지의 번호를 갖는다. 이를 한 줄로 세울 수 있는 모든 경우를 출력하시오.
if) n =3
for(int i =1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
if(i!=j&&j!=k&&i!=k){
System.out.println("%d %d %d",i,j,k);
}
}
}
}
위 코드를 보면, n의 값만큼 for문이 증가한다. + n의 값이 변한다면 반복문을 처리하기 어렵다.
재귀 함수를 이용하면 아래와 같다.
void getResult(int current,int n, int result[]){
if (current>=n){
System.out.println(result);
}
for (int i=1;i<=n;i++){
if (isNotContaining(result,i)){
result[current] = i;
getResult(current+1,n,result);
result[current]=0;
}
}
}
currnet는 현재 배열의 index를 의미한다. n의 n까지의 번호 즉 n 중 for문이다.
result배열은 값을 넣어줄 배열이다.
isNotContaining은 중복 체크하는 함수이다.
맨 처음 값은 아래 그림과 같이 된다.

위 배열 current 가 0일 때 즉 배열의 index가 0일 때 1을 넣고, 그다음 index로 넘어간다.
그리고 index1에서는 앞에 1이 있으므로 2가 들어가고, 다음 index로 들어간다.
이것을 보면 index 0, 1, 2가 3중 for문을 돌리는 것과 같다.
즉 index 0의 값이 1이고, 그다음 index1의 값은 1일 때, index2의 값은 1,2,3이 들어온다.
이후 index1의 값이 2가 된 후, 다시 index 2의 값이 1,2,3 반복된다.
재귀 함수 예제
N개의 알파벳 중에서 R개를 나열할 수 있는 경우를 모두 출력하시오.
정답 코드
import java.util.Scanner;
public class Main{
static int n; //경우의 수
static int r; //for문의 수
static char[] arr;
static boolean[] check;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
r = sc.nextInt();
arr = new char[r];
check = new boolean[n];
alpa(0);
}
static void alpa(int x){
if (x>=r){
String str ="";
for (int arr: arr){
str+=(char)arr;
}
System.out.println(str);
return;
}
for (int i=0;i<n;i++){
char ch = (char)(i+'a');
if (check[i]==false){
check[i] = true;
arr[x] = ch;
alpa(x+1);
check[i] = false;
}
}
}
}
위 코드에서 중요하게 볼 것은 check배열인데, 이는 중복을 체크해주는 역할을 한다.
기저 조건의 경우 우리가 원하는 배열의 크기 이상이 되면 return 해준다.
다음 문제이다.

정답 코드
import java.util.Scanner;
public class Main {
static int[] arr;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //자리수
int k = sc.nextInt(); //1의수
arr = new int[n];
funtion(k,0);
}
static void funtion(int k,int index){
if (k==0){
for (int arr:arr){
System.out.print(arr);
}
System.out.println();
return;
}
for (int i=index;i<arr.length;i++) {
arr[i] = 1;
funtion(k - 1, i + 1);
arr[i] = 0;
}
}
}
이 코드는 기저 조건이 그 전과는 다르다.
1의 개수가 정해져 있으므로, 1을 모두 소모하게 되는 것이 기저 조건이다.
1을 초반에 다 소모하여도 뒤에 값은 0이 나오게 되는데, 이는 배열 초기값이 0으로 설정되기 때문이다.
우선 함수에 대해 알아보자
함수란 값을 입력받아 특정 연산을 수행하여 결과를 반환하는 것이다.

다음은 재귀 함수에 대해 알아보자
자기 자신을 부르는 함수
대표적인 예시는 팩토리얼이다.
import java.util.Scanner;
public class Main2 {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n =sc.nextInt();
System.out.println(getFactorial(n));
}
public static int getFactorial(int n){
if (n==0)return 1;
else return n*factory(n-1);
}
}
위 코드를 보면 계속해서 자기 자신을 부른다.
즉 아래 그림과 같이 계속해서 getFactorial이 불러진다.

n의 값이 0이 될 때까지 자기 자신을 부른다.
이후 다시 올라가면서 돌아간다.
재귀는 아래 부르는 재귀 호출이 잘 작동된다고 가정하고 코드를 작성한다.
즉 getFactorial(4)는 3,2,1,0이 잘 작동한다고 가정한다.
우리는 getFactorial이 n팩토리얼을 잘 반환하는지 궁금하다.
따라서 아래와 같은 증명을 할 수 있다.
수학적 귀납법
명제 P(n)이 모든 자연수 n에 대하여 성립함을 보이자.
*증명 순서
P(1)이 참임을 보인다.
P(k)가 성립한다고 가정한 후, P(k+1)이 성립함을 보인다.
따라서 모든 자연수 n에 대하여 P(n)이 성립한다.

1일 때 되면, 2일 때 된다. 2일 때 되면 3일 때 된다...... n이 된다.

즉 k가 된다면, k+1도 가능하다.
****재귀 함수 디자인****
1. 함수의 역할을 말로 정확하게 정의한다.
2. 기저 조건에서 함수가 제대로 동작함을 보인다.
3. 함수가 제대로 동작한다고 가정하고 함수를 완성한다.
재귀 함수를 이용한 Brute-Force
모든 경우를 시도해보는 코드를 짜기가 까다로운 경우
n 중 for문을 돌려야 한다??
ex) n개의 카드가 있고, 각각은 1부터 n까지의 번호를 갖는다. 이를 한 줄로 세울 수 있는 모든 경우를 출력하시오.
if) n =3
for(int i =1;i<=3;i++){
for(int j=1;j<=3;j++){
for(int k=1;k<=3;k++){
if(i!=j&&j!=k&&i!=k){
System.out.println("%d %d %d",i,j,k);
}
}
}
}
위 코드를 보면, n의 값만큼 for문이 증가한다. + n의 값이 변한다면 반복문을 처리하기 어렵다.
재귀 함수를 이용하면 아래와 같다.
void getResult(int current,int n, int result[]){
if (current>=n){
System.out.println(result);
}
for (int i=1;i<=n;i++){
if (isNotContaining(result,i)){
result[current] = i;
getResult(current+1,n,result);
result[current]=0;
}
}
}
currnet는 현재 배열의 index를 의미한다. n의 n까지의 번호 즉 n 중 for문이다.
result배열은 값을 넣어줄 배열이다.
isNotContaining은 중복 체크하는 함수이다.
맨 처음 값은 아래 그림과 같이 된다.

위 배열 current 가 0일 때 즉 배열의 index가 0일 때 1을 넣고, 그다음 index로 넘어간다.
그리고 index1에서는 앞에 1이 있으므로 2가 들어가고, 다음 index로 들어간다.
이것을 보면 index 0, 1, 2가 3중 for문을 돌리는 것과 같다.
즉 index 0의 값이 1이고, 그다음 index1의 값은 1일 때, index2의 값은 1,2,3이 들어온다.
이후 index1의 값이 2가 된 후, 다시 index 2의 값이 1,2,3 반복된다.
재귀 함수 예제
N개의 알파벳 중에서 R개를 나열할 수 있는 경우를 모두 출력하시오.
정답 코드
import java.util.Scanner;
public class Main{
static int n; //경우의 수
static int r; //for문의 수
static char[] arr;
static boolean[] check;
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
r = sc.nextInt();
arr = new char[r];
check = new boolean[n];
alpa(0);
}
static void alpa(int x){
if (x>=r){
String str ="";
for (int arr: arr){
str+=(char)arr;
}
System.out.println(str);
return;
}
for (int i=0;i<n;i++){
char ch = (char)(i+'a');
if (check[i]==false){
check[i] = true;
arr[x] = ch;
alpa(x+1);
check[i] = false;
}
}
}
}
위 코드에서 중요하게 볼 것은 check배열인데, 이는 중복을 체크해주는 역할을 한다.
기저 조건의 경우 우리가 원하는 배열의 크기 이상이 되면 return 해준다.
다음 문제이다.

정답 코드
import java.util.Scanner;
public class Main {
static int[] arr;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt(); //자리수
int k = sc.nextInt(); //1의수
arr = new int[n];
funtion(k,0);
}
static void funtion(int k,int index){
if (k==0){
for (int arr:arr){
System.out.print(arr);
}
System.out.println();
return;
}
for (int i=index;i<arr.length;i++) {
arr[i] = 1;
funtion(k - 1, i + 1);
arr[i] = 0;
}
}
}
이 코드는 기저 조건이 그 전과는 다르다.
1의 개수가 정해져 있으므로, 1을 모두 소모하게 되는 것이 기저 조건이다.
1을 초반에 다 소모하여도 뒤에 값은 0이 나오게 되는데, 이는 배열 초기값이 0으로 설정되기 때문이다.