我正在做一个伪随机数生成器,它创建 100 个从 1 到 56 范围内的 6 个数字的组合,不重复,然后将它们保存在文本文件中。像这样:
33 28 46 7 30 57
15 29 43 41 16 21
11 43 7 18 31 25
36 32 19 42 47 33
46 13 14 1 28 25
33 14 55 43 29 13
30 14 12 45 46 32
56 31 54 32 20 21
10 52 40 57 31 14
28 44 15 47 57 45
...
这是我的代码,但我觉得它有点太大了,特别是检查数字是否重复的部分
// Including C Standard Libraries
#include <stdint.h>
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
// Initialing Random Generation C Library
srand(time(NULL));
// Variables for PRNG Loop
uint16_t i;
uint8_t j;
uint8_t x[6] = {0,0,0,0,0,0};
// Opening File to save Results
FILE *fp;
fp = fopen("combinations.txt", "w+");
// PRNF Loop
for(i = 0; i<100; i++) // Numbers of Combinations
{
for(j = 0; j<6; j++) // Number of Elements of the Combinations
{
x[j] = rand() % 57 + 1; // Generating Random Number
// Avoiding Repetition of Numbers
if ( j==1)
{
while(x[1] == x[0])
{
x[1] = rand() % 57 + 1;
}
}
if ( j==2)
{
while(x[2] == x[0] || x[2] == x[1])
{
x[2] = rand() % 57 + 1;
}
}
if ( j==3)
{
while(x[3] == x[0] || x[3] == x[1] || x[3] == x[2] )
{
x[3] = rand() % 57 + 1;
}
}
if ( j==4)
{
while(x[4] == x[0] || x[4] == x[1] || x[4] == x[2] || x[4] == x[3] )
{
x[4] = rand() % 57 + 1;
}
}
if ( j==5)
{
while(x[5] == x[0] || x[5] == x[1] || x[5] == x[2] || x[5] == x[3] || x[5] == x[4] )
{
x[5] = rand() % 57 + 1;
}
}
fprintf(fp, "%d", x[j]); // Saving Random Number in File
fprintf(fp, " ");
}
fprintf(fp, "\n"); // Saving Newline
for (int i = 0; i < 6; ++i)
{
x[i] = 0;
}
}
fclose(fp);
}
有没有办法简化代码?
您可以通过使用循环迭代当前集来简化唯一性测试,其中对于所有所有前面值,您测试当前值的唯一性:
// Generate a unique random number
bool unique = false ;
while( !unique )
{
x[j] = rand() % RANGE + 1; // Random 1 to 56
// Test uniqueness from all preceding values in
// the set (x[0] is always unique)
unique = true ;
for( int k = 1; unique && k < j; k++ )
{
unique = x[k] != x[j] ;
}
}
请注意,如果设置的长度很大,那么扫描所有前面的值方法可能不是最佳的,但在这种情况下似乎合理且简单。
既然您(有点)要求,可以通过以下方式进一步改进代码(例如 - 合并这些的代码如下):
int
。使用 int
除非有充分的理由不这样做以避免意外。使用 stdint 的充分理由包括符合某些文件格式或通信协议,或者匹配设备驱动程序中的寄存器宽度。不在这里。 rand() % 57 + 1
)。x[]
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SET_LENGTH 6 // Number of values in each set
#define SET_COUNT 100 // Number of sets
#define RANGE 56 // Range of each value 1 to RANGE
int main()
{
// Initialing Random Generation C Library
srand(time(NULL));
int x[SET_LENGTH] = {0};
// Open file
FILE* fp = fopen( "combinations.txt", "w" ) ;
if( fp != NULL )
{
// For each random number set...
for( int i = 0; !ferror( fp ) && i < SET_COUNT; i++ )
{
// For each number in the set...
for( int j = 0; j < SET_LENGTH; j++)
{
// Generate a unique random number
bool unique = false ;
while( !unique )
{
x[j] = rand() % RANGE + 1; // Random 1 to 56
// Test uniqueness from all preceding values in
// the set (x[0] is always unique)
unique = true ;
for( int k = 1; unique && k < j; k++ )
{
unique = x[k] != x[j] ;
}
}
// Write value to file.
// Space separated values with newline end.
fprintf(fp, "%d%c",x[j],
j < SET_LENGTH - 1? ' ' : '\n' ) ;
}
}
}
fclose(fp);
return 0 ;
}
这里没有“改进”的是当
rand() % n
,n
不是RAND_MAX+1
的因子时发生的“随机偏差”问题。如果随机性很关键,您可能需要考虑这一点。
您可以通过使用已用值数组来简化。
例如添加:
uint8_t used[56] = {0};
第一个和第二个
for
循环之间
然后这样选择您的值:
do
{
x[j] = rand() % 56 + 1; // Generating Random Number
} while (used[x[j]-1] != 0);
used[x[j]-1] == 1;
当使用一个值时,我们将 1 放在数组中的该位置。 当寻找一个值时,我们循环直到找到一个空槽。
要使用随机数组,您可以这样做。
创建一个包含第一个
for
循环之前的所有值的数组:
uint8_t shuffle[56] = { 1, 2, 3, 4, 5, 6, 7, /* ... */ 54, 55, 56 };
然后这样选择你的价值观:
uint8_t pos = rand() % (56-j); // Generating Random Number Location
x[j] = shuffle[pos]; // Selecting Random Number
shuffle[pos] = shuffle[55];
shuffle[55] = x[j];
为什么不类似:
#define NUM_RANDOM_NUMBERS 56
// Include all possible numbers.
int range[NUM_RANDOM_NUMBERS];
// Record current size of the array (this could be dynamic if we don't know the starting size...
int curSize = NUM_RANDOM_NUMBERS;
int getRandom()
{
// If we are called with curSize at 0 or less, this is an error condition. We have no numbers to return.
assert(curSize > 0);
// Get a number from our table and place it into a return Value.
int index = random() % curSize;
int retValue = range[index];
// Move all the values above the index over the swapped out value.
// memmove would probably be more efficient.
for(int i = index; i < curSize - 1; i++)
range[i] = range[i+1];
// Reduce the size of our pool of numbers.
curSize--;
// Return the value;
return retValue;
}
int main()
{
// Load the range pool with all the possible numbers.
for(int i=0; i<NUM_RANDOM_NUMBERS; i++)
{
range[i] = i+1;
}
// Now retreive them in random order.
for(int i=0; i<NUM_RANDOM_NUMBERS; i++)
{
printf("%d \n", getRandom());
}
return 0;
}
请注意,我用此方法调用 random 的次数最少。该运行时间最长的部分可能是范围池中内存的移动,但它将运行确定的时间量。
您可以使用下面的这个函数从一组 N 个整数中生成 k 个整数的组合。 它在变量 arr 中返回从 0 到 N-1 的 k 个值,该变量是一个数组。
void random_combination(int *arr, int N, int k){
// function returning shuffled indexes
// arr: array to receive shuffled indexes
// N: size of the array
// k: number of index to return. Must be lower or equal to N
int used[N]; // storage of already picked indexes
for (int i=0; i<N; i++) used[i]=0;
int cnt; // counter for unused indexes
int r; // holds the random number
for (int i=0; i<k; i++){
r = rand() % (N-i); // generate random number modulo the remaining number of unused indexes
cnt = 0;
for (int j=0; j<N; j++){ // going through each used/unused indexes
if (used[j]==0){
if (cnt==r){
used[j] = 1;
arr[i] = j;
//printf("r: %d j: %d\n", r, j); // uncomment me to see what I do.
break;
}
cnt++;
}
}
}
}
对于此处的示例,请按如下方式使用
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
// copy paste the above function here
int main(int argc, char *argv[]){
srand(time(0));
int NN = 56;
int k = 6;
int arr[NN];
random_combination(arr, NN, k);
for (int i=0; i<k; i++){
printf(" %d", arr[i]+1);
// do whatever is needed with arr[i]+1 here
}
printf("\n");
}