[TIL]underscore라이브러리 shuffle 파헤치기

Violet Lee·2020년 10월 25일
0

javascript

목록 보기
19/24

shuffle 라이브러리가 뭐에요?

underscore 라이브러리의 많은 기능 중에서도, shuffle이라는 라이브러리가 있다.
우선 shuffle의 사전적 의미는 '발을 끌며 걷다', '뒤 섞다'등의 뜻이 있는데,

그렇다면 shuffle 라이브러리의 기능은 무언가
'여러개의 요소들을 뒤섞어서 랜덤하게 값을 나타내주는 역할'을 해주지않을까?

말 그대로 shuffle은, '배열의 element들을 무작위로 섞은 뒤, 섞은 배열을 리턴'해주는 기능을 갖고있다.
오늘은 이 라이브러리를 조금씩 파헤쳐보면서 어떻게 작동되는 원리인건지 알아보도록 하겠다.

  1. 우선 인자로 들어오는 배열을 클론하여 똑같은 값을 가진 배열을 선언해줘야 할 것이다.
    최대한 원래 배열을 안 건드리는선에서 리턴을 하고 싶은것이다.

그러므로

let arrCloned = arr.slice();
  1. 원래는 아예 한 변수에, Math.random()의 값을 arr.length와 곱하여 Math.floor()를 한번에 적용시키는게 빠르겠지만,
    각각 어떤 값이 들어오는지 일일이 확인하여 그 과정을 확인하려 한다!

    그러므로 우선, for문은 array의 길이만큼 돌면서,
    먼저 Math.random()함수를 이용해 0 이상 1 미만의 수 중 랜덤한 소수를 하나 가져온다.

