常用数据生成器

所有代码均可在 -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;
}

参考资料:

https://oi-wiki.org/string/hash/#卡自然溢出-hash

https://www.luogu.com.cn/article/og23mn1j

posted @ 2025-09-22 17:56  headless_piston  阅读(26)  评论(1)    收藏  举报