[algorithm] O (1)의 고유 한 (반복되지 않는) 난수?

반복하지 않는 0에서 1000 사이의 고유 한 난수를 생성하고 싶습니다 (예 : 6은 두 번 표시되지 않음). 이전 값을 O (N) 검색하는 것과 같은 것은 아닙니다. 이게 가능해?



답변

값을 0-1000으로 1001 개의 정수 배열을 초기화하고 변수 max를 배열의 현재 최대 인덱스 (1000으로 시작)로 설정하십시오. 0에서 최대 사이의 난수 r을 선택하고, 위치 r에있는 숫자를 최대 위치에있는 숫자로 바꾸고, 이제 최대 위치에있는 숫자를 반환하십시오. 최대 값을 1 씩 줄이고 계속하십시오. max가 0 인 경우 max를 배열의 크기-1로 다시 설정하고 배열을 다시 초기화 할 필요없이 다시 시작하십시오.

업데이트 :
질문에 대답했을 때이 방법을 스스로 생각해 냈지만 일부 연구 후에 이것이 Durstenfeld-Fisher-Yates 또는 Knuth-Fisher-Yates로 알려진 수정 된 버전의 Fisher-Yates 임을 알았습니다. 설명하기가 약간 어려울 수 있으므로 아래 예제를 제공했습니다 (1001 대신 11 요소 사용).

11 개의 요소가 array [n] = n으로 초기화 된 상태에서 배열이 시작되고 최대 값은 10에서 시작합니다.

+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|
+--+--+--+--+--+--+--+--+--+--+--+
                                ^
                               max

각 반복에서 임의의 숫자 r이 0과 최대 사이에서 선택되고 array [r]과 array [max]가 바뀌고 새 배열 [max]가 반환되고 max가 감소합니다.

max = 10, r = 3
           +--------------------+
           v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 7| 8| 9| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 9, r = 7
                       +-----+
                       v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 1| 2|10| 4| 5| 6| 9| 8| 7: 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 8, r = 1
     +--------------------+
     v                    v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 5| 6| 9| 1: 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

max = 7, r = 5
                 +-----+
                 v     v
+--+--+--+--+--+--+--+--+--+--+--+
| 0| 8| 2|10| 4| 9| 6| 5: 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

...

11 회 반복 한 후 배열의 모든 숫자가 선택되고 max == 0이되고 배열 요소가 섞입니다.

+--+--+--+--+--+--+--+--+--+--+--+
| 4|10| 8| 6| 2| 0| 9| 5| 1| 7| 3|
+--+--+--+--+--+--+--+--+--+--+--+

이 시점에서 최대 값을 10으로 재설정하고 프로세스를 계속할 수 있습니다.


답변

당신은 이것을 할 수 있습니다 :

  1. 목록 0..1000을 만듭니다.
  2. 목록을 섞으십시오. 이를 수행하는 좋은 방법 은 Fisher-Yates shuffle 을 참조하십시오 .
  3. 셔플 된 목록에서 순서대로 숫자를 반환하십시오.

따라서 매번 이전 값을 검색 할 필요는 없지만 초기 셔플에는 여전히 O (N)이 필요합니다. 그러나 Nils가 의견에서 지적했듯이, 이것은 O (1)로 상각됩니다.


답변

최대 선형 피드백 시프트 레지스터를 사용하십시오 .

그것은 몇 줄의 C로 구현 가능하며 런타임에는 몇 가지 테스트 / 분기, 약간의 추가 및 비트 이동 이상을 수행하지 않습니다. 그것은 무작위가 아니지만 대부분의 사람들을 바보입니다.


답변

선형 일치 생성기를 사용할 수 있습니다 . 어디 m당신이 범위를 벗어난 숫자를 얻을 때 (계수)가 1000보다 가까운 주요 더 큰 것, 바로 다음 하나를 얻을. 시퀀스는 모든 요소가 발생한 후에 만 ​​반복되며 테이블을 사용할 필요가 없습니다. 그러나이 생성기의 단점 (임의의 부족 포함)을 알고 있어야합니다.


답변

형식 보존 암호화를 사용할 수 있습니다 카운터를 암호화 할 수 있습니다. 카운터는 0에서 위로 올라가고 암호화는 원하는 키를 사용하여 원하는 기수와 너비의 임의의 값으로 바꿉니다. 예를 들어이 질문의 예는 기수 10, 너비 3입니다.

