무작위성을 어떻게 테스트해야합니까?


127

배열에서 요소를 무작위로 섞는 방법을 고려하십시오. 이것이 효과가 있는지 확인하기 위해 간단하면서도 강력한 단위 테스트를 어떻게 작성 하시겠습니까?

나는 눈에 띄는 결함이있는 두 가지 아이디어를 생각해 냈습니다.

  • 배열을 섞은 다음 순서가 이전과 다른지 확인하십시오. 이것은 잘 들리지만 셔플이 같은 순서로 섞이면 실패합니다. (불가능하지만 가능합니다.)
  • 일정한 시드로 어레이를 셔플하고 미리 결정된 출력과 비교하여 점검하십시오. 이것은 동일한 시드에 대해 항상 동일한 값을 반환하는 랜덤 함수에 의존합니다. 그러나,이는 가끔 잘못된 가정 .

주사위 롤을 시뮬레이션하고 난수를 반환하는 두 번째 함수를 고려하십시오. 이 기능을 어떻게 테스트 하시겠습니까? 그 기능을 어떻게 테스트하겠습니까?

  • 주어진 범위 밖의 숫자를 반환하지 않습니까?
  • 유효한 분포로 숫자를 반환합니까? (하나의 주사위에 대한 균등, 많은 주사위에 대한 정상)

이 예제뿐만 아니라 일반적으로 임의의 코드 요소를 테스트하는 데 대한 통찰력을 제공하는 답변을 찾고 있습니다. 단위 테스트도 여기에 올바른 솔루션입니까? 그렇지 않은 경우 어떤 종류의 테스트입니까?


모든 사람의 마음을 편하게하기 위해 나는 내 자신의 난수 생성기를 작성 하지 않습니다 .


35
단단한 커플 링은 헤드를 보여줍니다. 난수를 생성하는 객체를 전달하십시오. 그런 다음 테스트하는 동안 셔플 후 데크의 모양을 알 수있는 지정된 숫자 세트를 생성하는 객체를 전달할 수 있습니다. 난수 생성기의 임의성을 개별적으로 테스트 할 수 있습니다.
Martin York

1
셔플 (java Collections.shuffle () 또는 이와 유사한)에 기존 라이브러리 루틴을 사용하는 것이 좋습니다. 결함이있는 셔플 알고리즘 작성에 대한 developer.com/tech/article.php/616221/… 에서주의해야 할 이야기가 있습니다 . d6 () 함수를 작성하려면 범위를 벗어난 숫자를 생성하지 않고 분포에서 카이 제곱 테스트 (키 제곱은 의사 난수 시퀀스에 다소 민감 함)를 확신 할 수있을 정도로 테스트합니다. 직렬 상관 계수도보십시오.

"이는 동일한 시드에 대해 항상 같은 값을 반환하는 랜덤 함수에 의존합니다. 그러나 이것은 때때로 잘못된 가정입니다." 링크를 따라 갔는데 잘못된 가정이 보이지 않습니다. "똑같은 시드가 반복적으로 사용되면 동일한 일련의 숫자가 생성됩니다."
Kyralessa

@Kyralessa "Random 클래스의 난수 생성기 구현은 주요 버전의 .NET Framework에서 동일하게 유지되는 것은 아닙니다." 따라서 큰 관심사는 아니지만 여전히 고려해야 할 사항입니다.
dlras2

4
@Kyralessa 나는 그 인용문의 중요한 절반을 놓쳤다. "결과적으로, 애플리케이션 코드는 동일한 시드가 다른 버전의 .NET Framework에서 동일한 의사 난수 시퀀스를 초래한다고 가정해서는 안됩니다."
dlras2

답변:


102

단위 테스트가 무작위 테스트에 적합한 도구라고 생각하지 않습니다. 단위 테스트는 메소드를 호출하고 예상 값에 대해 리턴 된 값 (또는 오브젝트 상태)을 테스트해야합니다. 무작위 테스트의 문제점은 테스트하려는 대부분의 항목에 대해 예상되는 값이 없다는 것입니다. 주어진 시드로 테스트 할 수 있지만 반복성 만 테스트 합니다. 분포가 얼마나 임의적 인지 또는 전혀 임의적인지 를 측정 하는 방법을 제공하지 않습니다 .

