• 「笔记」如何优雅的造数据


    可以配合如何快速的写出对拍程序食用qwq。

    下面讲的方法本着简单快捷的原则,更多是为了方便大家在比赛中快速造出数据进行对拍。也是为了方便我随手取用。

    如果有什么更牛逼的造数据方法,也可以跟我说/kel,欢迎补充。

    批量数据生成器

    感谢 @斜揽残箫 提供/qq。

    #include<bits/stdc++.h>
    using namespace std;
    namespace rad {
    	mt19937_64 R(time(0));
    	inline int Rand(int l,int r) {
    		uniform_int_distribution<int> distribution(l,r);
    		return distribution(R);
    	}
    } using namespace rad;
    int main() {
    	string in,out;
    	for(int i = 1;i <= 3;i ++) { // 序号 
    		in.clear();
    		int j = i;
    		while(j) {
    			char m = j % 10 + '0';
    			in = m + in;
    			j /= 10;
    		}
    		out = in;
    		in += ".in";
    		out += ".out";
    		string A = "average"; // 文件名 
    		string B = "average";
    		A = A + in,B = B + out;
    		in = A,out = B;
    		freopen(in.data(),"w",stdout);
    		system("srandom.exe"); // 数据生成器 
    		fclose(stdout);
    		string Str="average.exe < "+ in +" > "+ out; // 你的 std 
    		system(Str.data());
    	}
    }
    

    rand() 和 srand()

    rand() 函数用来产生随机数,但是,rand() 的内部实现是用线性同余法实现的,是伪随机数,由于周期较长,因此在一定范围内可以看成是随机的。

    rand() 会返回一个范围在 0 到 RAND_MAX(一般是32767)之间的伪随机数(整数)。

    srand() 函数则是设置随机数种子,如果没有 srand() 函数,rand() 默认的随机数种子为 \(1\)。随机种子相同,每次产生的随机数也会相同。

    随机一个数

    一般形式为 x = rand()。随机一个数(\(0 \sim 32767\))并存入 \(x\) 内。

    一个 long long 范围的数

    因为 \(0 \sim 32767\) 在二进制下只占用了 \(15\) 位,所以我们可以把四个 rand() 拼起来。

    long long Rand(long long n) { // 限定上界 n,生成一个 [0,n) 范围内的数
        long long x = rand() * (1ll << 45) + rand() * (1ll << 30) + rand() * (1ll << 15) + rand();
        return x % n;
    }
    

    注意这里产生的数的范围只在 \(0 \sim 2^{61}-1\),并不是真正意义上的 long long 范围。不过这一般也够用了。

    一个 [m, n] 范围的数

    把上面的函数稍微修改一下,确定一个下界即可。或者理解为 “在 \(m\) 的基础上在加一个数 \(x\),可加的 \(x\) 的大小为区间长度 \(n-m+1\)”。

    long long Rand(long long n) { // 限定上界 n
        long long x = rand() * (1ll << 45) + rand() * (1ll << 30) + rand() * (1ll << 15) + rand();
        return x % (n - m + 1) + m;
    }
    

    一个小数

    原理:如果你要一个 \(x\) 位小数,你可以先生成一个 \(x\) 位的数,然后除以 \(10^x\)

    double Rand(long long n) { // 限定上界 n = 10^x
        long long x = rand() * (1ll << 45) + rand() * (1ll << 30) + rand() * (1ll << 15) + rand();
        return (double)x / n;
    }
    

    一个序列

    一个长度为 \(n\) 的序列直接输出 \(n\) 个随机数就好了吧。

    随机一个排列

    这里我使用 random_shuffle(a + 1, a + n + 1) 来打乱一个排列。

    不过这个函数好像在 C++ 14 中被弃用了?

    一棵树

    一棵普通的树

    Kruskal

    利用 Kruskal 的思想打造即可。

    int fa[MAXN];
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    void Get_A_Tree(int n) {
        int cnt = 0;
        for(int i = 1; i <= n; ++i) fa[i] = i;
        while(cnt < n - 1) {
            int u = Rand(n) + 1, v = Rand(n) + 1;
            while(find(u) == find(v)) v = Rand(n) + 1;
            fa[find(u)] = find(v);
            printf("%d %d\n", u, v);
            ++ cnt;
        }
    }
    

    随机排列

    感谢 @KnightL 提供/qq。

    这里提供另外一种简单的方法:

    随机一个排列,然后后面的一个点之和前面的某个点连边。

    在这个方法下,内向树和外向树也同样非常方便构造,只需要在输出时确定连边方向即可。

    int c[MAXN];
    void Get_A_Tree(int n) {
        for(int i = 1; i <= n; ++i) c[i] = i;
        random_shuffle(c + 1, c + n + 1);
        for(int i = 2; i <= n; ++i) {
            printf("%d %d\n", Rand(i - 1) + 1, i); 
        }
    }
    

    一棵外(内)向树

    在上面的一棵普通的数的基础上,需要还通过 dfs 来确立一个方向。

    struct edge { int to, nxt; }e[MAXN << 2];
    int head[MAXN], num_edge = 1;
    int fa[MAXN];
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    void add_edge(int from, int to) { e[++num_edge] = (edge){to, head[from]}, head[from] = num_edge; }
    void dfs(int u, int fa) {
        for(int i = head[u]; i; i = e[i],nxt) {
            int v = e[i].to;
            if(v == fa) continue;
            printf("%d %d\n", u, v); // 外向树
            // printf("%d %d\n", v, u); // 内向树
            dfs(v, u);
        }
    }
    void Get_A_Tree(int n, int m) {
        int cnt = 0;
        for(int i = 1; i <= n; ++i) fa[i] = i;
        while(cnt < n - 1) {
            int u = Rand(n) + 1, v = Rand(n) + 1;
            while(find(u) == find(v)) v = Rand(n) + 1;
            fa[find(u)] = find(v);
            add_edge(u, v), add_edge(v, u);
            ++ cnt;
        }
    }
    

    一张图

    一张无向联通图

    先随机生成一棵树,然后在添加边即可。

    避免重边

    一个暴力的方法,使用 map 判重。

    需要注意的是效率会比较低。

    避免自环

    随机一条边的时候注意保持 u != v 即可。

    一个 DAG (有向无环图)

    你可以考虑随机一个拓扑序。

    如果要保证图联通的话还是先随机一棵树,然后在随机其他边,否则直接随机若干条边就可以。

    注意输出边 \((u,v)\) 的时候你要保证 topo[u] < topo[v],即总是拓扑序前面的点向后面的点连边。

  • 相关阅读:
    JS系列:三元运算符与循环
    浏览器解析js和type判断数据类型
    JS系列:数据类型详细讲解
    JS系列:编程语言
    京东校招面试汇总
    有关axios的request与response拦截
    正则表达式 判断内容是否为合法的url
    H5 小代码(实时更新)
    H5 回到顶部按钮
    图片压缩(js压缩,底部有vue压缩图片依赖使用的教程链接)
  • 原文地址:https://www.cnblogs.com/Silymtics/p/15527932.html
Copyright © 2020-2023  润新知