Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby-on-rails-4/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在C语言中以均匀概率从文本文件中有效地选择随机行?_C_Random_Random Access - Fatal编程技术网

在C语言中以均匀概率从文本文件中有效地选择随机行?

在C语言中以均匀概率从文本文件中有效地选择随机行?,c,random,random-access,C,Random,Random Access,这本质上是一个更受约束的版本 假设我们有一个非常大的文本文件,包含大量的行 我们需要以统一的概率从文件中随机选择一行,但有一些限制: 因为这是一个软实时应用程序,我们不能迭代整个文件。选择应该持续一定的时间 由于内存限制,无法缓存该文件 由于允许文件在运行时更改,因此不能假定文件的长度为常量 我的第一个想法是使用lstat()调用来获取以字节为单位的总文件大小fseek()直接访问随机字节偏移量,从而获得对文件随机部分的O(1)访问 问题是,我们不能像读到下一条新行并结束这一天那样做,因为这

这本质上是一个更受约束的版本

假设我们有一个非常大的文本文件,包含大量的行

我们需要以统一的概率从文件中随机选择一行,但有一些限制:

  • 因为这是一个软实时应用程序,我们不能迭代整个文件。选择应该持续一定的时间
  • 由于内存限制,无法缓存该文件
  • 由于允许文件在运行时更改,因此不能假定文件的长度为常量
我的第一个想法是使用
lstat()
调用来获取以字节为单位的总文件大小<然后可以使用code>fseek()直接访问随机字节偏移量,从而获得对文件随机部分的O(1)访问

问题是,我们不能像读到下一条新行并结束这一天那样做,因为这样会产生一个偏向于长行的分布

我解决这个问题的第一个想法是一直读到第一个“n”换行符(如果需要,可以换回文件的开头),然后从这个较小的集合中选择一个概率相同的行。可以安全地假设文件的内容是随机排序的,因此该子样本的长度应该是一致的,并且由于其起点是从所有可能的点中统一选择的,因此它应该代表从整个文件中统一选择的。因此,在pseudo-C中,我们的算法类似于:

 lstat(filepath, &filestat);
 fseek(file, (int)(filestat.off_t*drand48()), SEEK_SET);
 char sample[n][BUFSIZ];
 for(int i=0;i<n;i++)
     fgets(sample[i], BUFSIZ, file); //plus some stuff to deal with file wrap around...
 return sample[(int)(n*drand48())];
lstat(文件路径和文件状态);
fseek(file,(int)(filestat.off\u t*drand48()),SEEK\u SET);
煤焦样品[n][BUFSIZ];

对于(int i=0;i如果文件只在末尾更改(添加更多行),则可以创建具有统一概率的算法:

准备:创建一个索引文件,其中包含第n行的偏移量。使用固定宽度格式,以便可以使用该位置确定您拥有的记录

  • 打开索引文件并读取最后一条记录。使用
    ftell
    确定记录编号

  • 打开大文件并
    fseek
    到步骤1中获得的偏移量

  • 将大文件读到最后,计算换行的数量。现在,您已经获得了大文件中的总行数

  • 生成一个不超过步骤3中获得的行数的随机数

  • fseek
    查找并读取索引文件中的相应记录

  • fseek
    到大文件中的适当偏移量。跳过其余的换行符

  • 读台词

  • 示例

    假设我们选择了n=100,并且这个大文件包含367行

    索引文件:

    00000000,00004753,00009420,00016303
    
  • 索引文件有4条记录,因此大文件至少包含300条记录(100*(4-1))。最后一个偏移量为16303

  • 打开大文件并
    fseek
    至16303

  • 计算剩余的行数(67)

  • 生成[0-366]范围内的随机数。假设我们得到112

  • 112/100=1,余数为12。读取偏移量为1的索引文件记录。我们得到结果4753

  • fseek
    到大文件中的4753,然后跳过11(12-1)行

  • 读第12行

  • 编辑:


    我看到有关目标文件的注释发生了变化。如果目标文件很少发生变化,那么这可能仍然是一种可行的方法。在切换目标文件之前,您需要创建一个新的索引文件。当目标文件增长超过
    n
    行时,您可能还需要更新索引文件。

    找到了解决方案,该解决方案在非常好。在这里为我自己和其他人记录

    这个示例代码在实践中每秒绘制80000次,平均行长度与大多数运行中文件的行长度匹配到4个有效数字。相比之下,我使用来自的方法每秒绘制250次

    从本质上讲,它所做的是在文件中随机抽取一个位置,然后丢弃它,并以与行长度成反比的概率再次绘制。这就消除了较长单词的偏差。平均而言,该方法在接受一个绘制之前,使绘制数等于文件中的平均行长度

    一些明显的缺点:

    • 线条长度较长的文件在每次绘制时会产生更多的拒绝,这会使绘制速度慢得多
    • 具有较长线条长度的文件要求rdraw函数中的常量大于50,这意味着如果线条长度表现出较大的差异,则实际查找时间可能要长得多。例如,在我测试的一个文件上,将其设置为BUFSIZ,绘制速度降低到每秒10000次左右。仍然比在t中计算线条快得多不过他还是归档了

      int rdraw(FILE* where, char *storage, size_t bytes){
          int offset = (int)(bytes*drand48());
          int initial_seek = offset>50?offset-50:0;
          fseek(where, initial_seek, SEEK_SET);
          int chars_read = 0;
          while(chars_read + initial_seek < offset){
                  fgets(storage,50,where);
                  chars_read += strlen(storage);
          }
          return strlen(storage);
      }
      
      int main(){
          srand48(time(NULL));
          struct stat blah;
          stat("/usr/share/dict/words", &blah);
          FILE *where = fopen("/usr/share/dict/words", "r");
          off_t bytes = blah.st_size;
          char b[BUFSIZ+1];
      
          int i;
          for(i=0;i<1000000; i++){
                  while(drand48() > 1.0/(rdraw(where, b, bytes)));
          }
      
      }
      
      int-rdraw(文件*其中,字符*存储,大小\u t字节){
      int offset=(int)(字节*drand48());
      int初始搜索=偏移>50?偏移-50:0;
      fseek(其中,初始搜索,搜索集);
      int chars_read=0;
      while(字符读取+初始搜索<偏移){
      fgets(储存,50,若有);
      字符读取+=strlen(存储);
      }
      返回strlen(存储);
      }
      int main(){
      srand48(时间(空));
      结构统计等等;
      stat(“/usr/share/dict/words”,等等);
      文件*where=fopen(“/usr/share/dict/words”,“r”);
      off_t bytes=blah.st_size;
      字符b[BUFSIZ+1];
      int i;
      对于(i=0;i 1.0/(rdraw(其中,b,字节));
      }
      }
      

    从文件中选择一个随机字符(如您所述,通过rand和seek)。现在,我将应用以下算法,而不是查找关联的换行符,因为这是有偏差的,如您所述:

    
    Is the character a newline character?
       yes - use the preceeding line
       no  - try again
    
    我看不出这怎么能给出线的均匀分布。效率取决于线的平均长度。如果