【问题标题】:Random Number Generation of Combination of six numbers六数组合的随机数生成
【发布时间】:2019-11-01 19:09:14
【问题描述】:

我正在做一个伪随机数生成器,它创建 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);
} 

有没有办法简化代码?

【问题讨论】:

  • 不用生成和检查重复,可以shuffle数组,看geeksforgeeks.org/…
  • rand() % 57 + 1 生成一个从 1 到 57 的随机数。请注意,1 和 57 都出现在您的示例输出中。
  • 57 也出现了?我希望 1 出现但 57 不出现 .. 所以它必须是 56 + 1 ?
  • 我建议这个问题太开放了。也许是“减少长度”或“简化”而不是“改进”。代码当然可以改进 - 以你目前不关心的方式,只是你真正想要实现的噪音和意见。
  • 另见stackoverflow.com/questions/10984974/…关于在给定范围内生成随机数的方法

标签: c file random combinations


【解决方案1】:

您可以通过循环迭代当前集来简化您的唯一性测试,其中对于所有前面的值您测试当前值的唯一性:

            // 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+”)
  • 错误检查 I/O 函数
  • 不要不必要地使用 stdint 类型 - 算术运算经常会导致隐式转换为 int。使用int,除非有充分的理由不避免意外。使用 stdint 的充分理由包括符合某些文件格式或通信协议,或匹配设备驱动程序中的寄存器宽度。不在这里。
  • 例如测试和修复错误 (rand() % 57 + 1)。
  • 避免重复 - 使用循环(本例中的唯一性测试)。
  • 只说一些对 cme​​ts 有用的东西。如果代码是自我记录的,请保持安静。如果不明显,请解释 - 不要简单地重复任何人可以从代码中看到的内容(例如“生成随机数”)
  • 不要用现在进行时写 cmets(没有 'ing 动词)。好的,并不是真正的改进,只是我觉得很烦人的东西;-)。源代码描述了程序做的事情,而不是它正在做的事情。
  • 不要在每行最后一个值之后写多余的空格。
  • 删除不必要的代码 - 例如最后的循环归零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() % nn 不是RAND_MAX+1 的一个因素时出现的“随机偏差”问题。如果随机性很关键,您可能需要考虑这一点。

【讨论】:

    【解决方案2】:

    您可以通过使用已使用值的数组来简化。

    例如添加:

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

    【讨论】:

      【解决方案3】:

      为什么不这样:

      #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 的次数最少。这个运行时间最长的部分可能是内存在范围池中的移动,但它会运行一个确定的时间。

      【讨论】:

        猜你喜欢
        • 2021-05-15
        • 2010-11-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-05-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多