首页 > 其他分享 >学习日常:造数据 - 上

学习日常:造数据 - 上

时间:2024-08-08 21:30:21浏览次数:5  
标签:cout int ll long 学习 参数 日常 cpp 数据

前言

上次自己造数据,感悟颇丰,今天就来写一下这个话题。

陈老师将这个任务交给我们时,给了我们一个板子,姑且叫它 build_data.cpp:

/*
测试数据生成说明:
1.本文件放入标程同文件夹
2.在标程内贴入右边语句(不要修改): freopen("data.in","r",stdin); freopen("data.out","w",stdout);
3.运行标程,生成标程的exe文件
4.修改下面 0-3 处地方,根据题意,打印 in 文件
5.运行本程序,批量生成对应的in文件和out文件
P.S.本程序无法覆盖同名文件,如果生成过程有错,需要手动删除之前同名文件
*/
#include<bits/stdc++.h>
using namespace std;
char fn[50],op[50];
int rand(int l,int r) { //RAND_MAX*RAND_MAX
	int tmp = (rand() * RAND_MAX + rand()) % (r - l + 1);
	return l+tmp;
}
mt19937_64 ran(time(0)^rand()^(*(new int)));
//修改 0 :控制数据规模
long long b[11]= {10,100,1000,10000,100000,100000,100000,100000,100000,100000};
long long bb[11]= {10,10,10,10,100,500,1000,1000,1000,1000};
long long bbb[11]= {50,50,50,100000000,100000000,100000000,100000000,100000000,100000000,100000000};
long long bbbb[11]= {2,2,2,5,5,10,10,10,10,10};
int main() {
	sprintf(fn,"A.exe");		//修改 1 :标程名称
	srand(time(0));
	for (int fd=0; fd<=19; fd++) {			//修改 2:fd是 data文件的数字后缀 0-9
		sprintf(op,"data.in",fn);
		freopen(op,"w",stdout);
		//↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓  修改 3 : in文件的内容
		// do something...
		
		//↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑  修改 3 : in文件的内容
		freopen("con","w",stdout);
		system(fn);
		sprintf(op,"ren data.in data%d.in",fd);
		system(op);
		sprintf(op,"ren data.out data%d.out",fd);
		system(op);
	}
	return 0;
}

不知道大家觉得这个这个板子怎么样,反正我是觉得又丑又没用,我认为可以改进,于是就自己搓了一个 generator.cpp

改进

发现缺陷

首先,我认为这个板子有以下的缺陷:

  • 需要手动删除造错的数据,很不方便。
  • 需要在标程内写以下语句:
freopen("data.in","r",stdin); 
freopen("data.out","w",stdout);

感觉不太好,破坏了标程其实就是强迫症

  • 生成输入数据批量生成数据的程序写到了一起,个人觉得看起来分开更好,层次更分明,也不需要对着这份 build_data.cpp 不停改,其实也是强迫症
  • 实现很丑,还有一些意义不明的神奇东西,其实还是强迫症

于是针对这些问题,我自己搓了一个板子,将原来一个 build_data.cpp 拆成了两个程序:datamaker.cppgenerator.cpp

一分为二

datamaker.cpp

datamaker.cpp 负责生成输入数据

#include<bits/stdc++.h>
#define to_int(x) atoi(x)
#define to_ll(x) atoll(x)
#define shuf(st,ed) shuffle(st,ed,default_random_engine(rnd()))
using namespace std;
typedef long long ll;
mt19937_64 rnd(time(0));
ll rand(ll l,ll r){return rnd()%(r-l+1)+l;} 
int main(int argc,char *argv[]){
	// do something...
	return 0;
}

这里我写了一些造数据时常用的函数和宏,我们一个个来看:

  • shuf(st,ed):打乱 \([st,ed)\) 中的元素。
  • to_intto_ll:将 C 风格字符串(字符数组)转化为其存储的整数类型。
  • rand(l,r):生成 \([l,r]\) 中的随机数。

