반복하지 않는 0에서 1000 사이의 고유 한 난수를 생성하고 싶습니다 (예 : 6은 두 번 표시되지 않음). 이전 값을 O (N) 검색하는 것과 같은 것은 아닙니다. 이게 가능해?
O(n)
시간이나 메모리 와 같은) 무언가를 금지하는 경우 수락 된 답변을 포함하여 아래의 많은 답변이 잘못되었습니다.
반복하지 않는 0에서 1000 사이의 고유 한 난수를 생성하고 싶습니다 (예 : 6은 두 번 표시되지 않음). 이전 값을 O (N) 검색하는 것과 같은 것은 아닙니다. 이게 가능해?
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으로 재설정하고 프로세스를 계속할 수 있습니다.
N
매번 원하는 결과를 얻기 위해 반복 (이 예제에서는 11) 을 수행해야한다는 사실이 아닙니다 O(n)
. 동일한 초기 상태에서 조합 N
을 얻으려면 반복 을 수행해야하므로 N!
그렇지 않으면 출력은 N 상태 중 하나입니다.
당신은 이것을 할 수 있습니다 :
따라서 매번 이전 값을 검색 할 필요는 없지만 초기 셔플에는 여전히 O (N)이 필요합니다. 그러나 Nils가 의견에서 지적했듯이, 이것은 O (1)로 상각됩니다.
최대 선형 피드백 시프트 레지스터를 사용하십시오 .
그것은 몇 줄의 C로 구현 가능하며 런타임에는 몇 가지 테스트 / 분기, 약간의 추가 및 비트 이동 이상을 수행하지 않습니다. 그것은 무작위가 아니지만 대부분의 사람들을 바보입니다.
선형 일치 생성기를 사용할 수 있습니다 . 어디 m
당신이 범위를 벗어난 숫자를 얻을 때 (계수)가 1000보다 가까운 주요 더 큰 것, 바로 다음 하나를 얻을. 시퀀스는 모든 요소가 발생한 후에 만 반복되며 테이블을 사용할 필요가 없습니다. 그러나이 생성기의 단점 (임의의 부족 포함)을 알고 있어야합니다.
k
, 시퀀스에서 떨어져있는 숫자 는 결코 함께 일어날 수 없다).
형식 보존 암호화를 사용할 수 있습니다 카운터를 암호화 할 수 있습니다. 카운터는 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
... ...
반복되지 않는 다른 의사 난수 시퀀스를 얻으려면 암호화 키를 변경하십시오. 각 암호화 키는 서로 다른 반복되지 않는 의사 랜덤 시퀀스를 생성합니다.
k
시퀀스에서 떨어져있는 값 은 절대로 함께 발생할 수 없음).
k
?
1,2,...,N
를 다른, 그러나 여전히 일정한 순서로 동일한 숫자 의 시퀀스 로 대체합니다 . 그런 다음이 순서에서 숫자를 하나씩 가져옵니다. k
선택한 값의 수입니다 (OP는 문자를 지정하지 않았으므로 소개해야했습니다).
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 암호에서 영감을 얻었습니다 .
hash()
, 위 코드에서 사용되는, 보안 의사 랜덤 함수는,라도 유용 (의 참고 및 Rackoff 1988)를 산출 할 것이다 이러한 구성 의사 랜덤 순열을 철저한보다 훨씬 적은 노력을 이용하여 진정한 랜덤 셔플 구별 할 수없는, 키 길이에 기하 급수적 인 전체 키 공간 검색 합리적인 크기의 키 (예 : 128 비트)의 경우에도 지구상에서 사용할 수있는 총 컴퓨팅 성능을 뛰어 넘습니다.
hash( key + "/" + int2str(temp) )
위의 임시 구성을 HMAC 로 바꾸는 것을 선호 합니다. 그것은 덜 실수로 사람을 안전하지 않은 비 암호화 해시 기능이 구성을 사용하려고).
여기에 설명 된 내 Xincrol 알고리즘을 사용할 수 있습니다.
http://openpatent.blogspot.co.il/2013/04/xincrol-unique-and-random-number.html
이것은 배열, 목록, 순열 또는 과도한 CPU로드없이 임의의 고유 한 숫자를 생성하는 순수한 알고리즘 방법입니다.
최신 버전에서는 숫자 범위를 설정할 수도 있습니다 (예 : 0-1073741821 범위의 고유 한 난수를 원하는 경우).
나는 실제로 그것을 사용했습니다
무료입니다. 시도 해봐...
k
시퀀스에서 떨어져있는 값 은 절대로 함께 발생할 수 없음).
이 문제를 해결하기 위해 배열이 필요하지 않습니다.
비트 마스크와 카운터가 필요합니다.
카운터를 0으로 초기화하고 연속 호출에서 증가시킵니다. 의사 난수를 생성하기 위해 비트 마스크 (시작시 무작위로 선택되거나 고정 된)와 카운터를 XOR합니다. 1000을 초과하는 숫자를 가질 수 없으면 9 비트보다 넓은 비트 마스크를 사용하지 마십시오. 즉, 비트 마스크는 511보다 크지 않은 정수입니다.
카운터가 1000을 통과하면 카운터를 0으로 재설정해야합니다. 이때 다른 랜덤 비트 마스크 (원하는 경우)를 선택하여 다른 순서로 동일한 숫자 세트를 생성 할 수 있습니다.
선형 합동 발생기 가 가장 간단한 해결책 이라고 생각합니다 .
a , c 및 m 값 에는 3 가지 제한 사항 만 있습니다
추신 : 이 방법은 이미 언급되었지만 게시물에는 상수 값에 대한 잘못된 가정이 있습니다. 아래 상수는 귀하의 경우에 잘 작동합니다.
귀하의 경우에는 당신이 사용할 수 a = 1002
, c = 757
,m = 1001
X = (1002 * X + 757) mod 1001
다음은 첫 번째 솔루션의 논리를 사용하여 입력 한 코드입니다. 나는 이것이 "언어에 구애받지 않는다"는 것을 알고 있지만 누군가가 빠른 실용적인 솔루션을 찾고있는 경우 C #에서 이것을 예제로 제시하고 싶었습니다.
// Initialize variables
Random RandomClass = new Random();
int RandArrayNum;
int MaxNumber = 10;
int LastNumInArray;
int PickedNumInArray;
int[] OrderedArray = new int[MaxNumber]; // Ordered Array - set
int[] ShuffledArray = new int[MaxNumber]; // Shuffled Array - not set
// Populate the Ordered Array
for (int i = 0; i < MaxNumber; i++)
{
OrderedArray[i] = i;
listBox1.Items.Add(OrderedArray[i]);
}
// Execute the Shuffle
for (int i = MaxNumber - 1; i > 0; i--)
{
RandArrayNum = RandomClass.Next(i + 1); // Save random #
ShuffledArray[i] = OrderedArray[RandArrayNum]; // Populting the array in reverse
LastNumInArray = OrderedArray[i]; // Save Last Number in Test array
PickedNumInArray = OrderedArray[RandArrayNum]; // Save Picked Random #
OrderedArray[i] = PickedNumInArray; // The number is now moved to the back end
OrderedArray[RandArrayNum] = LastNumInArray; // The picked number is moved into position
}
for (int i = 0; i < MaxNumber; i++)
{
listBox2.Items.Add(ShuffledArray[i]);
}
이 방법은 한계가 높고 소수의 난수 만 생성하려고 할 때 적절 합니다.
#!/usr/bin/perl
($top, $n) = @ARGV; # generate $n integer numbers in [0, $top)
$last = -1;
for $i (0 .. $n-1) {
$range = $top - $n + $i - $last;
$r = 1 - rand(1.0)**(1 / ($n - $i));
$last += int($r * $range + 1);
print "$last ($r)\n";
}
숫자는 오름차순으로 생성되지만 나중에 셔플 할 수 있습니다.
(top,n)=(100,10)
있습니다 (0.01047705, 0.01044825, 0.01041225, ..., 0.0088324, 0.008723, 0.00863635)
. 파이썬으로 테스트 했으므로 수학의 약간의 차이가 여기에서 중요한 역할을 할 수 있습니다 (계산 r
을 위한 모든 연산 이 부동 소수점 인지 확인했습니다 ).
좋은 의사 난수 생성기를 사용할 수 있습니다. (10 개) 비트를 1000에 0을 떠나 1023 1001 버린다.
에서 여기에 우리는 10 비트 PRNG에 대한 설계를 얻을 수 ..
10 비트, 피드백 다항식 x ^ 10 + x ^ 7 + 1 (기간 1023)
Galois LFSR을 사용하여 빠른 코드 얻기
public static int[] randN(int n, int min, int max)
{
if (max <= min)
throw new ArgumentException("Max need to be greater than Min");
if (max - min < n)
throw new ArgumentException("Range needs to be longer than N");
var r = new Random();
HashSet<int> set = new HashSet<int>();
while (set.Count < n)
{
var i = r.Next(max - min) + min;
if (!set.Contains(i))
set.Add(i);
}
return set.ToArray();
}
N 비 반복 난수는 필요에 따라 복잡도가 O (n)입니다.
참고 : 스레드 안전이 적용된 상태에서 임의는 정적이어야합니다.
O(n)
다시 셔플하기 위해 다시 시작할 때마다 지연 없이 셔플 목록을 계속 반복하고 싶다고 가정 해 봅시다 .
0에서 1000까지의 2 개의 목록 A와 B를 작성하면 2n
공간 이 필요 합니다.
Fisher-Yates를 사용하여 목록 A를 섞으면 n
시간 이 걸립니다 .
숫자를 그릴 때 다른 목록에서 1 단계 Fisher-Yates 셔플을 수행하십시오.
커서가 목록 끝에 있으면 다른 목록으로 전환하십시오.
전처리
cursor = 0
selector = A
other = B
shuffle(A)
무승부
temp = selector[cursor]
swap(other[cursor], other[random])
if cursor == N
then swap(selector, other); cursor = 0
else cursor = cursor + 1
return temp
[1,3,4,5,2]
은 셔플 링 과 동일한 결과를 생성합니다 [1,2,3,4,5]
.
질문 0에서 상한 N 사이의 K 반복되지 않는 정수 목록을 효율적으로 생성하는 방법 중복으로 연결되어 있습니다 - 당신은 어떤 O (N에 생성 된 난수 당 O (1) 뭔가를 (원하는 경우) 시작 비용)) 허용되는 답변의 간단한 조정이 있습니다.
초기화 된 배열을 사용하는 대신 정수에서 정수로 빈 정렬되지 않은 맵 (빈 정렬 맵은 요소 당 O (log k)을 사용함)을 만듭니다. 최대 값 인 경우 최대 값을 1000으로 설정하십시오.
초기화 된 배열을 사용하는 것과 비교할 때의 유일한 차이점은 요소의 초기화가 연기되거나 건너 뛴다는 것입니다. 그러나 동일한 PRNG에서 동일한 숫자를 생성합니다.
또 다른 가능성 :
플래그 배열을 사용할 수 있습니다. 그리고 이미 선택된 경우 다음을 가져 가십시오.
그러나 1000 번의 호출 후에는 기능이 종료되지 않으므로주의해야합니다.
다음은 재생할 수있는 샘플 COBOL 코드입니다.
나는 당신에게 RANDGEN.exe 파일을 보낼 수 있습니다
IDENTIFICATION DIVISION.
PROGRAM-ID. RANDGEN as "ConsoleApplication2.RANDGEN".
AUTHOR. Myron D Denson.
DATE-COMPILED.
* **************************************************************
* SUBROUTINE TO GENERATE RANDOM NUMBERS THAT ARE GREATER THAN
* ZERO AND LESS OR EQUAL TO THE RANDOM NUMBERS NEEDED WITH NO
* DUPLICATIONS. (CALL "RANDGEN" USING RANDGEN-AREA.)
*
* CALLING PROGRAM MUST HAVE A COMPARABLE LINKAGE SECTION
* AND SET 3 VARIABLES PRIOR TO THE FIRST CALL IN RANDGEN-AREA
*
* FORMULA CYCLES THROUGH EVERY NUMBER OF 2X2 ONLY ONCE.
* RANDOM-NUMBERS FROM 1 TO RANDOM-NUMBERS-NEEDED ARE CREATED
* AND PASSED BACK TO YOU.
*
* RULES TO USE RANDGEN:
*
* RANDOM-NUMBERS-NEEDED > ZERO
*
* COUNT-OF-ACCESSES MUST = ZERO FIRST TIME CALLED.
*
* RANDOM-NUMBER = ZERO, WILL BUILD A SEED FOR YOU
* WHEN COUNT-OF-ACCESSES IS ALSO = 0
*
* RANDOM-NUMBER NOT = ZERO, WILL BE NEXT SEED FOR RANDGEN
* (RANDOM-NUMBER MUST BE <= RANDOM-NUMBERS-NEEDED)
*
* YOU CAN PASS RANDGEN YOUR OWN RANDOM-NUMBER SEED
* THE FIRST TIME YOU USE RANDGEN.
*
* BY PLACING A NUMBER IN RANDOM-NUMBER FIELD
* THAT FOLLOWES THESE SIMPLE RULES:
* IF COUNT-OF-ACCESSES = ZERO AND
* RANDOM-NUMBER > ZERO AND
* RANDOM-NUMBER <= RANDOM-NUMBERS-NEEDED
*
* YOU CAN LET RANDGEN BUILD A SEED FOR YOU
*
* THAT FOLLOWES THESE SIMPLE RULES:
* IF COUNT-OF-ACCESSES = ZERO AND
* RANDOM-NUMBER = ZERO AND
* RANDOM-NUMBER-NEEDED > ZERO
*
* TO INSURING A DIFFERENT PATTERN OF RANDOM NUMBERS
* A LOW-RANGE AND HIGH-RANGE IS USED TO BUILD
* RANDOM NUMBERS.
* COMPUTE LOW-RANGE =
* ((SECONDS * HOURS * MINUTES * MS) / 3).
* A HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE
* AFTER RANDOM-NUMBER-BUILT IS CREATED
* AND IS BETWEEN LOW AND HIGH RANGE
* RANDUM-NUMBER = RANDOM-NUMBER-BUILT - LOW-RANGE
*
* **************************************************************
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
DATA DIVISION.
FILE SECTION.
WORKING-STORAGE SECTION.
01 WORK-AREA.
05 X2-POWER PIC 9 VALUE 2.
05 2X2 PIC 9(12) VALUE 2 COMP-3.
05 RANDOM-NUMBER-BUILT PIC 9(12) COMP.
05 FIRST-PART PIC 9(12) COMP.
05 WORKING-NUMBER PIC 9(12) COMP.
05 LOW-RANGE PIC 9(12) VALUE ZERO.
05 HIGH-RANGE PIC 9(12) VALUE ZERO.
05 YOU-PROVIDE-SEED PIC X VALUE SPACE.
05 RUN-AGAIN PIC X VALUE SPACE.
05 PAUSE-FOR-A-SECOND PIC X VALUE SPACE.
01 SEED-TIME.
05 HOURS PIC 99.
05 MINUTES PIC 99.
05 SECONDS PIC 99.
05 MS PIC 99.
*
* LINKAGE SECTION.
* Not used during testing
01 RANDGEN-AREA.
05 COUNT-OF-ACCESSES PIC 9(12) VALUE ZERO.
05 RANDOM-NUMBERS-NEEDED PIC 9(12) VALUE ZERO.
05 RANDOM-NUMBER PIC 9(12) VALUE ZERO.
05 RANDOM-MSG PIC X(60) VALUE SPACE.
*
* PROCEDURE DIVISION USING RANDGEN-AREA.
* Not used during testing
*
PROCEDURE DIVISION.
100-RANDGEN-EDIT-HOUSEKEEPING.
MOVE SPACE TO RANDOM-MSG.
IF RANDOM-NUMBERS-NEEDED = ZERO
DISPLAY 'RANDOM-NUMBERS-NEEDED ' NO ADVANCING
ACCEPT RANDOM-NUMBERS-NEEDED.
IF RANDOM-NUMBERS-NEEDED NOT NUMERIC
MOVE 'RANDOM-NUMBERS-NEEDED NOT NUMERIC' TO RANDOM-MSG
GO TO 900-EXIT-RANDGEN.
IF RANDOM-NUMBERS-NEEDED = ZERO
MOVE 'RANDOM-NUMBERS-NEEDED = ZERO' TO RANDOM-MSG
GO TO 900-EXIT-RANDGEN.
IF COUNT-OF-ACCESSES NOT NUMERIC
MOVE 'COUNT-OF-ACCESSES NOT NUMERIC' TO RANDOM-MSG
GO TO 900-EXIT-RANDGEN.
IF COUNT-OF-ACCESSES GREATER THAN RANDOM-NUMBERS-NEEDED
MOVE 'COUNT-OF-ACCESSES > THAT RANDOM-NUMBERS-NEEDED'
TO RANDOM-MSG
GO TO 900-EXIT-RANDGEN.
IF YOU-PROVIDE-SEED = SPACE AND RANDOM-NUMBER = ZERO
DISPLAY 'DO YOU WANT TO PROVIDE SEED Y OR N: '
NO ADVANCING
ACCEPT YOU-PROVIDE-SEED.
IF RANDOM-NUMBER = ZERO AND
(YOU-PROVIDE-SEED = 'Y' OR 'y')
DISPLAY 'ENTER SEED ' NO ADVANCING
ACCEPT RANDOM-NUMBER.
IF RANDOM-NUMBER NOT NUMERIC
MOVE 'RANDOM-NUMBER NOT NUMERIC' TO RANDOM-MSG
GO TO 900-EXIT-RANDGEN.
200-RANDGEN-DATA-HOUSEKEEPING.
MOVE FUNCTION CURRENT-DATE (9:8) TO SEED-TIME.
IF COUNT-OF-ACCESSES = ZERO
COMPUTE LOW-RANGE =
((SECONDS * HOURS * MINUTES * MS) / 3).
COMPUTE RANDOM-NUMBER-BUILT = RANDOM-NUMBER + LOW-RANGE.
COMPUTE HIGH-RANGE = RANDOM-NUMBERS-NEEDED + LOW-RANGE.
MOVE X2-POWER TO 2X2.
300-SET-2X2-DIVISOR.
IF 2X2 < (HIGH-RANGE + 1)
COMPUTE 2X2 = 2X2 * X2-POWER
GO TO 300-SET-2X2-DIVISOR.
* *********************************************************
* IF FIRST TIME THROUGH AND YOU WANT TO BUILD A SEED. *
* *********************************************************
IF COUNT-OF-ACCESSES = ZERO AND RANDOM-NUMBER = ZERO
COMPUTE RANDOM-NUMBER-BUILT =
((SECONDS * HOURS * MINUTES * MS) + HIGH-RANGE).
IF COUNT-OF-ACCESSES = ZERO
DISPLAY 'SEED TIME ' SEED-TIME
' RANDOM-NUMBER-BUILT ' RANDOM-NUMBER-BUILT
' LOW-RANGE ' LOW-RANGE.
* *********************************************
* END OF BUILDING A SEED IF YOU WANTED TO *
* *********************************************
* ***************************************************
* THIS PROCESS IS WHERE THE RANDOM-NUMBER IS BUILT *
* ***************************************************
400-RANDGEN-FORMULA.
COMPUTE FIRST-PART = (5 * RANDOM-NUMBER-BUILT) + 7.
DIVIDE FIRST-PART BY 2X2 GIVING WORKING-NUMBER
REMAINDER RANDOM-NUMBER-BUILT.
IF RANDOM-NUMBER-BUILT > LOW-RANGE AND
RANDOM-NUMBER-BUILT < (HIGH-RANGE + 1)
GO TO 600-RANDGEN-CLEANUP.
GO TO 400-RANDGEN-FORMULA.
* *********************************************
* GOOD RANDOM NUMBER HAS BEEN BUILT *
* *********************************************
600-RANDGEN-CLEANUP.
ADD 1 TO COUNT-OF-ACCESSES.
COMPUTE RANDOM-NUMBER =
RANDOM-NUMBER-BUILT - LOW-RANGE.
* *******************************************************
* THE NEXT 3 LINE OF CODE ARE FOR TESTING ON CONSOLE *
* *******************************************************
DISPLAY RANDOM-NUMBER.
IF COUNT-OF-ACCESSES < RANDOM-NUMBERS-NEEDED
GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.
900-EXIT-RANDGEN.
IF RANDOM-MSG NOT = SPACE
DISPLAY 'RANDOM-MSG: ' RANDOM-MSG.
MOVE ZERO TO COUNT-OF-ACCESSES RANDOM-NUMBERS-NEEDED RANDOM-NUMBER.
MOVE SPACE TO YOU-PROVIDE-SEED RUN-AGAIN.
DISPLAY 'RUN AGAIN Y OR N '
NO ADVANCING.
ACCEPT RUN-AGAIN.
IF (RUN-AGAIN = 'Y' OR 'y')
GO TO 100-RANDGEN-EDIT-HOUSEKEEPING.
ACCEPT PAUSE-FOR-A-SECOND.
GOBACK.
여기에있는 대부분의 답변은 동일한 숫자를 두 번 반환하지 않을 것이라고 보장하지 않습니다. 올바른 해결책은 다음과 같습니다.
int nrrand(void) {
static int s = 1;
static int start = -1;
do {
s = (s * 1103515245 + 12345) & 1023;
} while (s >= 1001);
if (start < 0) start = s;
else if (s == start) abort();
return s;
}
제약 조건이 올바르게 지정되어 있는지 잘 모르겠습니다. 1000 개의 다른 출력 후에 값을 반복 할 수 있다고 가정하지만 1000 세트의 끝과 시작에 모두 나타나는 한 0 다음에 순진하게 0을 따라갈 수 있다고 가정합니다. 반대로, 거리를 유지할 수 있습니다. 반복 사이에 1000 개의 다른 값을 지정하면 해당 제한을 벗어난 다른 값이 없기 때문에 매번 시퀀스가 정확히 동일한 방식으로 재생되는 상황이 발생합니다.
다음은 값을 반복하기 전에 항상 500 개 이상의 다른 값을 보장하는 방법입니다.
int nrrand(void) {
static int h[1001];
static int n = -1;
if (n < 0) {
int s = 1;
for (int i = 0; i < 1001; i++) {
do {
s = (s * 1103515245 + 12345) & 1023;
} while (s >= 1001);
/* If we used `i` rather than `s` then our early results would be poorly distributed. */
h[i] = s;
}
n = 0;
}
int i = rand(500);
if (i != 0) {
i = (n + i) % 1001;
int t = h[i];
h[i] = h[n];
h[n] = t;
}
i = h[n];
n = (n + 1) % 1001;
return i;
}
N이 1000보다 크고 K 개의 랜덤 샘플을 추출해야하는 경우 지금까지 샘플이 포함 된 세트를 사용할 수 있습니다. 각 추첨에 대해 거부 샘플링 을 사용합니다 대해 "거의"O (1) 연산이 될 하므로 총 실행 시간은 O (N) 저장시 거의 O (K)입니다.
이 알고리즘은 K가 "가까운"N 일 때 충돌이 발생합니다. 이는 실행 시간이 O (K)보다 훨씬 나쁘다는 것을 의미합니다. 간단한 수정은 논리를 반대로하여 K> N / 2의 경우 아직 그려지지 않은 모든 샘플의 기록을 유지하는 것입니다. 각각의 드로우는 거부 세트에서 샘플을 제거합니다.
거부 샘플링의 또 다른 명백한 문제는 O (N) 스토리지라는 점입니다. N이 수십억 이상인 경우 나쁜 소식입니다. 그러나이 문제를 해결하는 알고리즘이 있습니다. 이 알고리즘은 발명자 이후 Vitter의 알고리즘이라고합니다. 알고리즘은 여기 에 설명되어 있습니다 . Vitter 알고리즘의 요점은 각 드로우 후에 균일 한 샘플링을 보장하는 특정 분포를 사용하여 랜덤 스킵을 계산한다는 것입니다.
for i from n−1 downto 1 do
j ← random integer such that 0 ≤ j ≤ i
exchange a[j] and a[i]
마지막 두 개에 대해 하나의 스왑 만 필요하므로 실제로는 O (n-1)
입니다.
public static List<int> FisherYates(int n)
{
List<int> list = new List<int>(Enumerable.Range(0, n));
Random rand = new Random();
int swap;
int temp;
for (int i = n - 1; i > 0; i--)
{
swap = rand.Next(i + 1); //.net rand is not inclusive
if(swap != i) // it can stay in place - if you force a move it is not a uniform shuffle
{
temp = list[i];
list[i] = list[swap];
list[swap] = temp;
}
}
return list;
}
내 답변을 참조하십시오 https://stackoverflow.com/a/46807110/8794687 참조하십시오
그것은 평균 시간 복잡도가 간단한 알고리즘 중 하나 인 O ( S 로그 들 ) s의 샘플 크기를 나타내는. 복잡성으로 주장의 해시 테이블 알고리즘에 대한 몇 가지 링크가도있다 O ( 들 ).
누군가 "엑셀에서 난수 생성"을 게시했습니다. 나는이 이상적인 것을 사용하고 있습니다. str.index와 str.ran의 두 부분으로 구성된 구조를 만듭니다. 10 개의 난수에 대해 10 개의 구조로 구성된 배열을 만듭니다. str.index를 0에서 9로 설정하고 str.ran을 다른 난수로 설정하십시오.
for(i=0;i<10; ++i) {
arr[i].index = i;
arr[i].ran = rand();
}
arr [i] .ran의 값으로 배열을 정렬하십시오. str.index는 이제 무작위 순서입니다. 아래는 C 코드입니다.
#include <stdio.h>
#include <stdlib.h>
struct RanStr { int index; int ran;};
struct RanStr arr[10];
int sort_function(const void *a, const void *b);
int main(int argc, char *argv[])
{
int cnt, i;
//seed(125);
for(i=0;i<10; ++i)
{
arr[i].ran = rand();
arr[i].index = i;
printf("arr[%d] Initial Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
}
qsort( (void *)arr, 10, sizeof(arr[0]), sort_function);
printf("\n===================\n");
for(i=0;i<10; ++i)
{
printf("arr[%d] Random Order=%2d, random=%d\n", i, arr[i].index, arr[i].ran);
}
return 0;
}
int sort_function(const void *a, const void *b)
{
struct RanStr *a1, *b1;
a1=(struct RanStr *) a;
b1=(struct RanStr *) b;
return( a1->ran - b1->ran );
}