# 为“生成代码”定义参数
在上一个例子的结尾,我们发现了即使使用了 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=10000
与 n=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),该平台会定期展开月赛,并且数据均由该生成器制作。
← 快速生成多个输入文件 进化! 保存项目文件 →