1. 순열(nPr)
서로 다른 n개의 원소 중 m개를 순서에 맞게(=순서가 다르면 다른 것으로 취급) 선택 후 나열
반복문 구현
반복문으로 구현하는 경우 for문의 수가 n만큼 반복하게 된다. n이 작다면 상관없지만, 큰 경우에는 for문을 많이 사용하기 때문에 비효율적이다.
function permutationLoop(list) {
const result = [];
for(let i=0; i<list.length; i++) {
for(let j=0; j<list.length; j++) {
for(let k=0; k<list.length; k++) {
// ...m번 반복
if(i == j || j == k || k == i || ...) continue;
result.push([list[i],list[j],list[k],...]);
}
}
}
return result;
}
재귀 구현
재귀로 구현하는 경우 반복하는 횟수를 제한하기 위한 조건문만 추가하면 한 번의 반복문을 사용해 효율적인 코드를 작성할 수 있다.
// 방법1
function permutationRecursion(list,m) {
if(m === 1) return list.map(el=>[el]);
const result = [];
list.forEach((fixed,i,origin)=>{
const rest = [...origin.slice(0,i),...origin.slice(i+1)];
const permutations = permutationRecursion(rest,m-1);
const attached = permutations.map(el=>[fixed,...el]);
result.push(...attached);
});
return result;
}
// 방법2
function permutationRecursion2(list,m) {
const result = [];
function recursion(items,rest) {
if(items.length === m) {
result.push(items);
return;
}
for(let i=0; i<rest.length; i++) {
const temp = rest.slice(); // 새로운 rest를 만들기 위한 복사
temp.splice(i,1); // 선택 된 것을 뺀 나머지(새로운 rest)
recursion([...items, rest[i]],temp);
}
}
recursion([],list);
return result;
}
2. 중복 순열
서로 다른 n개의 원소 중 m개를 중복을 허용하며 순서에 맞게 선택 후 나열
반복문 구현
순열을 구했던 방법에서 중복을 제거하기 위해 들어갔던 조건문을 지우면 끝이다.
function permutationWithRepetitionLoop(list) {
const result = [];
for(let i=0; i<list.length; i++) {
for(let j=0; j<list.length; j++) {
for(let k=0; k<list.length; k++) {
// ...m번 반복
result.push([list[i],list[j],list[k],...]);
}
}
}
return result;
}
재귀 구현
중복이 가능하기에 기존에 사용하던 리스트를 사용해 해결할 수 있다.
// 방법1
function permutationWithRepetitionRecursion(list,m) {
if(m === 1) return list.map(el=>[el]);
const result = [];
list.forEach((fixed,i,origin)=>{
const permutations = permutationRecursion(origin,m-1);
const attached = permutations.map(el=>[fixed,...el]);
result.push(...attached);
});
return result;
}
// 방법2
function permutationWithRepetitionRecursion2(list,m) {
const result = [];
function recursion(items) {
if(items.length === m) {
result.push(items);
return;
}
for(let i=0; i<list.length; i++) {
recursion([...items, list[i]]);
}
}
recursion([],list);
return result;
}
3. 조합(nCm)
서로 다른 n개의 원소 중 m개를 순서에 상관없이(=순서가 바뀌어도 같은 것으로 취급) 선택 후 나열
반복문 구현
순열과 비슷한 형태이지만 순서에 상관없기 위해서 다음 반복문의 시작이 이전 반복문의 시작보다 +1 되어 있다.
function combinationLoop(list) {
const result = [];
for (let i = 0; i < list.length; i++) {
for (let j = i + 1; j < list.length; j++) {
for (let k = j + 1; k < list.length; k++) {
// ...m번 반복
result.push([list[i], list[j], list[k],...]);
}
}
}
return result;
}
재귀 구현
// 방법1
function combinationRecursion1(list, m) {
if(m === 1) return list.map(el=>[el]);
const result = [];
list.forEach((fixed,i,origin)=>{
const rest = origin.slice(i+1);
const combinations = combinationRecursion1(rest,m-1);
const attached = combinations.map(el=>[fixed,...el]);
result.push(...attached);
});
return result;
}
// 방법2
function combinationRecursion2(list, m) {
const result = [];
function recursion(items,index) {
if(items.length === m) {
result.push(items);
return;
}
for(let i=index; i<list.length; i++) {
recursion([...items, list[i]], i+1);
}
}
recursion([],0);
return result;
}
4. 중복 조합(nHm = n+m-1Cm)
서로 다른 n개의 원소 중 m개를 중복을 허용하며 순서에 상관없이 선택 후 나열
반복문 구현
조합과 같은 형태이지만, 다음 반복문에서 +1을 해주지 않고 처음부터 시작한다는 차이가 있다.
function combinationWithRepetitionLoop(list,m) {
const result = [];
for(i=0; i<list.length; i++) {
for(j=i; j<list.length; j++) {
// ...m번 반복
result.push([list[i],list[j],...]);
}
}
return result;
}
재귀 구현
조합에서 본인을 빼기위해 사용했던 i+1에서 +1을 제거해 본인을 포함시킨다.
// 방법1
function combinationWithRepetitionRecursion(list,m) {
if(m === 1) return list.map(el=>[el]);
const result = [];
list.forEach((fixed,i,origin)=>{
const rest = origin.slice(i)
const combinations = combinationRecursion1(rest,m-1);
const attached = combinations.map(el=>[fixed,...el]);
result.push(...attached);
});
return result;
}
// 방법2
function combinationWithRepetitionRecursion2(list, m) {
const result = [];
function recursion(items,index) {
if(items.length === m) {
result.push(items);
return;
}
for(let i=index; i<list.length; i++) {
recursion([...items, list[i]], i);
}
}
recursion([],0);
return result;
}
참고
https://mine-it-record.tistory.com/508
https://bttrthn-ystrdy.tistory.com/68
'알고리즘&코테' 카테고리의 다른 글
| JS 알고리즘 공부 2회차 with. BaaaaaaaarkingDog (0) | 2024.03.28 |
|---|---|
| 백준 입출력 방식 JS (0) | 2024.03.27 |
| JS 알고리즘 공부1.with BaaaaaaaarkingDog (0) | 2024.03.26 |
| JS Heap 구현 (0) | 2024.03.21 |
| 왜 최단거리 문제에 BFS를 사용하는가? (0) | 2024.01.18 |