C# 개발 공부 5일

규수·2026년 2월 27일

개발 공부 일지

목록 보기
5/44

오늘은 배열 내 데이터의 위치를 바꾸는 스왑(Swap)과 전체적인 순서를 무작위로 뒤섞는 셔플(Shuffle) 알고리즘에 대해 정리해 보겠습니다.

1. Swap

스왑은 두 변수에 담긴 값을 서로 교체하는 알고리즘입니다. 프로그래밍에서 두 값을 직접 바꾸는 것은 불가능하기 때문에, 데이터를 잠시 보관할 임시 변수(Temp)를 활용하는 것이 핵심입니다.

예를 들어, 두 개의 컵에 담긴 물의 종류를 바꾸려면 빈 컵 하나가 추가로 필요한 것과 같은 원리입니다. 배열 내부에서 특정 인덱스의 값들을 서로 맞바꿀 때 가장 기본이 되는 기술입니다.

using UnityEngine;

public class Swap : MonoBehaviour
{
    void Start()
    {
        int a = 10;
        int b = 20;

        // 스왑 로직 시작
        int temp = a; // a의 값을 임시 저장소에 보관
        a = b;        // a 자리에 b의 값을 대입
        b = temp;     // b 자리에 임시 저장했던 원래 a의 값을 대입
    }
}

2. Shuffle

셔플은 배열의 요소들을 무작위로 재배치하는 과정입니다. 게임 개발에서는 카드 덱 섞기, 몬스터 스폰 순서 정하기, 아이템 드랍 리스트 무작위화 등 다양한 곳에서 사용됩니다. 배열 전체를 순회하며 각 요소의 위치를 랜덤하게 바꾸는 것이 기본 개념입니다.

3. Bad Shffule?

셔플을 처음 구현할 때 흔히 하는 방식은 다음과 같습니다. "처음부터 끝까지 모든 인덱스를 돌면서, 배열 전체 범위 중 하나를 무작위로 골라 스왑한다." 이를 배드 셔플(Bad Shuffle) 혹은 나이브 셔플이라고 부릅니다.

void BadShuffle(int[] arr)
{
	for(int i = 0; i < arr.Length; i++)
	{
		int j = Random.Range(0, arr.Length);
        Swap(arr, i ,j);
    }
    
}

겉보기에는 잘 섞이는 것 같지만, 수학적으로는 심각한 편향성(Bias)이 발생하는데 그 이유는 발생 가능한 경로의 수와 실제 결과의 수가 일치하지 않기 때문입니다.
예를 들어 크기가 3인 배열이 있다면, 실제 가능한 결과(순열)는 3! = 6가지입니다. 하지만 모든 인덱스에 대해 3가지 선택지를 가지고 스왑을 진행하면 총 3의 3제곱인 27가지의 경로가 생깁니다.
문제는 27이 6으로 나누어떨어지지 않는다는 점입니다. 이는 27가지 경로 중 어떤 결과는 4번 나오고, 어떤 결과는 5번 나오게 됨을 의미합니다. 즉, 특정 패턴이 더 자주 등장하는 확률적 불균형(Bias)이 생기게 되어 공정하지 못한 게임 로직이 됩니다.

4. Fisher-Yates

이러한 편향성을 회피하고 수학적으로 완벽하게 공정한 결과를 얻기 위한 방법이 바로 피셔-예이츠(Fisher-Yates) 알고리즘입니다. 이 방식의 핵심은 "이미 섞인 요소는 다음 선택에서 제외한다"는 점입니다.

배열의 마지막 인덱스부터 시작하여, 아직 선택되지 않은 앞쪽의 요소들(0부터 현재 인덱스까지) 중 하나를 무작위로 뽑아 스왑합니다. 이렇게 하면 첫 번째 선택에서는 n가지, 다음은 n-1가지 순으로 선택지가 줄어들어 정확히 n!의 경우의 수가 만들어집니다. 모든 결과가 동일한 확률(1/n!)로 등장하게 되는 가장 이상적인 방식입니다.

using UnityEngine;

public class ShuffleLogic : MonoBehaviour
{
    void Start()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        for (int i = numbers.Length - 1; i > 0; i--)
        {
            // 이미 섞인 i+1 이후의 요소는 선택지에서 제외
            int randomIndex = Random.Range(0, i + 1);

            // 스왑(Swap)
            int temp = numbers[i];
            numbers[i] = numbers[randomIndex];
            numbers[randomIndex] = temp;
        }
    }
}

오늘은 단순히 데이터를 섞는 행위를 넘어, 알고리즘의 설계가 결과의 공정성에 어떤 영향을 미치는지 배웠습니다.
게임 관련 소식에 가장 빠질 수 없는 요소가 랜덤 시스템이다보니 더 자세하게 공부해보았고 동시에 왜 중요한지 깊게 이해할 수 있었습니다.

정확한 알고리즘을 사용하는 습관이 결국 사용자의 불쾌함을 줄임과 동시에 신뢰할 수 있는 게임을 만드는 중요한 요소라는 것을 다시 한 번 깨닫게 된 시간이었습니다.

0개의 댓글