블록 암호는 일반적으로 고정 블록 크기 (예 : 64 또는 128 비트)를 갖습니다. 그러나 Format-Preserving Encryption을 사용하면 AES와 같은 표준 암호를 사용하고 원하는 암호화 기수를 사용하는 알고리즘을 사용하여 원하는 기수와 너비의 더 작은 너비의 암호를 만들 수 있습니다.

암호화 알고리즘이 1 : 1 매핑을 생성하기 때문에 충돌이 발생하지 않습니다. 또한 뒤집을 수 있으므로 (양방향 매핑) 결과 숫자를 가져 와서 시작한 카운터 값으로 되돌릴 수 있습니다.

이 기술은 셔플 배열 등을 저장하는 데 메모리가 필요하지 않으므로 메모리가 제한된 시스템에서 유리할 수 있습니다.

AES-FFX 는이를 달성하기 위해 제안 된 표준 방법 중 하나입니다. 완벽하게 준수하지는 않지만 AES-FFX 아이디어를 기반으로 한 기본 Python 코드를 실험했습니다 . 여기에서 Python 코드를 참조하십시오 . 예를 들어 카운터를 무작위로 보이는 7 자리 10 진수 또는 16 비트 숫자로 암호화 할 수 있습니다. 다음은 질문과 같이 기수 10, 너비 3 (0에서 999 사이의 숫자 제공)의 예입니다.

000   733
001   374
002   882
003   684
004   593
005   578
006   233
007   811
008   072
009   337
010   119
011   103
012   797
013   257
014   932
015   433
...   ...

반복되지 않는 다른 의사 난수 시퀀스를 얻으려면 암호화 키를 변경하십시오. 각 암호화 키는 서로 다른 반복되지 않는 의사 랜덤 시퀀스를 생성합니다.


답변

0 … 1000과 같은 낮은 숫자의 경우 모든 숫자가 포함 된 목록을 작성하고 셔플하는 것이 간단합니다. 그러나 숫자 집합이 매우 큰 경우 또 다른 우아한 방법이 있습니다. 키와 암호화 해시 함수를 사용하여 의사 난수 순열을 만들 수 있습니다. 다음 C ++-ish 예제 의사 코드를 참조하십시오.

unsigned randperm(string key, unsigned bits, unsigned index) {
  unsigned half1 =  bits    / 2;
  unsigned half2 = (bits+1) / 2;
  unsigned mask1 = (1 << half1) - 1;
  unsigned mask2 = (1 << half2) - 1;
  for (int round=0; round<5; ++round) {
    unsigned temp = (index >> half1);
    temp = (temp << 4) + round;
    index ^= hash( key + "/" + int2str(temp) ) & mask1;
    index = ((index & mask2) << half1) | ((index >> half2) & mask1);
  }
  return index;
}

여기에 hash문자열을 부호없는 정수로 매핑하는 임의의 의사 랜덤 함수가 있습니다. 이 함수 randperm는 고정 키를 가정하고 0 … pow (2, bits) -1 내의 모든 숫자를 순열 한 것입니다. 변수를 변경하는 모든 단계 index가 가역적이므로 구성에서 나옵니다. 이것은 Feistel 암호에서 영감을 얻었습니다 .


답변

여기에 설명 된 내 Xincrol 알고리즘을 사용할 수 있습니다.

http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html

이것은 배열, 목록, 순열 또는 과도한 CPU로드없이 임의의 고유 한 숫자를 생성하는 순수한 알고리즘 방법입니다.

최신 버전에서는 숫자 범위를 설정할 수도 있습니다 (예 : 0-1073741821 범위의 고유 한 난수를 원하는 경우).

나는 실제로 그것을 사용했습니다

  • 모든 노래를 무작위로 재생하지만 앨범 / 디렉토리 당 한 번만 재생되는 MP3 플레이어
  • 픽셀 단위의 비디오 프레임 용해 효과 (빠르고 부드럽게)
  • 서명 및 마커에 대한 이미지 위에 비밀 “노이즈”안개 만들기 (스테 가노 그래피)
  • 데이터베이스를 통한 대량의 Java 객체 직렬화를위한 데이터 객체 ID
  • 트리플 과반수 메모리 비트 보호
  • 주소 + 값 암호화 (모든 바이트는 암호화 될뿐만 아니라 버퍼의 새로운 암호화 된 위치로 이동) 이것은 실제로 cryptanalysis 동료를 화나게했습니다 🙂
  • 일반 텍스트에서 일반 텍스트로 SMS, 이메일 등을위한 암호화 텍스트 암호화
  • 내 텍사스 홀덤 포커 계산기 (THC)
  • 시뮬레이션을위한 몇 가지 게임, “셔플 링”, 순위

무료입니다. 시도 해봐…