\(p.s.\) 随机数的生成采用 mt19937_64

其中,to_intto_ll 的使用场景下文会讲到,实际操作时我们也只需要修改这个文件即可。

generator.cpp

generator.cpp 负责批量生成数据

#include<bits/stdc++.h>
#include<windows.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const int CAS=205;
int cas,st,cnt,flag;
string pn,dmn,stdn,u,order,fn;
// problem name,data maker name,std name,file name
struct Unknownn{ll a[CAS];}U;
vector<Unknownn> v;
int main(){
	// Part1.
	cout<<"Loading",Sleep(500),system("cls");
	cout<<"Problem's name? ";
	cin>>pn;
	cout<<"How many cases do you want? ";
	cin>>cas;
	cout<<"The beginning ID? ";
	cin>>st;
	cout<<"DataMaker's name?(No \".exe\") ";
	cin>>dmn;
	cout<<"Std's name?(No \".exe\") ";
	cin>>stdn;
	Sleep(500);
	// Part2.
	system("cls");
	cout<<"How many unknown do you want? ";
	cin>>cnt;
	for(int i=1;i<=cnt;i++){
		cout<<"Unknown "+to_string(i)+":\nname? ";
		cin>>u;
		cout<<"val?\n";
		for(int j=1;j<=cas;j++) cout<<"Case #"<<j+st-1<<":",cin>>U.a[j];
		v.push_back(U);
		cout<<"Unknown "+to_string(i)+" already configured!\n";
	}
	Sleep(500);
	system("cls");
	// Part3.
	cout<<"What do you want to delete?\n1:All\n2:Only for use";
	cin>>flag;
	if(flag==1){
		cout<<"Deleting..."<<endl;
		system(("del "+pn+"*.in").c_str());
		system(("del "+pn+"*.out").c_str());
		Sleep(500);
	}
	cout<<"Making Input..."<<endl;
	for(auto k:v){
		for(int i=1;i<=cas;i++) cout<<k.a[i]<<" ";
		puts("");
	}
	for(int i=st;i<=st+cas-1;i++){
		order=dmn+".exe";
		for(auto k:v) order+=" "+to_string(k.a[i-st+1]);
		order+=" > "+pn+to_string(i)+".in";
		system(order.c_str());
		cout<<order<<endl;
		Sleep(1000);
	}
	Sleep(500);
	cout<<"Making Output..."<<endl;
	for(int i=st;i<=st+cas-1;i++){
		system((stdn+".exe < "+pn+to_string(i)+".in > "+pn+to_string(i)+".out").c_str());
		cout<<(stdn+".exe < "+pn+to_string(i)+".in > "+pn+to_string(i)+".out").c_str()<<endl;
	}
	cout<<"Succeed!";
	return 0;
}

可能要一下子搞懂这么多东西有些困难,我们将其拆开进行讲解。

\(p.s.\) 实际上这份程序在造数据时也是无需修改的,只需写好标程和 datamaker.cpp,再直接运行这个程序即可,这个程序在使用过程中有很多引导性的提示词,直接运行它,根据提示词应该就可以使用,所以如果你只是来拿代码的,那你可以不用往下读了。

约定:在下文中会出现每个部分的输入输出,使用连续的下划线表示真正使用时需要输入内容的位置,后面使用括号表示举例和说明

Part1. 基本配置

代码
// Part1.
cout<<"Loading",Sleep(500),system("cls");
cout<<"Problem's name? ";
cin>>pn;
cout<<"How many cases do you want? ";
cin>>cas;
cout<<"The beginning ID? ";
cin>>st;
cout<<"DataMaker's name?(No \".exe\") ";
cin>>dmn;
cout<<"Std's name?(No \".exe\") ";
cin>>stdn;
交互部分

开头会闪现一个 Loading,接着:

