# 为“生成代码”定义参数

上一个例子的结尾,我们发现了即使使用了 rand() 来生成数据,相同的代码生成的数据仍然是一模一样的,这是因为 rand() 函数是一个伪随机数,在不定义随机数种子的情况下,每次执行的结果都是相同的,而使用 srand() 函数可以解决这个问题:

#include <iostream>
#include <cstdlib>
#include <time.h>
using namespace std;
int main(){
    srand((unsigned)time(NULL));
    int n = 10000;
    cout<<n<<endl;
    for(int i=0;i<n;i++){
        cout<<rand()<<endl;
    }
}

通过 srand() 我们可以设定一个随机数种子,以往我们会选择使用时间戳来解决,通过运行我们发现确实生效了,它生成了规模为 n=10000,但内容不相同的 5 个文件。

不过此时又遇到了其他问题:一方面我们希望每次都能生成相同的那 5 组随机数据,这有点拗口,大致意思是这次我们生成了内容不相同的 5 个文件,但我希望下次我们点击生成按钮的时候,我仍然可以获得之前那次的 5 个文件。

另一方面,由于我们是自动化生成了 5 个文件,它们时间运行的间隔时间很短,以至于 (unsigned)time(NULL) 的精度不够用了,使其无法成为合格的随机数种子,这 5 个文件可能使用的是同一个时间戳种子。

总之,我们在寻找一种方式,让我们可以自己定义随机数种子,但又不至于将这段代码复制 5 遍,唯一的区别是修改一下 srand(10086) 中的种子。

我们注意到代码输入框的旁边还有一列叫做 运行指令参数 (args) 的列表,它的输入框个数与你需要的生成数量是相同的,我们可以在这里手动输入每次执行的参数:

我们输入 5 个不同的整数,作为随机数种子,然后,我们可以在代码中获取到这些种子:

#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc,char *argv[]){
    unsigned seed = stoul(argv[argc-1]); // 使用 argv[argc-1] 获取执行参数中的最后一个
    srand(seed);
    int n = 10000;
    cout<<n<<endl;
    for(int i=0;i<n;i++){
        cout<<rand()<<endl;
    }
}

此时点击生成,我们看到已经实现了我们想要的随机效果,并且随机数的种子也是我们指定的,更进一步,我们可以通过这个特性来快速生成不同规模大小的数据,将上一节n=10000n=20000 合并为一份代码,并且给 n 的取值更多样的选择:

我们的参数中第一个是 n,第二个是随机数种子 seed,最后的代码如下:

#include <iostream>
#include <cstdlib>
using namespace std;
int main(int argc,char *argv[]){
    unsigned seed = stoul(argv[argc-1]); // seed 是最后一个参数 argv[argc-1]
    srand(seed);
    int n = stoul(argv[argc-2]); // seed 是倒数第二个参数 argv[argc-2]
    cout<<n<<endl;
    for(int i=0;i<n;i++){
        cout<<rand()<<endl;
    }
}

TIP

由于 argv[] 中会包含执行时的完整指令,因此我们推荐反着取参数,以确保在不同的环境中通用。

至此你已经学会了如何使用 Aspirin 数据生成器来生成数据了,该生成器在过去 6 年中已经为一些组织机构生成了上千道题目的数据,如果你对 OJ 题目感兴趣的话,也可以访问 YACS 竞赛平台 (opens new window),该平台会定期展开月赛,并且数据均由该生成器制作。