JavaScript에서 나비 셔플(butterfly shuffle)된 배열이란 숫자들이 나비 날개처럼 대칭 구조를 이루도록 정렬된 배열을 말합니다. 배열의 중앙으로 갈수록 숫자가 작아지고, 끝으로 갈수록 다시 커지는 형태로, 가장 큰 숫자가 첫 번째 인덱스에 위치합니다.
또 다른 변형 형태로는 중앙으로 갈수록 숫자가 커지고 끝으로 갈수록 작아지는 경우도 있습니다. 이 경우에는 가장 작은 숫자가 첫 번째 인덱스에 놓입니다.
수학적 배경을 가진 분들이라면 이 패턴이 가우시안 분포(Gaussian distribution)와 비슷하게 느껴질 수 있습니다.
예시
다음과 같은 배열이 있다고 가정해 보겠습니다.
const arr = [8,2,6,3,9,1,4,5,0,7];
이 배열에 나비 셔플을 적용하면 결과는 다음과 같습니다.
[9, 7, 5, 3, 1, 0, 2, 4, 6, 8]
가장 큰 수와 두 번째로 큰 수가 양쪽 끝에 배치되고, 가장 작은 수가 중앙에 오는 것을 확인할 수 있습니다.
또 다른 가능한 결과는 다음과 같습니다.
[0, 2, 4, 6, 8, 9, 7, 5, 3, 1]
우리가 작성해야 할 함수는 숫자 배열과 문자열 두 개의 인자를 받으며, 문자열은 'asc' 또는 'des' 두 값 중 하나를 가질 수 있습니다.
- 문자열이 'des'라면 배열을 증가했다가 감소하는 순서로 셔플해야 합니다.
- 문자열이 'asc'라면 배열을 감소했다가 증가하는 순서로 셔플해야 합니다.
접근 방법
- 문자열이 'asc'면 먼저 배열을 오름차순으로 정렬하고, 그렇지 않으면 내림차순으로 정렬합니다. 예를 들어 함수가 'asc'로 호출되었다면 배열은 다음과 같이 됩니다.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- 그다음 배열을 두 개의 배열로 분할하되, 인접한 요소들이 서로 다른 배열에 번갈아 배치되도록 합니다. 한쪽 배열에는 push로 요소를 추가하고 다른 쪽에는 unshift로 추가하면, 하나의 배열이 자동으로 뒤집힌 상태가 되어 수동으로 뒤집을 필요가 없습니다.
이렇게 만들어진 두 배열은 다음과 같습니다.
[ 0, 2, 4, 6, 8 ] [ 9, 7, 5, 3, 1 ]
- 마지막 단계는 이 두 배열을 연결(concatenate)하여 원하는 최종 배열을 얻는 것입니다. 지금까지의 과정을 코드로 표현하면 다음과 같습니다.
예제 코드
const array = [8,2,6,3,9,1,4,5,0,7];
const butterflyShuffle = (array, order = 'asc') => {
// 원본 배열을 변경하지 않도록 복사본 생성
const arr = array.slice();
// 인자에 따라 오름차순 또는 내림차순 정렬
arr.sort((a, b) => order === 'asc' ? a-b : b-a);
const first = [], second = [];
// 배열 길이의 짝수/홀수 여부에 따라 마지막 요소가
// 어느 배열로 들어갈지 결정됩니다.
// 짝수 길이면 마지막 요소는 second 배열로,
// 홀수 길이면 first 배열로 들어갑니다.
const isEven = arr.length % 2 === 0;
for (let i = 0; i < arr.length; i++){
if(i % 2 === 0){
isEven ? first.push(arr[i]) : first.unshift(arr[i]);
continue;
};
isEven ? second.unshift(arr[i]) : second.push(arr[i]);
};
return isEven ? second.concat(first) : first.concat(second);
};
console.log(butterflyShuffle(array));
console.log(butterflyShuffle(array, 'des'));출력
콘솔 출력 결과는 다음과 같습니다.
[ 9, 7, 5, 3, 1, 0, 2, 4, 6, 8 ] [ 0, 2, 4, 6, 8, 9, 7, 5, 3, 1 ]