nPr
==> n개
의 순서를 고려해서 r개의 열
을 세우는 것.
선택 한 것이 r개가 될때 까지 반복(재귀) 하는 것이 중요하다.
def dfs_permutation(array, r):
i_array = [(x, i) for i, x in enumerate(array)]
stack = [[i] for _, i in i_array]
return_list = []
while len(stack) > 0:
current = stack.pop()
for i in range(len(i_array)):
if i not in current:
temp = current + [i_array[i][1]]
if len(temp) == r:
elements = []
for idx in temp:
elements.append(i_array[idx][0])
return_list.extend([elements])
else:
stack.append(temp)
return return_list
print(dfs_permutation("ABCD", 2))
# > 출력
[['D', 'A'], ['D', 'B'], ['D', 'C'], ['C', 'A'], ['C', 'B'], ['C', 'D'], ['B', 'A'], ['B', 'C'], ['B', 'D'], ['A', 'B'], ['A', 'C'], ['A', 'D']]
"ABCD" 중 r개를 뽑는다고 생각 했을 때,
선택 한 것이 r개가 되게 하기 위하여
기준
을 제외한 나머지
에서 r - 1
개가 뽑힐 때 까지 선택을 한다.
즉 , "ABCD" 에서 2개를 순열로 선택 하는 것은
0. '기준'을 뽑고, "나머지" 에서 `r - 1개` 를 선택 하는 것.
1. A를 뽑고, "BCD" 에서 1개를 순열로 선택하는 것
2. B를 뽑고, "ACD" 에서 1개를 순열로 선택하는 것
3. C를 뽑고, "ABD" 에서 1개를 순열로 선택하는 것
4. D를 뽑고, "ABC" 에서 1개를 순열로 선택하는 것
이 모든 4가지 경우의 수를 다 더한 것과 같은 것을 의미한다.
r-1
을 계속해서 하다 보면 최종적으로
1
이된다 1
개씩 선택하며 r
개가 될 때까지 1개씩 선택한 것을 추가 하는 것으로 생각하면 된다
다시 위의 함수를 살펴보면, temp = current + [i_array[i][1]]
이 의미 하는 것을 하나 씩 살펴 보면,
1. current => 뽑아야 되는 갯수 r 이 될 때 까지
나머지에서 1개씩 선택한 것들을 더한 것이다.
즉, 위에서 말한 과정을 계속해서 더한 현재 값
인 것이다.
(아직, r개 만큼 선택 되지 않음)
2. [i_array[i][1]] =>
전체에서 1개를 선택 했음을 의미한다.
3. temp => 현재값과 선택 된 값을 더한 것
즉, 미래의 값.
def permutation(arr, r):
arr = sorted(arr)
used = [0 for _ in range(len(arr))]
return_array = []
def generate(chosen, used):
# 내가 원하는 만큼 뽑았으면, return
if len(chosen) == r:
return_array.append(''.join(chosen))
return
for i in range(len(arr)):
if not used[i]:
chosen.append(arr[i])
used[i] = 1
generate(chosen, used)
used[i] = 0
chosen.pop()
generate([], used)
return return_array
위에서의 설명과 마찬가지로, r
이 될 때까지 1개씩
더해주는 방법을 사용한다.
이 때, 재귀함수는 함수호출이 스택
으로 쌓이기 때문에,
# stack
| - | | - |
| - | | a.b |
| - | | a.c |
| - | | a.d |
| a | | a |
| b | | b |
| c | | c |
| d | | d |
---- -----
이런식으로 스택이 쌓이는데, A에서 호출된 함수에서 B가 chosen 에 들어 온 상태에서 generate()
함수를 실행하면, chosen
이 r 과 동일 함으로 , B가 chosen
에 들어온 시점에서의 generate() 함수는 return_array
에 값을 저장하며 실행이 종료 되고,
B는 chosen 에서 빠지고, A가 들어 있는 상태에서 C가 chosen
으로 들어오게 된다.
이후 같은 방법으로 계속 되며
A.D
가 마지막으로 실행이 종료 되면 A가 들어 와있는 상태에서의 함수실행도 종료가 되며
B가 들어가며 또 B가 B.A B.C B.D 에서의 함수를 실행한다.
이렇게 스택에 값이 다 끝날 때 까지 순열을 한다.
nCr
n개 중에서 r개를 선택 하는 것. (순서 상관 없이)0. '기준'을 뽑고, 한번이라도 선택 하지 않은 "나머지"
에서 `r - 1개` 를 선택 하는 것 들을 합
1. A를 뽑고, "BCD" 에서 1개를 순열로 선택하는 것
2. B를 뽑고, "CD" 에서 1개를 순열로 선택하는 것
3. C를 뽑고, "D" 에서 1개를 순열로 선택하는 것
4. D를 뽑고, "" 에서 1개를 순열로 선택하는 것
1~4 의 과정들의 합 = 조합
def combination(array, r):
chosen = []
if r > len(array):
return chosen
if r == 1:
for i in array:
chosen.append(i)
elif r > 1:
# r 개 만큼 빼주는 이유 (순서가 고려사항이 아니기 때문에, r개는 고려하지 않아도 앞서서 정해진다)
for i in range(len(array) - r + 1):
for temp in combination(array[i + 1:], r - 1):
chosen.append([array[i], temp])
return chosen
print(combination("ABCD", 2))
r을 계속해서 줄여가며, r 이 1이 될 때, 1개씩 선택한다.
남은 n개가 r보다 작게 되면, 현재 선택 된 것들을 다 반환한다.
그리고, 선택한 것에, 기준을 더해서 반환 해준다.
전체 배열을 다 검사하였으면, 함수가 종료 된다