JavaScript

[JS] 순열(Permutation)

에밀오구 2023. 5. 3. 19:15

순열이란

순열이란 n개의 원소를 갖는 집합에서  순서를 생각하고 중복 없이 모든 경우의 수를 선택하는 것을 말합니다. 백트래킹(backtracking)/ DFS 를 활용하여 문제를 접근할 수 있습니다. 

ex) 집합이 ["a" , "b", "c"] 주어질 때   

["a" , "b", "c"]

["a" , "c", "b"]

[ "b", "a" , "c"]

["b",  "c", "a"]

[ "c", "a" , "b"]

["c", , "b", "a" ]

 

순열구현

집합이 ["a" , "b", "c"] 주어질  때  요소 a,b,c를 각각 순서대로 픽스하고 나머지 요소만으로 이루어진 배열에서 (seletNumber-1)만큼을 선택하여 또 순열을 구한다.(재귀)

function getPermutation (array, selectNumber) {

 if (selectNumber === 1) return array. map((element) =› [element]);
 const result = [];
 array. forEach ((element, originalArrIndex, array) = {
 const fix = element;
  const rest = array. filter ((_, restArrIndex) => restArrIndex !== originalArrIndex);
const permutation0fRest = getPermutation (rest, selectNumber - 1) ;
const combineFix = permutation0fRest .map ((elementArr) = [fix, ... elementArr]);
result.push (... combineFix);
});
return result;
}

getPermutation ([1,2,3], 3) ; 했다고 가정할 때

->해석: 집합[1,2,3]에서 3개의 원소를 순서를 생각하고 중복 없이 선택해주세요

 

1. 배열 순회 

첫번째 요소가 1로 고정될 경우, 2일 경우, 3일 경우로 경우의 수가 반복됩니다. 고로 array.forEach 함수로 element 가 순회하면서 돌때마다 element에는 1 , 2, 3 이 들어가게 됩니다. 

 

2. 첫 번째 원소가 1인 경우 fix 

element 는  1이므로 변수 fix 에는 1이 들어가게 됩니다. 

3. 첫 번째 원소가 1인 경우 rest 

fix에는 1이 들어갔을 때 1이아닌 나머지 원소들은 rest 변수에 들어가게됩니다.  

array. forEach ((element, originalArrIndex, array) = {​
 const fix = element; //1
  const rest = array. filter ((_, restArrIndex) => restArrIndex !== originalArrIndex);
  // forEach에서 현재 돌고있는 element의 인덱스값과 다른 요소들만 즉 현재 element가 아닌 것들만 담기
  //[2,3]

4.  첫번째 원소가 1 fix 된 채 나머지 2 개 요소 선정하기  

1일 제외한 나머지 중에 2개를 선정해야함으로 재귀로 자기 자신을 다시 호출합니다.  그리고 그 리턴 값을 permutation0fRest 에 담습니다.  현재  rest 는 [1,2 ] selectNumber 는 3이므로 getPermutation([2,3],2) 가 호출합니다. 

const permutation0fRest = getPermutation (rest, selectNumber - 1) ;

5.  2번째 원소 선정하기 

함수가 자기자신을 호출합니다. 이번에 array값은  인자로 받아온 rest [2,3]이고  selectNumber 는 2가 들어갑니다. selectNumber는 1이 아니므로 if 문을 거치지 않습니다. array 는[2,3]이고 이를 forEach를 돌립니다. 현재 element 는 2입니다.   fix 로 2가 들어가며 Rest 로 나머지 [3]이 들어가게됩니다. 그리고 다시 스스로 getPermutation을 호출한 뒤에 그 리턴값을 permutation0fRest에 담습니다. getPermutation([3],1)을 호출하며 다시 위에 코드로 올라갑니다.

function getPermutation (array, selectNumber) { //array[2,3]이 ,selectNumber에는 2가 들어가게 됩니다. 

 if (selectNumber === 1) return array. map((element) =› [element]);
 const result = [];
 array. forEach ((element, originalArrIndex, array) = { //element =2
 const fix = element;  //2 
  const rest = array. filter ((_, restArrIndex) => restArrIndex !== originalArrIndex); //[3]
const permutation0fRest = getPermutation (rest, selectNumber - 1) ; //getPermutation ([3], 1); 호출

6.  3번째 원소 선정하기 

이번에의 array 는 [3]이며 selectNumber는 1입니다. selectNumber 1이므로 if문조건에 충족합니다. 리턴값으로 array [[3]]을 반환시킵니다.  그러면 getPermutation([3],1)은 호출을 끝나게 되며 5.  2번째 원소 선정하기 로 리턴값[[3]]을 가지고 돌아갑니다. 

function getPermutation (array, selectNumber) { //array[3]이 ,selectNumber에는 1가 들어가게 됩니다. 

 if (selectNumber === 1) return array. map((element) =› [element]);

7.  재귀돌아가기: 5번으로 돌아가기 

현재 element가 2일때, selectNumber가 1일 때 로 돌아갑니다.   7번째 줄 getPermutation (rest, selectNumber - 1)은 리턴값 [[3]]을 가지고 돌아왔음으로  변수 permutation0fRest 에 [[3]]가 담아집니다.

function getPermutation (array, selectNumber) { //[2,3] 2

 if (selectNumber === 1) return array. map((element) =› [element]);
 const result = [];
 array. forEach ((element, originalArrIndex, array) = { //2 
 const fix = element;
  const rest = array. filter ((_, restArrIndex) => restArrIndex !== originalArrIndex);
const permutation0fRest = getPermutation (rest, selectNumber - 1) ; // [[3]] <--- 다시복귀
const combineFix = permutation0fRest .map ((elementArr) = [fix, ... elementArr]);
result.push (... combineFix);
});
return result;
}