다행스럽게도 Diehard Battery of Randomness Test 와 같이 실행할 수있는 통계 테스트가 많이 있습니다 . 또한보십시오:

  1. 의사 난수 생성기를 단위 테스트하는 방법은 무엇입니까?

    • Steve Jessop 은 사용하고있는 것과 동일한 RNG 알고리즘의 테스트 된 구현을 찾아 그 결과를 선택한 시드와 자신의 구현과 비교하는 것이 좋습니다.
    • Greg HewgillENT 통계 테스트 모음을 권장합니다 .
    • John D. Cook 은 독자들에게 자신의 CodeProject 기사 인 Simple Random Number Generation을 언급하는데, 여기에는 Donald Knuth의 2 권, Seminumerical Algorithms에 언급 된 Kolmogorov-Smirnov 테스트의 구현이 포함됩니다.
    • 몇몇 사람들은 생성 된 수의 분포가 균일하다는 테스트, 카이 제곱 테스트, 평균 및 표준 편차가 예상 범위 내에 있는지 테스트하는 것이 좋습니다. (분포 만 테스트하는 것만으로는 충분하지 않습니다. [1,2,3,4,5,6,7,8]은 균일 한 분포이지만 확실하지는 않습니다.)
  2. 임의의 결과를 반환하는 함수를 사용한 단위 테스트

    • Brian Genisio 는 RNG를 조롱하는 것이 테스트를 반복 가능하게하는 한 가지 옵션이며 C # 샘플 코드를 제공 한다고 지적합니다.
    • 다시 말하지만, 더 많은 사람들이 반복성 및 고정 분포, 카이 제곱 등에 대한 간단한 테스트를 위해 고정 시드 값을 사용한다고 지적합니다.
  3. Unit Testing Randomness 는 본질적으로 반복 할 수없는 것을 테스트하려고 할 때 이미 직면 한 많은 문제에 대해 설명하는 위키 기사입니다. 내가 얻은 한 가지 흥미로운 점은 다음과 같습니다.

    winzip은 이전에 파일의 무작위성을 측정하는 도구로 사용되는 것을 보았습니다 (분명히 작을수록 파일을 덜 무작위로 압축 할 수 있음).


통계적 무작위성에 대한 또 다른 좋은 테스트 스위트는 fourmilab.ch/random에있는 'ent' 입니다.

1
답변의 완성을 위해 게시 한 링크 중 일부를 요약 할 수 있습니까?
dlras2

@ DanRasmussen 물론, 주말에 그렇게 할 시간이 있습니다.
Bill the Lizard

4
“임의의 문제는 기대 값이 없다는 것입니다. 이것이 의미하는 바는 아니지만 알고리즘이 확률이 매우 높은지 여부를 결정하기 위해 랜덤 샘플링 및 통계 테스트 와 함께 통계 분포의 알려진 속성을 사용하는 올바른 솔루션을 암시 합니다. 그렇습니다. 고전적인 단위 테스트는 아니지만 가장 쉬운 경우에는 예상 값 의 분포 만 살펴보기 때문에 언급하고 싶었습니다 .
Konrad Rudolph

2
National Institute for Standards and Technology (NIST)에서 개발 한 STS (Statistical Test Suite)를 포함하는 Dieharder의 유명한 무작위 테스트 용 Diehard 배터리의 업데이트 된 버전이 있습니다. 그것은 사용할 수 있습니다 실행할 준비가 우분투와 아마 다른 배포판 : phy.duke.edu/~rgb/General/dieharder.php
nealmcb

21

1. 알고리즘 단위 테스트

첫 번째 질문에 대해 알고리즘의 결과를 알 수있는 일련의 임의의 숫자를 제공하는 가짜 클래스를 작성합니다. 그렇게하면 임의 함수 위에 구축 한 알고리즘이 작동하는지 확인할 수 있습니다. 따라서 다음과 같은 내용이 있습니다.

Random r = new RandomStub([1,3,5,3,1,2]);
r.random(); //returns 1
r.random(); //returns 3
...

2. 임의의 기능이 의미가 있는지 확인하십시오

단위 테스트에는 여러 번 실행되고 결과를 주장하는 테스트를 추가해야합니다.

  • 당신이 설정 한 경계 내에 있습니다 (따라서 주사위 롤은 1과 6 사이입니다)
  • 합리적인 분포를 보여줍니다 (다중 테스트 실행 및 분포가 예상 한 것의 x % 내에 있는지 확인하십시오. 예를 들어, 주사위 굴림의 경우 210 % ~ 20 % (1/6 = 16.67 %)의 당신이 1000 번 굴 렀을 때 주어진 시간).

3. 알고리즘과 랜덤 함수에 대한 통합 테스트

