Генерация случайных чисел из комбинации шести чисел - PullRequest
0 голосов
/ 01 ноября 2019

Я делаю генератор псевдослучайных чисел, который создает 100 комбинаций из 6 чисел в диапазоне от 1 до 56 без повторений, а затем сохраняет их в текстовом файле. Например:

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);
} 

есть способ упростить код?

Ответы [ 3 ]

1 голос
/ 01 ноября 2019

Вы можете упростить свой тест уникальности, повторяя текущий набор с циклом, где для всех всех предшествующих значений, которые вы проверяете на уникальность из текущего значения:

            // 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] ;
                }
            }

Обратите внимание, что еслиЕсли заданная длина велика, то подход сканирования всех предыдущих значений может быть неоптимальным, но в этом случае кажется разумным и простым.

Поскольку вы (вроде бы) спросили,код может быть дополнительно улучшен с помощью (например, кода, включающего в себя следующее):

  • Устранить магические числа
  • Инициализация первого члена массива, инициализация всех остальных членов в ноль.
  • Инициализировать при создании экземпляра (а не определять и назначать отдельно).
  • Создавать экземпляры в максимально узкой области действия - не все в верхней части функции
  • Не открывать файлдля типов доступа, которые вы не используете («w», а не «w +»)
  • Проверка ошибок функций ввода-вывода
  • Не используйте типы stdint без необходимости - арифметические операции frравным образом приводят к неявным приведениям к 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. Если случайность имеет решающее значение, вы можете рассмотреть это.

0 голосов
/ 01 ноября 2019

Почему бы не что-то вроде:

#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;
}

Обратите внимание, что я звоню случайным образом минимальное количество раз с этим. Самая длинная часть этой среды выполнения - это, вероятно, перемещение памяти в пуле диапазонов, но она будет работать определенное количество времени.

0 голосов
/ 01 ноября 2019

Вы можете упростить использование массива используемых значений.

Например, добавьте:

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 в этом месте в массиве. При поиске значения мы выполняем цикл, пока не найдем пустой слот.

Чтобы использовать вместо этого массив shuffle, вы должны сделать это.

Создать массив со всеми значениями перед первым for -loop:

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];
...