Problem's name? ______(问题的名称,会生成在数据的文件名中)
How many cases do you want? ______(测试点的数量)
The beginning ID? ______(测试点的起始编号,在分段造数据时用到)
DataMaker's name?(No ".exe") ______(datamaker.cpp应用程序的文件名,不需".exe")
Std's name?(No \".exe\") ______(标程应用程序的文件名,不需".exe")
解释

这部分代码阅读起来应该没有问题,很基础。

Part2. 设置参数

代码
struct Unknownn{ll a[CAS];}U;
vector<Unknownn> v;
// Part2.
cout<<"How many unknown do you want? ";
cin>>cnt;
for(int i=1;i<=cnt;i++){
cout<<"Unknown "+to_string(i)+":\nname? ";
cin>>u;
cout<<"val?\n";
for(int j=1;j<=cas;j++) cout<<"Case #"<<j+st-1<<":",cin>>U.a[j];
v.push_back(U);
cout<<"Unknown "+to_string(i)+" already configured!\n";
}
交互部分
How many unknown do you want? ______(需要调控的参数个数)
Unknown [k]:
name? ______(参数的名称)

解释

用于调控数据规模增加数据限制,后文再讲。

Part3. 生成数据

代码
// Part3.
cout<<"What do you want to delete?\n1:All\n2:Only for use";
cin>>flag;
if(flag==1){
	cout<<"Deleting..."<<endl;
	system(("del "+pn+"*.in").c_str());
	system(("del "+pn+"*.out").c_str());
	Sleep(500);
}
cout<<"Making Input..."<<endl;
for(auto k:v){
	for(int i=1;i<=cas;i++) cout<<k.a[i]<<" ";
	puts("");
}
for(int i=st;i<=st+cas-1;i++){
	order=dmn+".exe";
	for(auto k:v) order+=" "+to_string(k.a[i-st+1]);
	order+=" > "+pn+to_string(i)+".in";
	system(order.c_str());
	cout<<order<<endl;
	Sleep(1000);
}
Sleep(500);
cout<<"Making Output..."<<endl;
for(int i=st;i<=st+cas-1;i++){
	system((stdn+".exe < "+pn+to_string(i)+".in > "+pn+to_string(i)+".out").c_str());
	cout<<(stdn+".exe < "+pn+to_string(i)+".in > "+pn+to_string(i)+".out").c_str()<<endl;
}
cout<<"Succeed!";
交互部分
What do you want to delete?
1:All(删除所有对应问题名下的之前生成的数据)
2:Only for use(不物理删除文件,直接创建新文件或者覆盖之前的同名文件)
______(输入1或2,表示删除方式)
......(删除指令)
Making Input...
......(生成输入文件指令)
Making Output...
......(生成输出文件指令)
Succeed!
解释

这里运用了 cmd 中运行应用程序并指定输入输出位置的技术。

// cmd
test.exe [参数,空格分隔] < [输入文件名] > [输出文件名]

合二为一

理论部分

为什么前面的小标题叫 “一分为二”,这里又叫 “合二为一” 呢?因为我们还需要一种媒介,使得 generator.cpp 可以与 datamaker.cpp “沟通”,使它们在结构上分开在功能上合为一个整体

由于 generator.cpp 是带交互性的,所以我们需要让 generator.cpp 能将交互内容 “告诉” datamaker.cpp ,“指导” datamaker.cpp 的工作。

具体而言,datamaker.cpp 生成的数据应有与数据点相关的限制,如不同的数据规模特殊性质等,那我们的 generator.cpp 就做到了这一点。

我们看回 datamaker.cpp

#include<bits/stdc++.h>
#define to_int(x) atoi(x)
#define to_ll(x) atoll(x)
#define shuf(st,ed) shuffle(st,ed,default_random_engine(rnd()))
using namespace std;
typedef long long ll;
mt19937_64 rnd(time(0));
ll rand(ll l,ll r){return rnd()%(r-l+1)+l;} 
int main(int argc,char *argv[]){ // main 函数的参数是干什么用的?
	// do something...
	return 0;
}