배열이 원래 정렬에서 얼마나 자주 정렬 될 것으로 기대하십니까? 몇 백 번 정렬하고 정렬이 변경되지 않는 시간의 x % 만 주장하십시오.

이것은 실제로 이미 통합 테스트이므로 임의 함수와 함께 알고리즘을 테스트하고 있습니다. 실제 임의 기능을 사용하면 더 이상 단일 테스트 실행으로 벗어날 수 없습니다.

경험에서 (유전자 알고리즘을 작성했습니다) 나는 알고리즘의 단위 테스트, 임의 함수의 분포 테스트 및 통합 테스트를 결합하는 방법을 말합니다.


14

잊혀진 PRNG의 한 측면은 모든 속성이 본질적으로 통계적이라는 것입니다. 배열을 섞으면 처음에 시작한 배열과 다른 순열이 발생할 것으로 기대할 수 없습니다. 기본적으로 일반적인 PRNG를 사용하는 경우 간단한 패턴을 사용하지 않고 반환되는 숫자 집합 사이에 분포가 있다는 것만 보장됩니다.

PRNG에 대한 적절한 테스트는 적어도 100 번 실행 한 다음 출력 분포 (질문의 두 번째 부분에 대한 직접적인 답변)를 확인하는 것입니다.

첫 번째 질문에 대한 답은 거의 같습니다. {1, 2, ..., n}으로 약 100 번 테스트를 실행하고 각 요소가 각 위치에 있었던 횟수를 세십시오. 셔플 방법이 좋은 경우에는 거의 동일해야합니다.

완전히 다른 문제는 암호화 등급 PRNG를 테스트하는 방법입니다. 이것은 당신이하고있는 일을 정말로 알지 못한다면 아마도 머물러서는 안될 문제입니다. 사람들은 단지 몇 가지 '최적화'또는 사소한 편집으로 좋은 암호 시스템 을 파괴하는 것으로 알려져 있습니다 (읽기 : 치명적인 허점).

편집 : 나는 질문, 최고 답변 및 내 자신을 완전히 다시 읽었습니다. 내가 주장하는 내용은 여전히 ​​똑같지 만 Bill The Lizard의 대답은 두 번째입니다. 단위 테스트는 본질적으로 부울입니다. 실패하거나 성공하기 때문에 PRNG (또는 PRNG를 사용하는 방법)의 속성이 "얼마나 좋은지"테스트에 적합하지 않습니다.이 질문에 대한 답변은 정량적입니다. 오히려 극성이 아닙니다.


1
각 요소가 각 위치에있는 횟수는 대략 같아야 한다고 생각 합니다 . 그것들이 일관되게 정확히 같다면, 무언가 잘못되었습니다.
octern

@octern 고마워, 내가 어떻게 쓸 수 있었는지 모르겠다 ... 지금까지는 완전히 틀렸다 ...
K.Steff

6

여기에는 무작위 화 테스트와 무작위 화를 사용하는 것의 두 가지가 있습니다.

무작위 테스트는 비교적 간단합니다. 난수 생성기의주기가 예상 한대로 (일부 임계 값 내에서 약간의 임의 시드를 사용하는 일부 샘플의 경우) 큰 샘플 크기에 대한 출력 분포가 예상 한 것과 같은지 확인합니다. (일부 임계 값 이내)

무작위 화를 사용하는 테스트는 결정적 의사 난수 생성기를 사용하여 수행하는 것이 가장 좋습니다. 랜덤 화의 출력은 시드 (입력)를 기반으로 알려져 있으므로 입력 대 예상 출력을 기반으로하여 단위 테스트를 정상적으로 수행 할 수 있습니다. RNG가 결정적 이지 않은 경우 결정적이거나 무작위 가 아닌 RNG 로 조롱하십시오. 무작위 코드를 사용하는 코드와는 별도로 무작위 화를 테스트하십시오.


6

여러 번 실행 하여 데이터를 시각화하십시오 .

코딩 호러 의 셔플 예제는 다음과 같습니다 . 알고리즘이 정상인지 여부를 알 수 있습니다.

여기에 이미지 설명을 입력하십시오

가능한 모든 항목이 한 번 이상 반환되고 (경계는 괜찮음) 분포는 괜찮다는 것을 쉽게 알 수 있습니다.


1
+1 시각화가 핵심입니다. 나는 항상 블록 암호 기사 의 ECB 섹션에있는 펭귄 사진이있는 예제를 좋아했습니다 . 자동화 된 소프트웨어는 이러한 규칙을 거의 감지 할 수 없습니다
Maksee

