프로그래밍을 하다 보면 중복되지 않는 난수를 생성해야할 때가 있습니다.
그럴때마다 어떻게 해야할까... 이런 생각이 들기도 하는데요!
정말 간단하게 중복되지 않는 난수를 생성하는 방법이 있습니다.
* 속도도 의외로 빠릅니다. (정말 그런가..?)
일단 중복되지 않는 난수를 생성하는 함수의 코드는 다음과 같습니다.
* 이 코드를 사용하시려면 반드시 아래의 네임스페이스들을 추가해야 합니다.
System
System.Collections.Generic
System.Diagnostics
/// <summary>
/// 지정된 숫자의 범위 갯수만큼 중복되지 않는 난수 배열을 생성합니다.<br />
/// 난수 범위: start ~ end (* end - 1 이 아님에 유의)
/// </summary>
/// <param name="start">난수의 시작 값 입니다.</param>
/// <param name="end">난수의 끝 값 입니다.</param>
public static Int32[] GetRandomNumbers(Int32 start, Int32 end) {
// 시작 인덱스, 종료 인덱스, 숫자 갯수, 반복 조건식 값
// 그리고 원본 숫자 및 결과 숫자를 저장할 목록(List)을 초기화한다.
Int32 startIndex = start > end ? end : start;
Int32 endIndex = start > end ? start : end;
Int32 nCount = endIndex - startIndex + 1;
Int32 nLoopCount = startIndex + endIndex + 1;
List<Int32> numberList = new List<Int32>(nCount);
List<Int32> resultList = new List<Int32>(nCount);
// 원본 목록에 start 부터 end 까지의 값을 집어넣는다.
for ( Int32 i = startIndex; i < nLoopCount; i++ )
numberList.Add(i);
// Random 클래스로 난수를 만들 때 유의할 점은
// 코드가 너무 빠르게 실행되면 동일한 값이 나오게 된다.
// (틱 값 기반으로 난수를 생성하기 때문에)
// Environment.TickCount 속성을 왜 사용하지 않느냐고 의아해하시는 분이 계실 수 있는데
// TickCount 속성은 자료형이 Int32 이기 때문에 정확도가 Stopwatch 클래스의 ElapsedTicks 보다 떨어지게 된다.
// 그래서 Stopwatch 클래스를 이용하여 시드를 초기화한다.
Stopwatch sw = new Stopwatch();
// 스톱워치 시작
sw.Start();
// 원본 목록에 값이 있을 경우 계속 반복한다.
while ( numberList.Count > 0 ) {
// 흐른 틱 값과 결과 목록의 항목 갯수를 더해서 시드 값을 생성한다.
Random rGen = new Random((Int32) sw.ElapsedTicks + resultList.Count);
// 0 부터 원본 목록의 항목 갯수까지의 난수를 생성한다.
Int32 pickedIndex = rGen.Next(0, numberList.Count);
// 원본 목록에서 값을 가져온 다음 결과 목록에 추가하고
// 가져온 값을 제거한다.
resultList.Add(numberList[pickedIndex]);
numberList.RemoveAt(pickedIndex);
}
// 스톱워치 정지
sw.Stop();
// 결과 목록을 배열로 만들어서 반환한다.
return resultList.ToArray();
}
이 코드의 원리는 다음과 같습니다.
- 0 부터 갯수 만큼의 값을 원본 목록에 넣습니다. (for 문에서의 array[i] = i 와 같은 개념)
- 0 부터 원본 목록의 항목 갯수 사이의 난수를 발생시키고 원본 목록의 "발생된 난수" 의 위치한 값을 결과 목록에 저장한 후 원본 목록에서부터 선택된 항목을 제거합니다.
- 원본 목록의 항목의 갯수가 0 이 될때까지 반복합니다.
public static void Main(string[] args) {
Int32[] rndArray = GetRandomNumbers(0, 0xFFFF);
Int32 overlappedNumberCount = 0;
for ( Int32 i = 0; i < 0xFFFF; i++ ) {
var overlappedNumberArray =
from n in rndArray
where n == i select n;
if ( overlappedNumberArray.Count() > 1 )
overlappedNumberCount++;
}
Console.WriteLine("중복된 숫자 갯수: {0:N0}", overlappedNumberCount);
Console.ReadKey(true);
}
실행 결과:
유용하게 사용될거라 생각합니다~!!
즐프하세요!
'.NET' 카테고리의 다른 글
형식 매개 변수에도 조건을 걸 수 있다! - 제네릭 형식에서의 형식 매개 변수에 조건 걸기 (0) | 2015.07.23 |
---|---|
제네릭(Generic) 이란 무엇일까? (8) | 2015.02.18 |
2D 게임 프로그래밍에서 유용하게 쓰이는 함수들! (0) | 2014.12.05 |
리스트뷰에 이미지 표시하기 (0) | 2014.11.21 |
BigInteger - 엄~~~~~~~청 큰 수를 처리하는 방법 (0) | 2014.11.19 |