你知道 main 函数的参数是干什么用的吗?

它就是两个程序 “沟通” 的媒介!

再看回 generator.cppPart 2

struct Unknownn{ll a[CAS];}U;
vector<Unknownn> v;
// Part2.
cout<<"How many unknown do you want? ";
cin>>cnt;
for(int i=1;i<=cnt;i++){
cout<<"Unknown "+to_string(i)+":\nname? ";
cin>>u;
cout<<"val?\n";
for(int j=1;j<=cas;j++) cout<<"Case #"<<j+st-1<<":",cin>>U.a[j];
v.push_back(U);
cout<<"Unknown "+to_string(i)+" already configured!\n";
}

刚开始我给这个部分取了一个小标题:设置参数

它的作用就是帮助你设置一些限制 datamaker.cpp 的参数,具体的调用参数则是在 Part 3 完成的:

// cmd
test.exe [参数,空格分隔] < [输入文件名] > [输出文件名]
//           这里↑

接着只需在 datamaker.cpp 中获取这些参数即可。

其中,main 函数的参数就是接受这些参数的部分。

  • int argc:接受到的参数个数。
  • char *argv[]:每个参数的 C 风格字符串(字符数组)。

因为我太菜了,还没写出不定长的参数,所以其实第一个参数没啥用。

第二个参数则是以 C 风格字符串(字符数组)的形式给出的,从 \(1\) 开始编号每个参数,我们可以使用 to_intto_ll 将其转化为整数类型。

目前的参数是用 long long 类型,仅支持整数类型,大家感兴趣的话可以加上别的类型,还可以写成模板的形式;或者可以写成按照数据点顺序排布的参数配置,这样每个数据点的参数数量可以都不一样。

实例部分

我们以 P1003 铺地毯 为例。

看它对数据范围的表述:

【数据范围】
对于 \(30\%\) 的数据,有 \(n \le 2\)。
对于 \(50\%\) 的数据,\(0 \le a, b, g, k \le 100\)。
对于 \(100\%\) 的数据,有 \(0 \le n \le 10^4\), \(0 \le a, b, g, k \le {10}^5\)。

我们不要那么复杂,不管 \(a, b, g, k\),简化一下:

【数据范围】
对于 \(30\%\) 的数据,有 \(n \le 2\)。
对于 \(100\%\) 的数据,有 \(0 \le n \le 10^4\)

这里有对 \(n\) 的限制,而是有数据点决定的,所以我们以数据点编号为参数,写出 datamaker.cpp

#include<bits/stdc++.h>
#define to_int(x) atoi(x)
#define to_ll(x) atoll(x)
#define shuf(st,ed) shuffle(st,ed,default_random_engine(rnd()))
using namespace std;
typedef long long ll;
mt19937_64 rnd(time(0));
ll rand(ll l,ll r){return rnd()%(r-l+1)+l;} 
int t,n;
int main(int argc,char *argv[]){
	t=to_int(argv[1]);
	n=(t<=3?rand(1,2):rand(200,1000)); // 200 是随便设的下限
	return 0;
}

这样就写好了,具体在 generator.cpp 中就创建一个参数,参数值直接赋为数据点编号即可,交互部分参照上文

当然,这只是一个比较粗略的实现,你当然可以通过别的参数设置判断方式来更加精细的配置。直接以 \(n\) 的范围为参数也可以,你甚至可以限制一个 \(n\),再让它波动一下,比如 \(n=[n-\sqrt{n},\min(n+\sqrt{n},20)]\)

尾声

那希望这篇文章对大家有帮助,你也可以继续改进我的板子,还可以私信跟我交流。

至于这篇文章的后继:科技·工程:造数据 - 下,肯定不会咕着的,已经在写了哦!

\(\bf{完结撒}\color{pink}{\bf{花}}\color{black}{\bf{!}}\)

声明