for(let i=0;i<arr.length;i++){
        let random = Math.random();
  ..
  1. 만약 처음에, 예를들어 0.8909277722120477 라는 수가 찍힌다고 가정해보자.
    arr의 길이는 총 6이므로 둘을 곱하면 5.345566633272286 라는 수가 나온다.

    왜냐하면 기본적으로
    Math.random()함수에 어떤 특정 정수를 곱할시에, 무조건 그 정수 미만의 수가 나오기때문이다.
    이는 여러번 그 값을 호출하더라도 무조건 그 범위안의 수만 나오는 식이기 때문에, 기본적으로 외워두면 좋다.
    자바에서도 마찬가지였다. 자바도 똑같은 Math객체의 메소드들이 마련되어있는데, 작동방식이 같아서 그대로 이해할 수 있었다.

let r = random*arr.length;
  1. 후에 이 값 r을 Math.floor()처리하여 뒤의 소수점을 없애면,
    toIdx 변수에는 최종적으로 5라는 랜덤한 arr의 인덱스 값을 얻어올수 있게된다.
let toIdx = Math.floor(r);
  1. 여기서 왜 toIdx의 값이 arr의 인덱스값이 될까?
    예를 들어 [1, 2, 34, 56, 7, 8] 의 배열을 인자로 넣어준다고 생각해보자.
[1, 2, 34, 56, 7, 8]

그리고, 임시로 저장할 변수인 temp에 arrCLoned의 0번째 요소인 1을 가져와서 저장해놓는다.

 let temp = arrCloned[i];
  1. 그 다음 비어있는 arrCloned[0]번째 공간에 방금 구한 랜덤 인덱스 값 5를 활용하여,
    arrCloned[5]의 값인 8을 할당해준다.
    즉, arrCloned[0] = arrCloned[toIdx] === arrCloned[0] = 8.
 arrCloned[i] = arrCloned[r];
  1. 그러면 arrCloned[toIdx]인 arrCloned[5]의 공간이 또 비어있을것이므로,
    거기에 아까 temp에 저장한 1을 집어넣어서, 서로의 값의 자리를 바꿔준다고 생각하면 된다.
    이제 arrCloned[5]는 1이다.
 arrCloned[r] = temp;

이제 for문이 모두 종료가 될때까지 랜덤한 수에 arr.length를 곱하고..
그 랜덤한 인덱스값을 또 임시저장방 temp에 집어넣고..
빈 arr의 공간에 랜덤 인덱스의 공간값을 집어넣고..
빈 랜덤인덱스의 공간에 temp의 값을 집어넣고....
속 반복하여 두 공간씩 자리를 바꿔준다고 생각하면 된다!! 😋😋

그러면 최종적으로 나온 랜덤이 적용된 배열의 각 요소들은,

  • '운'에 의해 원래 자리에 원래의 값이 다시 들어갔을수도 있고,
  • 안 바뀌었을수도 있고,
  • 자리가 바뀌어버린 값 들도 생길것이다.
    그러나 보이는것만 그러할 뿐,
    중요한것은, 이 배열이 arr.length만큼 모두 돌면서 랜덤하게 shuffle되었다는 것이다.

좀 더 한눈에 파악하기 쉽게 각 과정들 몇 개를 정리해보았다.

첫번째 과정

let temp = arrCloned[i] // temp = 1
arrCloned[i] = arrCloned[toIdx]; // arrCloned[0] = 8
arrCloned[toIdx] = temp; //arrCloned[5] = 1;

//arrCloned: (6) [8, 2, 34, 56, 7, 1]

두번째 과정

//random: 0.8909277722120477
//r: 4.680967397053604
//toIdx: 4
let temp = arrCloned[i] // temp = 2
arrCloned[i] = arrCloned[toIdx]; // arrCloned[1] = 7
arrCloned[toIdx] = temp; //arrCloned[4] = 2;

//arrCloned: (6) [8, 7, 34, 56, 2, 1]

세번째 과정

//random: 0.8730173096217597
//r: 5.238103857730558
//toIdx: 5
let temp = arrCloned[i] // temp = 34
arrCloned[i] = arrCloned[toIdx]; // arrCloned[2] = 8
arrCloned[toIdx] = temp; //arrCloned[5] = 34;

//arrCloned: (6) [8, 7, 1, 56, 2, 34] < 이렇게 i를 사용하는 인덱스의 방은 순차적으로 값이 바뀔테지만,
//랜덤코드에 의해 바뀔 상대의 공간은, 이렇게 처음 과정에서 한번 바뀜당한 곳(arrCloned[5])인데도 또 안의 값이 바뀌는것을 볼 수 있다. 이번엔 34로 바뀌었다.

.
.
.

해서 최종적으로 셔플된 결과는

(6)  [34, 2, 1, 56, 7, 8]

가 나온것을 볼 수 있다.

최종 레퍼런스 코드

function shuffle(arr){
    let arrCloned = arr.slice();
    for(let i=0;i<arr.length;i++){
        let random = Math.random();
        let r = random*arr.length;
        let toIdx = Math.floor(r); 
      //let toIdx = Math.floor(Math.random()*arr.length);
        let temp = arrCloned[i];
        arrCloned[i] = arrCloned[r];
        arrCloned[r] = temp;
    }
    return arrCloned;
}
let result = shuffle([1,2,34,56,7,8]);
console.log(result);

그렇다면 이 shuffle메소드는 대체, 어떤때에 활용할 수 있을까?
랜덤한 여러 개의 수들을 사용하여, 어떤 특정한 때에 그 랜덤수들은 마치, '어떤 특별한 키, 즉, 식별번호'처럼 사용할 수 있지 않을까?

해서 나는, '로또'가 이런 원리로 수를 랜덤하게 섞어서 행운의 번호를 만드는것이 아닐까 생각했다.

기본 6개의 수가 들어갈 배열을 하나 선언해놓고, 중복을 거르는 조건을 만들어서 배열의 i번째에 순차적으로 넣을 수 있다면..?

아니면 특정 식별번호인 택배 주문번호 라던가, 구글의 백업 키, 자동 비밀번호 생성 등에도 사용할 수 있을것같다!!

이번에 연습해서 로또 생성 프로그램을 꼭 만들어봐야겠다.

후기
내가 자바를 배우지 않았다면 random()함수의 이해가 어려웠을것 같은데, 그때 미친척 엄청 연습해놔서 다행이었다.... 잊지말자. 나는 남들보다 배는 노력해야한다. 이게 끝이 아니다. 화이팅.

profile
예비개발자

0개의 댓글