常用数据生成器
所有代码均可在 -std=c++17 下编译。
所有代码均经过检查,具体可见这里。
图论
树
期望高度对数
期望高度 \(O(\log n)\):
/**
* 生成期望树高 O(logn) 级别的树
* 生成方法:钦定 1 为根,对于后续的节点 i,随机在 [1,i-1] 中选取一个点作为父亲
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<vector>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
int rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e7+10;
int n,p[N];
struct edge{int u,v;};
vector<edge> e;
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(edge &x:e){
x.u=p[x.u];
x.v=p[x.v];
}
shuffle(e.begin(),e.end(),getrnd);
}
int main(){
ofstream out("1.in");
n=1e5;//节点数
out<<n<<'\n';
for(int i=2;i<=n;i++)
e.push_back({i,rnd(1,i-1)});
shuf();//随机打乱
for(edge &x:e)
out<<x.u<<' '<<x.v<<'\n';
return 0;
}
/**
* 对于 n=1e5:
* --以1为根:
* ----调用 shuf,平均树高在 20 左右浮动
* ----不调用 shuf,平均树高在 12 左右浮动
* --随机选根:
* ----调用 shuf,平均树高在 22 左右浮动且方差较大
* ----不调用 shuf,平均树高在 20 左右浮动且方差较大
*/
期望高度根号
期望高度 \(O(\sqrt n)\):
/**
* 生成期望树高 O(sqrtn) 级别的树
* 生成方法:随机生成一个 Prüfer 序列,根据此构造一棵树
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<queue>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
int rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e7+10;
int n,p[N],deg[N];
struct edge{int u,v;};
vector<int> prufer;
vector<edge> e;
priority_queue<int,vector<int>,greater<int>> leaf;
void prufer_decoding(){
for(int i=1;i<=n;i++) deg[i]=1;
for(int &v:prufer) ++deg[v];
for(int i=1;i<=n;i++) if(deg[i]==1) leaf.push(i);
for(int &v:prufer){
int u=leaf.top();
leaf.pop();
e.push_back({u,v});
--deg[u],--deg[v];
if(deg[v]==1) leaf.push(v);
}
vector<int> remain;
for(int i=1;i<=n;i++) if(deg[i]==1) remain.push_back(i);
e.push_back({remain[0],remain[1]});
}
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(edge &x:e){
x.u=p[x.u];
x.v=p[x.v];
}
}
int main(){
ofstream out("1.in");
n=1e5;//节点数
out<<n<<'\n';
for(int i=1;i<=n-2;i++) prufer.push_back(rnd(1,n));//随机生成 Prüfer 序列
prufer_decoding();//根据 Prüfer 序列还原树
// shuf();//随机打乱
for(edge &x:e)
out<<x.u<<' '<<x.v<<'\n';
return 0;
}
/**
* n=1e5:
* --若不调用 shuf,平均高度在 380~500 之间浮动,有时会有下至 200,上至 700 的极端情况
*/
菊花
/**
* 生成一个菊花
* 生成方法:钦定 1 为根,对于后续的节点 i,令 1 作为它的父亲
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<vector>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
int rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e7+10;
int n,p[N];
struct edge{int u,v;};
vector<edge> e;
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(edge &x:e){
x.u=p[x.u];
x.v=p[x.v];
if(rnd(0,1)) swap(x.u,x.v);
}
shuffle(e.begin(),e.end(),getrnd);
}
int main(){
ofstream out("1.in");
n=1e5;//节点数
out<<n<<'\n';
for(int i=2;i<=n;i++)
e.push_back({i,1});
shuf();//随机打乱
for(edge &x:e)
out<<x.u<<' '<<x.v<<'\n';
return 0;
}
链
/**
* 生成一条链
* 生成方法:钦定 1 为根,对于后续的节点 i,令 i-1 作为它的父亲
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<vector>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
int rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e7+10;
int n,p[N];
struct edge{int u,v;};
vector<edge> e;
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(edge &x:e){
x.u=p[x.u];
x.v=p[x.v];
}
shuffle(e.begin(),e.end(),getrnd);
}
int main(){
ofstream out("1.in");
n=1e5;//节点数
out<<n<<'\n';
for(int i=2;i<=n;i++)
e.push_back({i,i-1});
shuf();//随机打乱
for(edge &x:e)
out<<x.u<<' '<<x.v<<'\n';
return 0;
}
DAG
/**
* 生成随机有向无环图
* 生成方法:随机生成 m 条边 (u,v),保证 u<v,再随机打乱
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<fstream>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
ll rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e6+10;
int n,m,p[N];
struct edge{
int u,v;
bool operator<(const edge &x)const{
return u==x.u?v<x.v:u<x.u;
}
}e[N];
set<edge> st;
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(int i=1;i<=m;i++){
e[i].u=p[e[i].u];
e[i].v=p[e[i].v];
}
}
int main(){
ofstream out("1.in");
n=1e4;//节点数
m=2e5;//边数
out<<n<<' '<<m<<'\n';
for(int i=1,u,v;i<=m;i++){
while(1){
u=rnd(1,n),v=rnd(1,n);
if(u==v) continue;
if(u>v) swap(u,v);
if(st.find({u,v})==st.end()) break;
}
st.insert({u,v});
e[i]={u,v};
}
shuf();
for(int i=1;i<=m;i++)
out<<e[i].u<<' '<<e[i].v<<'\n';
return 0;
}
连通图
/**
* 生成随机连通图(弱连通,无重边、自环,不可用于生成稠密图)
* 生成方法:随机生成一棵树,然后在树上随机加边
* 打乱方法:对所有点重新随机编号
*/
#include<random>
#include<iostream>
#include<algorithm>
#include<fstream>
#include<vector>
#include<set>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
int rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
constexpr int N=1e7+10;
int n,m,p[N];
struct edge{
int u,v;
bool operator<(const edge &x)const{
return u==x.u?v<x.v:u<x.u;
}
};
vector<edge> e;
set<edge> st;
void shuf(){
for(int i=1;i<=n;i++) p[i]=i;
shuffle(p+1,p+1+n,getrnd);
for(edge &x:e){
x.u=p[x.u];
x.v=p[x.v];
}
shuffle(e.begin(),e.end(),getrnd);
}
int main(){
ofstream out("1.in");
n=1e5;//节点数
m=2e5;//边数
out<<n<<' '<<m<<'\n';
for(int i=2;i<=n;i++){
st.insert({i,rnd(1,i-1)});
e.push_back({i,rnd(1,i-1)});
}
for(int i=n;i<=m;i++){
int u,v;
while(1){
u=rnd(1,n),v=rnd(1,n);
if(u==v) continue;
if(u>v) swap(u,v);
if(st.insert({u,v}).second) break;
}
if(rnd(0,1)) swap(u,v);
e.push_back({u,v});
}
shuf();//随机打乱
for(edge &x:e)
out<<x.u<<' '<<x.v<<'\n';
return 0;
}
想要稠密图的话只需生成按以上方式生成的图的反图即可。
字符串
卡大模数哈希
根据生日悖论的一种粗略的估计:设模数为 \(p\),若哈希值分布足够均匀,大约 \(\sqrt p\) 个字符串就会产生冲突。
故大约生成 \(\sqrt p\) 个随机串就会产生冲突。
卡自然溢出哈希
/**
* 生成两个不同的串,满足其自然溢出哈希值相同
* 生成方法:
* 对于 base 为偶数的情况,利用 base 幂次较高时与 2^64 同余构造
* 对于 base 为奇数的情况,利用 Thue Morse 序列构造
*/
#include<iostream>
#include<random>
#include<algorithm>
#include<fstream>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
ll rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
char st[30];
string get_rev(const string &s){//反转字符,a 变 b,b 变 a
string res;
for(char c:s)
if(c=='a') res.push_back('b');
else res.push_back('a');
return res;
}
ofstream out("1.in");
void gen1(){//base 为偶数,需要令 len 大于 64
int len=70;
string s;
for(int i=2;i<=len;i++) s.push_back(rnd('a','z'));
char a,b;
shuffle(st,st+26,getrnd);
a=st[0],b=st[1];
out<<a<<s<<'\n';
out<<b<<s<<'\n';
}
void gen2(){//base 为奇数,使用 Thue Morse 序列
int len=5;//初始串长
int n=15;//最终串长为 len*(2^n) n>12
string s;
for(int i=1;i<=len;i++) s.push_back(rnd('a','b'));
for(int i=1;i<=n;i++){
string rev=get_rev(s);
s+=rev;
}
out<<s<<'\n';
out<<get_rev(s)<<'\n';
}
int main(){
for(int i=0;i<26;i++) st[i]='a'+i;
// gen1();
gen2();
return 0;
}
卡双哈希
/**
* 生成两个不同的串,满足其在给定的 base 和 p 下双哈希值相同
* 生成方法:
* 期望 sqrt p1 个串对于 p1 出现哈希冲突
* 以使得 p1 冲突的两串作为字符集随机生成串使得 p2 产生冲突
*/
#include<random>
#include<iostream>
#include<fstream>
#include<unordered_map>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
ll rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
int p1,p2,base1,base2;
int gethash(const string &s,int base,int p){//根据哈希方式动态调整
ll res=0;
for(char c:s) res=(res*base+c)%p;
return res;
}
unordered_map<int,string> h1,h2;
string rndstr1(int len){
string res;
for(int i=1;i<=len;i++) res.push_back(rnd('a','z'));
return res;
}
string x,y;
string rndstr2(int len){
string res;
for(int i=1;i<=len;i++) res+=rnd(0,1)?x:y;
return res;
}
bool check1(const string &s,string &a,string &b){
int h=gethash(s,base1,p1);
if(h1.count(h)){
a=s,b=h1[h];
if(a==b) return 1;
return 0;
}
h1[h]=s;
return 1;
}
ofstream out("1.in");
bool check2(const string &s){
int h=gethash(s,base2,p2);
if(h2.count(h)){
if(s==h2[h]) return 1;
out<<s<<'\n';
out<<h2[h]<<'\n';
return 0;
}
h2[h]=s;
return 1;
}
int main(){
base1=131;
base2=13331;
p1=1e9+7;
p2=998244353;
while(check1(rndstr1(10),x,y));
while(check2(rndstr2(40)));
return 0;
}
其他
括号序列
/**
* 生成指定长度的合法括号序列
* 按顺序加入,保证合法的前提下随机选取
*/
#include<random>
#include<iostream>
#include<fstream>
#include<cassert>
using namespace std;
typedef long long ll;
mt19937_64 getrnd(__builtin_ia32_rdtsc());
ll rnd(ll l,ll r){return getrnd()%(r-l+1)+l;}
int n,l,r,alpha;
string get_seq(){
string res;
for(int i=1;i<=n;i++){
if(l<n/2&&r<l)
res.push_back(rnd(0,100)<=alpha?'(':')');
else if(l<n/2) res.push_back('(');
else res.push_back(')');
if(res.back()=='(') ++l;
else ++r;
}
return res;
}
int main(){
ofstream out("1.in");
n=1e6;//生成的括号序列长度为 n
alpha=70;//当加入左右括号都合法时选择左括号的概率(百分数)
//alpha 值的增大能有效提升嵌套层数
assert(n%2==0);
assert(alpha>=0&&alpha<=100);
out<<get_seq()<<'\n';
return 0;
}
参考资料:

浙公网安备 33010602011771号