本作品采用 CC BY-SA 4.0 进行许可,附加条款亦可使用。-

标签:cout,int,ll,long,学习,参数,日常,cpp,数据
From: https://www.cnblogs.com/godmoo/p/18349770

相关文章

  • 轮换挑选图片,补充 es6的对象写法,uniapp使用,class和style,条件渲染,列表渲染,input
    Ⅰ轮换挑选图片【一】方式一<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><scriptsrc="./js2/vue.js"></script></head><body>......
  • 科技·工程:造数据 - 下
    前言好吧,咕了很久的这篇文章的后半部分终于来啦!上一篇文章链接:科技·工程:构建造数据神器-上在上一篇文章中,我们写好了一个批处理的数据生成器,而在这篇文章中,你将了解到常见的一些数据都是怎么造出来的。常见数据有哪些常见的要造的数据呢?无非就是树啊、图啊、多测啊等等,我......
  • Redis学习笔记_1_基本安装与使用
    Redis入门篇1初识RedisRedis是一种键值型的NoSql数据库键值型:指Redis中存储的数据都是以key、value对的形式存储,而value的形式多种多样,可以是字符串、数值、甚至jsonNoSql:相对于传统关系型数据库而言,有较大差异1.1认识NoSQLNoSql可以翻译做NotOnlySql(不仅仅是SQL......
  • 基于YOLOv10深度学习的交通信号灯检测识别系统【python源码+Pyqt5界面+数据集+训练代
    《博主简介》小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~......
  • 数据库系统 第3节 关系模型的基本概念
    关系模型的基本概念让我们通过一个具体的例子来详细阐述关系模型的基本概念。假设我们需要为一家小型图书销售网站设计一个简单的关系数据库模型。1.实体和属性首先,我们需要识别出几个主要的实体(也就是现实世界中的对象)以及这些实体具有的属性。书籍(Books)书号(Bo......
  • 用Python简单操作MySQL!轻松实现数据读写
    PyMySQL是Python编程语言中的一个第三方模块,它可以让Python程序连接到MySQL数据库并进行数据操作。它的使用非常简单,只需要安装PyMySQL模块,然后按照一定的步骤连接到MySQL数据库即可。本文将介绍PyMySQL的安装、连接MySQL数据库、创建表、插入数据、查询数据、更新数据和删除数据......
  • bitset 学习笔记
    bitset有点厉害,必须要学了。介绍bitset可以看成是一个每个位置都是\(0\)或\(1\)的bool数组。与bool数组相比,它的空间复杂度是其\(\frac{1}{32}\),时间复杂度也是\(\frac{1}{32}\),还支持位运算,所以不论是用处还是效率基本薄纱了bool数组。可以作为卡常、压位操作、......
  • 小白学习微信小程序的跨页面通信和数据传递
    跨页面通信和数据传递在微信小程序开发中非常重要,它们可以帮助不同页面之间共享数据并实现页面间的交互。本文将详细介绍微信小程序中的跨页面通信和数据传递的相关知识,并提供代码案例进行说明。小程序中跨页面通信的方式主要包括:使用全局变量使用事件总线使用页面栈数据传......
  • CH07_数据绑定
    第7章:数据绑定本章目标理解路由事件掌握键盘输入事件掌握鼠标输入事件掌握多点触控输入事件数据绑定概述什么是数据绑定​将WPF中的至少一个带有依赖项属性的两个对象的两个属性进行绑定,使某一个依赖项属性可以更新和它绑定的属性的功能。​数据绑定涉及两......
  • 大数据技术之Hadoop(YARN)
    文章目录一、YARN介绍二、YARN功能说明三、YARN3大组件1.ResourceManager(RM)2.NodeManager(NM)3.ApplicationMaster(AM)四、MR提交YARN交互流程五、YARN资源调度器Scheduler1.调度器策略(1)FIFOScheduler(先进先出调度器)(2)CapacityScheduler(容量调度器)(3)FairScheduler(公平调度......