뭐라고? 시각화의 요점은 분포가 좋지 않다는 것을 보여주는 것입니다 . 순진 셔플 알고리즘은 특정 주문 이 다른 주문 보다 훨씬 가능성이 높습니다. 2341, 2314, 2143 및 1342 막대가 오른쪽으로 얼마나 더 뻗어 있는지 확인하십시오.
hvd

4

무작위 입력을 취하는 코드를 다룰 때 유용한 일반적인 포인터 : 예상되는 임의성 (최대 및 최소값, 해당되는 경우 최대 +1 및 최소 -1 값)의 경우를 확인하십시오. 숫자에 변곡점이있는 장소 (위, 위, 아래)를 확인하십시오 (예 : -1, 0, 1 또는 1보다 크거나 1보다 작고 소수 값이 함수를 망칠 수있는 경우 음수가 아님). 허용 된 입력을 완전히 벗어난 곳을 확인하십시오. 몇 가지 일반적인 사례를 확인하십시오. 임의의 입력을 추가 할 수도 있지만 테스트가 실행될 때마다 동일한 값을 테스트하지 않는 바람직하지 않은 부작용이있는 단위 테스트의 경우 (시드 접근 방식이 작동 할 수 있지만, 시드에서 처음 1,000 개의 임의의 숫자를 테스트하십시오. S 또는 그와 같은).

랜덤 함수의 출력을 테스트하려면 목표를 식별하는 것이 중요합니다. 카드의 경우, 0-1 랜덤 제너레이터의 균일 성을 테스트하여 52 개의 카드가 모두 결과에 나타나는지 또는 다른 목표 (이 목록 및 그 이상일 수도 있음)를 결정하는 것이 목표입니까?

특정 예에서는 난수 생성기가 불투명하다고 가정해야합니다 (OS를 작성하지 않는 한 OS syscall 또는 malloc을 단위 테스트하는 것이 타당하지 않은 것처럼). 난수 생성기를 측정하는 것이 유용 할 수 있지만, 목표는 난수 생성기를 작성하는 것이 아니라 매번 52 개의 카드를 받고 순서가 바뀌는 것을 확인하는 것입니다.

RNG가 올바른 배포를 생성하는지 테스트하고 카드 셔플 코드가 해당 RNG를 사용하여 무작위 결과를 생성하는지 확인하는 두 가지 테스트 작업이 있습니다. RNG를 작성하는 경우 통계 분석을 사용하여 분포를 증명하고, 카드 셔플 러를 작성하는 경우 각 출력에 반복되지 않은 카드가 52 개 있는지 확인하십시오 (사용중인 검사에서 테스트하는 것이 더 좋습니다) RNG).


4

안전한 난수 생성기에 의존 할 수 있습니다

방금 끔찍한 생각을했습니다. 당신은 자신의 난수 생성기를 작성하지 않습니까?

그렇지 않다고 가정하면 다른 사람들의 코드 (예 : 프레임 워크 의 구현)가 아닌 담당 코드를 테스트 해야합니다 SecureRandom.

코드 테스트

코드가 올바르게 응답하는지 테스트하려면 단위 가시성 클래스로 쉽게 재정의 할 수 있도록 낮은 가시성 방법을 사용하여 난수를 생성하는 것이 일반적입니다. 이 재정의 된 방법은 난수 생성기를 효과적으로 조롱하고 생산 대상과시기를 완벽하게 제어 할 수 있습니다. 결과적으로 단위 테스트의 목표 인 코드를 완전히 연습 할 수 있습니다.

분명히 에지 조건을 확인하고 적절한 입력이 주어지면 알고리즘이 지시 한대로 셔플 링이 발생하는지 확인하십시오.

안전한 난수 생성기 테스트

언어의 보안 난수 생성기가 실제로 무작위가 아니거나 버그가있는 것 (범위 값을 벗어난 것 등)이 확실하지 않은 경우 수억 회 이상의 출력에 대한 자세한 통계 분석을 수행해야합니다. 각 숫자의 발생 빈도를 플로팅하면 같은 확률로 표시됩니다. 결과가 어긋나면 결과를 프레임 워크 디자이너에게보고해야합니다. 보안 난수 생성기는 많은 암호화 알고리즘의 기본이므로 문제 해결에 확실히 관심이 있습니다.


1

