首页 > 其他分享 >科技·工程:造数据 - 下

科技·工程:造数据 - 下

时间:2024-08-08 21:16:31浏览次数:7  
标签:std rand 工程 int res mn 科技 随机 数据

前言

好吧,咕了很久的这篇文章的后半部分终于来啦!

上一篇文章链接:科技·工程:构建造数据神器 - 上

在上一篇文章中,我们写好了一个批处理的数据生成器,而在这篇文章中,你将了解到常见的一些数据都是怎么造出来的。

常见数据

有哪些常见的要造的数据呢?无非就是树啊、图啊、多测啊等等,我们今天就先探讨一部分,等我什么时候想到别的了再填坑

随机数

rand 和 srand

这两个函数是 C 中的随机数函数,在头文件 <cstdlib> 下定义,函数原型:

int rand();
void srand( unsigned seed );

rand() 是一个伪随机的函数,返回一个 \([0,RAND\_MAX]\) 中的数。

srand( unsigned seed ) 是用于设置随机种子的,初始的随机种子为 \(1\)。

所以,我们常用 srand(time(0)) 来初始化随机种子,这样就能保证每次运行时随机出来的数不一样。

但是 rand() 有很大的问题,我们来看 cppreference 上的一段话:

……不保证生成的随机数列质量。 过去,一些 rand() 在随机性、分布和产生的序列周期上有严重缺陷(在一个众所周知的例子中,调用之间最低位简单地在 \(1\) 与 \(0\) 间切换)。
对于严肃的随机数生成需求不推荐使用 rand()。推荐用 C++11 的随机数生成设施替换 rand()。 (C++11 起)

由这段文字我们知道,rand() 生成的随机数的质量并不高,日常用用还行,造数据时就应该另寻他法了。

mt19937

这是 C++11 中的一个随机数引擎,也是目前 OI 中常见的生成随机数的工具。

这个随机数引擎采用梅森旋转算法,周期长度为可达 \(2^{19937}-1\),这也是它名字的由来。

它的值域大概是整个 \(\text{int}\) 的范围,而且它还有 \(64\) 位的版本——mt19937_64

我们在定义时就设置好随机种子,同样用时间作为随机种子:

std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

然后调用时用 rnd() 即可。

我们接下来用的也是这个随机数的引擎。

同时,我们还可以写一个在 \([l,r]\) 之间生成随机数的函数:

ll rand(ll l, ll r) {
    return rnd() % (r - l + 1) + l;
}

打乱

打乱显然可以用 <algorithm> 下的 shuffle 函数,此函数的使用请自行参阅其他语法资料:

template<class RandomIt>
void shuffle(RandomIt first, RandomIt last) {
    std::default_random_engine generator(rnd());
    std::shuffle(first, last, generator);
}

template<class RandomIt>
void shuffle(RandomIt* first, RandomIt* last) {
    std::default_random_engine generator(rnd());
    std::shuffle(first, last, generator);
}

注意到原生指针并不是 class template,所以我们需要对原生指针的情况进行偏特化。

多测

多测的题是很常见的,在多测的题中,通常会给定部分参数的 \(\footnotesize\sum\) 作为数据范围,那么我们就需要【随机出 \(n\) 个和为 \(s\) 的数】。

另外,这些数往往还会有下界,这个下界一般都是一个较小的常数,常见的应该就是 \(1\) 到 \(3\)。

所以问题转变为【随机出 \(n\) 个和为 \(s\) 的数,每个数至少为 \(mn\)】。

容易想到随机出前缀和再做一次差分即可,\(mn\) 的限制等价于【随机出来的两个前缀和的差至少为 \(mn\)】,于是 std::set 判重即可。

std::vector<ll> split(ll n, ll s, ll mn = 1) {
    assert(n > 0 && s >= n * mn);
    std::vector<ll> res;
    std::set<ll> st;
    while ((ll) res.size() < n) {
        assert((ll) st.size() < s - mn + 1);
        int tmp = st.empty() ? s : rand(mn, s);
        if (st.insert(tmp).second) {
            res.push_back(tmp);
            for (ll i = 1; i < mn; i++) {
                if(tmp - i >= mn) st.insert(tmp - i);
                if(tmp + i <= s) st.insert(tmp + i);
            }
        }
    }
    std::sort(res.begin(), res.end());
    for (ll i = res.size() - 1; i >= 1; i--) res[i] -= res[i - 1];
    return res;
}

注意,这份代码要求 \(mn\gt1\),不然就无法造出为 \(0\) 的数(被 std::set 判掉了),如有需要,自行改代码/手写。

还有,这份代码是纯靠随机的。也就是说,如果只剩一个可选的数,我们认为要 \(O(s)\) 次才能选到这个数,于是复杂度 \(O(s\log s)\),数据范围太大时完全承受不起。但是我们说过,\(mn\) 一般是一个小常数,况且 \(n\) 的数量级应该会比 \(s\) 小很多(不然每个数就太小了),所以这样子的情况极低,如果真的有需要每个数都要很小那就自己另外写吧。

随机的树怎么造呢?容易想到 \(\text{prufer}\) 序列:先随机出 \(\text{prufer}\) 序列,再 \(\text{prufer to tree}\) 即可,期望深度 \(O(\log n)\),如果需求更复杂可以自行研究/使用别的工具,比如 ouuan 的 \(\text{Tree Generator}\)

