[수학] 3. 경우의 수, 순열
1. 경우의 수
- 어떤 사건 혹은 일이 일어날 수 있는 경우의 가짓수를 수로 표현
일상 생활에서의 경우의 수
- 주사위 : 던지는 결과, 1~6 사이의 숫자이므로 경우의 수는 6
- 윷 : 던지는 결과, 도, 개, 걸, 윶, 모 이므로 경우의 수는 5
- 가위바위보 : 게임 결봐, 가위, 바위, 보 중에 하나를 낼 수 있으므로 경우의 수는 2
- 동전 : 던지는 결과, 앞면 혹은 뒷면이르모 경우의 수는 2
완전 탐색으로 경우의 수를 푸는 알고리즘
- 순열 : 서로 다른 n개의 원소 중에서 r을
중복 없이
골라순서에 상관 있게
나열하는 경우의 수nPr
- 조합 : 서로 다른 n개의 원수 중에서 r을
중복 없이
골라순서에 상관 없이
나열하는 경우의 수nCr
- 중복 순열 : 서로 다른 n개의 원소 중에서 r개를
중복 있게 골라 순서에 상관 있게
나열하는 경우의 수nH
2. 순열 (Permutation)
- 서로 다른 n개의 원소 중에서 r개를 중복 없이 골라
순서에 상관 있게
나열하는 경우의 수nPr
nPr = n! / (n-r)!
- 3개의 알파벳으로 단어를 만드는 경우의 수 : 3P3 = 3 _ 2 _ 1 = 6
- for 문으로 구현하면 뽑는 갯수 늘면 for 문도 늘어난다 (지양)
- 재귀 함수로 구현 지향
let input = ["a", "b", "c"];
let count = 0;
function permutation(arr) {
// 첫번째 위치시킬 요소 a? b? c?
for (let i = 0; i < arr.length; i++) {
// 두번째 위치시킬 요소
for (let j = 0; j < arr.length; j++) {
// 첫번째 === 두번째 같을 경우
// 있을 수 없기 때문에 skip
if (i === j) continue;
// 세번째 위치시킬 요소
for (let k = 0; k < arr.length; k++) {
if (i === k) continue;
if (j === k) continue;
console.log(arr[i], arr[j], arr[k]);
count++
}
}
}
}
permutation(input);
console.log(count);
// output
// a b c
// a c b
// b a c
// b c a
// c a b
// c b a
//6
let input = ["a", "b", "c"];
let count = 0;
// arr : 배열 s : start 위치 r : 뽑을 갯수(0부터)
function permutation(arr, s, r) {
// 1. 재귀함수를 멈춰야할 조건
if (s == r) {
count++;
console.log(arr);
return;
}
// 2. 재귀를 돌면서 변경되어야 할 부분 / 메인 로직
for (let i = s; i < arr.length; i++) {
[arr[s], arr[i]] = [arr[i], arr[s]]; // swap
permutation(arr, s + 1, r)
[arr[s], arr[i]] = [arr[i], arr[s]]; // 원상복귀
}
}
permutation(input, 0, 2);
console.log(count);
// output
// a b c
// a c b
// b a c
// b c a
// c a b
// c b a
//6
3. 조합(Combination)
- 서로 다른 n개의 원수 중에서 r을
중복 없이
골라순서에 상관 없이
나열하는 경우의 수 nCr = n!/(n-r)!r!
- 4개의 숫자 카드에서 2개를 뽑는 경우의 수 : 4C2 = 6
let input = [1, 2, 3, 4];
let count = 0;
function combination(arr) {
// i = 3 v = 4
for (let i = 0; i<arr.length; i++){
// i 이후의 값만 선택 가능하도록
// j = 4 j < 4 자동 skip
for (let j = i+1; j<arr.length; j++){
console.log(arr[i],arr[j])
count++
}
}
}
combination(input);
console.log(count);
// output
// 1 2
// 1 3
// 1 4
// 2 3
// 2 4
// 3 4
// 6
let input = [1, 2, 3, 4];
let count = 0;
let output = [];
function combination(arr, data, s, idx, r) {
if (s == r) {
count++;
console.log(data);
return;
}
for (let i = idx; arr.length - i >= r - s; i++) {
data[s] = arr[i];
combination(arr, data, s + 1, i + 1, r);
}
}
combination(input, output, 0, 0, 2);
console.log(count);
// output
// [ 1, 2 ]
// [ 1, 3 ]
// [ 1, 4 ]
// [ 2, 3 ]
// [ 2, 4 ]
// [ 3, 4 ]
// 6
4. 점화식
- 점화식(재귀식) : 수열에서 이웃하는 두개의 항 사이에 성립하는 관계를 나타낸 관계식
- 등차 수열 :
F(n) = F(n-1) + a
- 등비 수열 :
F(n) = F(n-1) * a
- 팩토리얼 :
F(n) = F(n-1) * n
- 피보나치 수열 :
F(n) = F(n-1) + F(n-2)
등차수열
let result;
// s : start
// t : 몇씩 증가
// number : 몇번 반복
function forloop(s, t, number) {
let acc = 0;
for (let i = 1; i <= number; i++) {
if (i == 1) {
acc += s;
} else {
acc += t;
}
console.log(i, acc);
}
return acc
}
// 3부터 시작해서 2씩증가하는 등차수열의 5번째 값 찾기
result = forloop(3, 2, 5);
console.log(result);
//output
// 1 3
// 2 5
// 3 7
// 4 9
// 5 11
// 11
let result;
function recursive(s, t, number) {
// 멈출 조건
if (number == 1) {
return s
}
// 반복할 조건
return recursive(s, t, number-1) + t
}
result = recursive(3, 2, 5);
console.log(result);
// number : 5 recursie(s,t,4) + 2 = s + 2 + 2 + 2
// number : 4 recursie(s,t,3) + 2 = s + 2 + 2 + 2
// number : 3 recursie(s,t,2) + 2 = s + 2 + 2
// number : 2 recursie(s,t,1) + 2 = s + 2
// number : 1 s
등비수열
function forloop(s, t, number) {
let acc = 1;
for (i = 1; i <= number; i++) {
if (i == 1) {
acc *= s;
} else {
acc *= t;
console.log(i, acc);
}
}
return acc;
}
console.log(forloop(3, 2, 5));
function recursive(s, t, number) {
// 종료 조건
if (number == 1){
return s
}
return recursive(s, t, number-1) * t
}
console.log(recursive(3, 2, 5));
팩토리얼
function forloop(n) {
let acc = 1
for (let i = n; i>0; i--){
acc *= i
}
return acc
}
console.log(forloop(5))
// 120
function recursive(n) {
if (n == 1){
return n
}
return recursive(n-1) * n
}
console.log(recursive(5))
// 120
피보나치 수열
function recursive(n) {
if (n-1 == 0 || n == 0){
return n
}
return recursive(n-1) + recursive(n-2)
}
console.log(recursive(5)) // 5
console.log(recursive(6)) // 8
console.log(recursive(7)) // 13
console.log(recursive(8)) // 21
- f(n) = f(n-1) + f(n-2)
- 양수에서 멈출 수 있도록 break 걸어줘야 한다.
- f(1) = 1
- f(2) = 1
- f(3) = 2
- f(4) = 3
나의 회고 🤫
순열과 조합의 재귀함수 부분을 몇번이고 복습해도 이해가 잘 가지 않는다…ㅠㅠ
아직 재귀함수에 익숙하지 않아서 벌어지는 문제 같아서 쭉 공부해나가고 가끔 와서 그때는 이해가 갔는지 다시 확인해봐야겠다.
재귀함수 사용하는거 너무 쿨하고 멋진데 나는 아직 그 실력이 안되는 것 같아 너무 아쉽다.
공부를 어느 정도 했을 때는 이 글을 보면서 “이땐 그랬지….ㅋㅋ”하고 넘어갈 수 있는 실력자가 됐으면 좋겠다😭