글쎄, 당신은 결코 100 % 확신 할 수 없으므로 최선을 다하면 숫자가 무작위 일 가능성이 있다는 것입니다. 확률을 고르세요-숫자 나 항목의 표본이 오차 한계 내에서 백만 개의 표본이 주어지면 x 번 나올 것이라고 말합니다. 일을 백만 번 실행하고 여백 내에 있는지 확인하십시오. 다행스럽게도 컴퓨터는 이런 종류의 일을 쉽게 해줍니다.


그러나 이와 같은 단위 테스트는 모범 사례로 간주됩니까? 나는 항상 단위 테스트가 가능한 한 간단해야한다고 생각했습니다. 루프, 분기 또는 피할 수있는 것은 없습니다.
dlras2

4
단위 테스트가 정확 해야 합니다 . 분기, 루프, 재귀가 필요한 경우 가격입니다. 한 줄짜리 단위 테스트로 매우 정교하고 고도로 최적화 된 클래스를 단위 테스트 할 수 없습니다. 클래스를 한 번 단위 테스트하기 위해 Dijkstra의 알고리즘을 구현했습니다.
K.Steff

3
@ K.Steff, 와우. Dijkstra 알고리즘이 올바른지 확인하기 위해 단위 테스트를 단위 테스트 했습니까?
Winston Ewert

사실, 요점은 그렇습니다. 그러나 이번에는 '사소한'테스트를합니다. 하지만 원래 프로그램 (A *)에 대한 단위 테스트이기도합니다. 빠른 알고리즘을 테스트하는 것은 구현을 엉망으로 만듭니다.
K.Steff

1

임의의 숫자의 소스가 최소한 임의의 모양을 가진 무언가를 생성하는지 테스트하려면 테스트에서 상당히 큰 바이트 시퀀스를 생성하여 임시 파일에 작성한 다음 Fourmilab의 ent 도구에 셸 아웃하십시오 . 파싱하기 쉬운 CSV를 생성 할 수 있도록 -t (terse) 스위치를 입력하십시오. 그런 다음 다양한 숫자를 확인하여 "좋은지"확인하십시오.

어떤 숫자가 좋은지 결정하려면 알려진 임의의 소스를 사용 하여 테스트를 교정하십시오. 좋은 난수 세트가 주어지면 테스트는 거의 항상 통과해야합니다. 실제로 무작위 시퀀스라도 무작위가 아닌 것으로 보이는 시퀀스를 생성 할 가능성이 있기 때문에 확실한 테스트를받을 수 없습니다. 무작위 시퀀스로 인해 테스트가 실패 할 가능성이 적은 임계 값 만 선택하면됩니다. 임의성이 재미 있지 않습니까?

참고 : PRNG가 "랜덤"시퀀스를 생성 함을 나타내는 테스트를 작성할 수 없습니다. 통과하면 PRNG에 의해 생성 된 시퀀스가 ​​"무작위"일 가능성이있는 테스트 만 작성할 수 있습니다. 무작위의 기쁨에 오신 것을 환영합니다!


1

사례 1 : 셔플 테스트 :

배열 [0, 1, 2, 3, 4, 5]을 고려하고 섞으십시오. 무엇이 잘못 될 수 있습니까? 일반적인 물건 : a) 전혀 섞지 않음, b) 1-5를 섞지 않고 0을 섞지 않고, 0-4를 섞지 않고 5를 섞지 않고, 섞고, 항상 같은 패턴을 생성합니다 ...

그들 모두를 잡기위한 하나의 테스트 :

100 번 셔플하고 각 슬롯에 값을 추가하십시오. 각 슬롯의 합은 서로 비슷해야합니다. 평균 / Stddev를 계산할 수 있습니다. (5 + 0) /2=2.5, 100 * 2.5 = 25. 예를 들어 예상 값은 약 25입니다.

값이 범위를 벗어나면 약간의 부정확 한 가능성이 있습니다. 그 기회가 얼마나 큰지 계산할 수 있습니다. 테스트를 반복하십시오. 글쎄-물론 테스트가 두 번 연속 실패 할 가능성은 적습니다. 그러나 단위 테스트에 실패하면 소스를 자동으로 삭제하는 루틴이 없습니다. 다시 실행하십시오!

연속 3 회 실패 할 수 있습니까? 복권에서 행운을 시험 해봐야 할 수도 있습니다.

사례 2 : 주사위 굴리기

주사위 롤 문제는 같은 질문입니다. 주사위를 6000 번 던지십시오.

for (i in 0 to 6000) 
    ++slot [Random.nextInt (6)];
return (slot.max - slot.min) < threshold;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.