std::vector<std::pair<int, int>> tree(int n) {
    assert(n > 0);
    std::vector<std::pair<int, int>> res;
    if (n == 1) return res;
    std::vector<int> prufer(n - 2);
    for (auto &x : prufer) x = rand(1, n);
    std::vector<int> deg(n + 1, 1);
    for (int x : prufer) ++deg[x];
    int cur = 0;
    while (deg[++cur] != 1);
    int leaf = cur;
    for (int x : prufer) {
        res.push_back({x, leaf});
        if (--deg[x] == 1 && x < cur) leaf = x;
        else {
            while (deg[++cur] != 1);
            leaf = cur;
        }
    }
    res.push_back({n, leaf});
    return res;
}

只说连通图,其他自己造。

树上随机加边即可,用 std::set 判掉重边。

可以加个小优化,就是当 \(m>\frac{n(n-1)}{2}-(n-1)\) 时,这个时候图非常稠密,随机复杂度很高,建出完全图,接着随机删边即可。(但这样就意味着 \(n\) 很小,好像问题也不大)。

std::vector<std::pair<int, int>> graph(int n, ll m) {
    assert(n > 0);
    assert(m >= n - 1 && m <= n * (n - 1) / 2);
    if(m > n * (n - 1) / 2 - (n - 1)){
    	std::vector<std::pair<int, int>> res;
    	for(int u = 1; u <= n; u++){
    		for(int v = u + 1; v <= n; v++){
    			res.push_back({u, v});
			}
		}
		shuffle(res.begin(),res.end());
		for(int i = m + 1; i <= n * (n - 1) / 2; i++) res.pop_back();
		return res;
	}
    auto res = tree(n);
    std::set<std::pair<int, int>> st;
    for (const auto &e : res) {
        st.insert(e);
        st.insert({e.second, e.first});
    }
    m -= (n - 1);
    while (m--) {
        generate_edge:;
        int u = rand(1, n), v = u;
        while (u == v) v = rand(1, n);
        if (!st.insert({u, v}).second || !st.insert({v, u}).second) goto generate_edge;
        res.push_back({u, v});
    }
    return res;
}

声明

本作品除代码块内的内容以外,其他内容均采用 CC BY-SA 4.0 进行许可,附加条款亦可使用。

本作品中所有代码均受 MIT License 保护,版权声明如下:

MIT License

Copyright (c) 2024 godmoo

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

若要获取完整代码,请访问:Hint

标签:std,rand,工程,int,res,mn,科技,随机,数据
From: https://www.cnblogs.com/godmoo/p/18349767

相关文章

  • 基于YOLOv10深度学习的交通信号灯检测识别系统【python源码+Pyqt5界面+数据集+训练代
    《博主简介》小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~......
  • STM32CubleMX创建FreeRtos工程教程,图文教程
        前言:STM32CubeMX是一个开发工具,它已经将FreeRTOS这个实时操作系统(RTOS)集成到其工具中。换句话说,通过STM32CubeMX,可以非常方便地为STM32微控制器生成配置代码,其中包括对FreeRTOS的支持。    而本篇就是使用STM32CubleMX,生成支持FreeRtos的图文教程......
  • 数据库系统 第3节 关系模型的基本概念
    关系模型的基本概念让我们通过一个具体的例子来详细阐述关系模型的基本概念。假设我们需要为一家小型图书销售网站设计一个简单的关系数据库模型。1.实体和属性首先,我们需要识别出几个主要的实体(也就是现实世界中的对象)以及这些实体具有的属性。书籍(Books)书号(Bo......
  • 用Python简单操作MySQL!轻松实现数据读写
    PyMySQL是Python编程语言中的一个第三方模块,它可以让Python程序连接到MySQL数据库并进行数据操作。它的使用非常简单,只需要安装PyMySQL模块,然后按照一定的步骤连接到MySQL数据库即可。本文将介绍PyMySQL的安装、连接MySQL数据库、创建表、插入数据、查询数据、更新数据和删除数据......
  • 【招聘】前端软件工程师、高级前端软件工程师
    最近我们公司在招聘【前端软件工程师】和【高级前端软件工程师】两个职位,坐标上海浦东张江,有兴趣的读者欢迎扫描下方二维码了解详细信息。我们公司名为【瑞孚迪生物医学(上海)有限公司】,所招两个职位来自于公司的SignalsR&D部门,Signals是一款面向药物研发和化工领域的SaaS软件套......
  • 小白学习微信小程序的跨页面通信和数据传递
    跨页面通信和数据传递在微信小程序开发中非常重要,它们可以帮助不同页面之间共享数据并实现页面间的交互。本文将详细介绍微信小程序中的跨页面通信和数据传递的相关知识,并提供代码案例进行说明。小程序中跨页面通信的方式主要包括:使用全局变量使用事件总线使用页面栈数据传......
  • 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(公平调度......
  • 【探索数据结构与算法】——深入了解双向链表(图文详解)
    目录一、双向链表的基本概念 ​​​二、双向链表的结构三、双向链表的基本操作实现方法 1.双向链表的初始化2.双向链表的头插3.双向链表的尾插6.查找节点7.在指定位置之前插入节点8.删除指定位置节点9.打印链表数据  10.双向链表销毁四、完整代码实现 LIst.h......
  • 深入理解淘客返利系统中的数据加密与隐私保护策略
    深入理解淘客返利系统中的数据加密与隐私保护策略大家好,我是微赚淘客返利系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代互联网环境中,数据加密与隐私保护已经成为开发者必须重视的重要问题。本文将深入探讨淘客返利系统中的数据加密与隐私保护策略,并通过Java代......