C++ 由快排学习到的的随机数等知识
- 起:
力扣的912题 数组排序 ,想着先用快速排序来写写,在实际用c++编写的时候,有一些之前没注意到的细节问题造成了一些麻烦。
- 快排思想
每次以数组最后一个数为基准,按照波兰国旗问题对数组进行分层(partition)。假设最后一个数为P,则将数组分为小于P、等于P、大于P的3层。之后对小于P和大于P的层进行此过程的迭代。迭代完成后,目标数组即排列完成。
- 问题:最坏的结果的O(n^2),因为每次最后一个数当成分层基准,最坏的情况是左右两层极度不平衡
- 改进:引入随机数,每次进行分层之前,随机将数组前面的一个数与最后一个数P进行swap,这样分层就成了一个随机事件。在数学证明中,可证明时间复杂度收敛于0(N*LogN)。
- C++中的随机数
在c++中,获取随机数一般广为人知的方法是 rand() ,但是这种方法获得的随机数是伪随机数,其原理是从一个已经确定了的序列中按顺序返回整数。
在直接使用 rand()时,默认的 srand(1)。srand可以认为是种子。
只使用rand()时
int main() {
for (int i = 0; i < 10; i++) {
cout << rand() << "\t";
}
return 0;
}
输出结果(因电脑而异):
41 18467 6334 26500 19169 15724 11478 29358 26962 24464
你每次运行,答案和该结果都一致,这是因为种子没变,永远输出的是该序列前十个数字。
所以有一个思路就是改变种子,想让每次运行的种子发生变化,那么就想到了时间,时间是每秒都在变化的,可以让时间来充当种子参数
int main() {
srand((int)time(NULL)); // #include<ctime> for time()
for (int i = 0; i < 10; i++) {
cout << rand() << "\t";
}
return 0;
}
输出结果:
31937 9951 11817 1295 252 30339 22649 12202 9420 16246
与之前结果不同了。似乎这达到了我们获取真随机数的目的。但是依旧有一个问题,那就是time 是以秒为单位的,如果你的项目要在一秒之内调用多次随机数呢?这样一来,种子在这一秒之内是不会发生变化的。
int getrd_1() {
srand((int)time(NULL)); // #include<ctime> for time()
return rand();
} int main() {
for (int i = 0; i < 10; i++) {
cout << getrd_1() << "\t";
}
return 0;
}
输出结果:
32753 32753 32753 32753 32753 32753 32753 32753 32753 32753
果然是一样的,因为这十次调用都是在1秒之内。
这种情况下,可以使用random_device 来生成真随机数
int getrd(const int &min, const int&max) {//范围 [min,max)
std::random_device rd; //#include<random> for std::random_device
return min + rd() % max;
}
int main() {
for (int i = 0; i < 10; i++)
std::cout << getrd(0, 10) << "\t";
return 0;
}
输出结果:
3 0 0 9 7 8 5 4 9 2
达到了我们预期的要求。
但是,需要注意,这个实现要看你的库支持不支持,如果不支持,将继续使用伪随机数发生器。可以通过调用random_device的成员函数 entropy()来查看熵,如果是伪随机发生器,熵恒为零
- swap
//通过一个临时变量来储存b的值,完成交换
void swap(int &a, int &b) {
int tmp(b);
b = a;
a = tmp;
}
//通过异或^来完成交换
void swap(int &a, int &b) {
if (a == b)
return;
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
第一种交换司空见惯了,第二种则用到了位操作 异或 ^ 的性质:
a ^ 0 等于 a a ^ a 等于 0
满足结合律,交换律
因此:
第一句:a = a ^ b ; 第二句:b = a ^ b,此时 b 等于 a^b^b,结合性质 结果是 a ; 第三句 : a = a ^ b ,此时 a等于 a ^ b ^ a,结果是b ,交换完成
需要注意的是,如果a 与 b 的地址相同 则 a^b 等于0。
最后贴上我的快排:
class Solution {
private:
void swap(int& a, int& b) {
if (a == b)
return;
a = a ^ b;
b = a ^ b;
a = a ^ b;
}
int getrd(const int &min,const int& max) {
random_device rd;
return min + rd() % (max - min);
}
public:
//快排
int* Mypartition(vector<int>& nums, int L, int R) {
int less(L - 1), more(R);
int i(L);
while (i < more) {
if (nums[i] < nums[R]) {
swap(nums[++less], nums[i++]);
}
else if (nums[i] > nums[R]) {
swap(nums[i], nums[--more]);
}
else
i++;
}
swap(nums[more], nums[R]);
return new int [2]{ less, more+1 };
}
void process(vector<int>& nums, int L, int R) {
if (L < R) {
// cout<<"L ="<<L<<"\t R="<<R<<endl;
swap(nums[getrd(L,R)], nums[R]);
int *p = Mypartition(nums,L,R);
process(nums, L, p[0]);
process(nums, p[1], R);
}
}
vector<int> sortArray(vector<int>& nums) {
process(nums, 0, nums.size()-1);
return nums;
}
};
C++ 由快排学习到的的随机数等知识的更多相关文章
- C++学习(三十八)(C语言部分)之 排序(冒泡 选择 插入 快排)
算法是解决一类问题的方法排序算法 根据元素大小关系排序 从小到大 从大到小冒泡 选择 插入 快排希尔排序 归并排序 堆排序 冒泡排序 从头到尾比较 每一轮将最大的数沉底 或者最小数字上浮 选择排序 1 ...
- Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等
本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...
- poj 2804 字典 (特里 要么 快排+二分法)
2804:词典 总时间限制: 3000ms 内存限制: 65536kB 描写叙述 你旅游到了一个国外的城市.那里的人们说的外国语言你不能理解.只是幸运的是,你有一本词典能够帮助你. 输入 首先输 ...
- 排序 之 快排、归并、插入 - <时间复杂度>----掌握思想和过程
俗话说:天下武功无坚不破,唯快不破.对于算法当然也是要使用时间最短.占用空间最小的算法来实现了. 注意:我代码里面打的备注仅供参考,建议不要背模板(因为没有固定的模板),可以写一个数列按着代码跑两圈或 ...
- <泛> 多路快排
今天写一个多路快排函数模板,与STL容器兼容的. 我们默认为升序排序 因为,STL容器均为逾尾容器,所以我们这里采用的参数也是逾尾的参数 一.二路快排 基本思路 给你一个序列,先选择一个数作为基数,我 ...
- 【C语言编程入门笔记】排序算法之快速排序,一文轻松掌握快排!
排序算法一直是c语言重点,各个算法适应不用的环境,同时,在面试时,排序算法也是经常被问到的.今天我们介绍下快速排序,简称就是快排. 1.快速排序思想: 快排使用 分治法 (Divide and con ...
- Java基础进阶:APi使用,Math,Arrarys,Objects工具类,自动拆装箱,字符串与基本数据类型互转,递归算法源码,冒泡排序源码实现,快排实现源码,附重难点,代码实现源码,课堂笔记,课后扩展及答案
要点摘要 Math: 类中么有构造方法,内部方法是静态的,可以直接类名.方式调用 常用: Math.abs(int a):返回参数绝对值 Math.ceil(double a):返回大于或等于参数的最 ...
- 【PHP数据结构】交换排序:冒泡、快排
上篇文章中我们好好地学习了一下插入类相关的两个排序,不过,和交换类的排序对比的话,它们真的只是弟弟.甚至可以说,在所有的排序算法中,最出名的两个排序都在今天要介绍的交换排序中了.不管是冒泡.还是快排, ...
- F#之旅4 - 小实践之快排
参考文章:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-quicksort.html F#之旅4 - 小 ...
随机推荐
- OpenSSF安全计划:SBOM将驱动软件供应链安全
在 软件成分分析(SCA)一文中,我们简单提到软件物料清单(SBOM)在安全实践中的价值. 本期文章将带你深入了解 "SBOM 无处不在"计划是什么,以及 SBOM 对未来软件供应 ...
- ssh-免密钥登陆
实现openssh免密钥登陆(公私钥验证) 在主机A上,通过root用户,使用ssh-keygen生成的两个密钥:id_rsa和id_rsa.pub 私钥(id_rsa)保存在本地主机,公钥(id_r ...
- ShardingSphere-proxy-5.0.0企业级分库分表、读写分离、负载均衡、雪花算法、取模算法整合(八)
一.简要说明 以下配置实现了: 1.分库分表 2.每一个分库的读写分离 3.读库负载均衡算法 4.雪花算法,生成唯一id 5.字段取模 二.配置项 # # Licensed to the Apache ...
- 基于UniApp社区论坛多端开发实战
什么是移动端WebApp 移动端WebApp: 泛指手持设备移动端的web 特点: - 类App 应用,运行环境是浏览器 - 可以包一层壳,成为App - 常见的混合应用: ionic, Cordov ...
- mesi--cpu内存一致性协议
目录 cpu缓存一致性问题 mesi协议 mesi协议4种状态,及状态转换 模拟工具演示 cpu缓存一致性问题 一个服务器中有多个核,每个核中有多个cpu,每个cpu有多个线程.缓存最少分为3级,1级 ...
- 线程池的概念&原理和线程池的代码实现
线程池:一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作, 无需反复创建线程而消耗过多资源.工作原理:可以用一张图来简洁明了说明: 合理利用线程池能够带来三个好处∶1.降低 ...
- Etcd 使用场景:通过分布式锁思路实现自动选主
分布式锁?选主? 分布式锁可以保证当有多台实例同时竞争一把锁时,只有一个人会成功,其他的都是失败.诸如共享资源修改.幂等.频控等场景都可以通过分布式锁来实现. 还有一种场景,也可以通过分布式锁来实现, ...
- HBase学习(二) 基本命令 Java api
一.Hbase shell 1.Region信息观察 创建表指定命名空间 在创建表的时候可以选择创建到bigdata17这个namespace中,如何实现呢? 使用这种格式即可:'命名空间名称:表名' ...
- P4289 【一本通提高篇广搜的优化技巧】[HAOI2008]移动玩具
[HAOI2008]移动玩具 题目描述 在一个 4 × 4 4\times4 4×4 的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动时只能将玩具向上下左右四个方 ...
- 虚言妙诀终虚见,面试躬行是致知,Python技术面试策略与技巧实战记录
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_183 2021年,对于正在找工作的朋友来说,笼罩在新冠肺炎疫情之下,今年的就业季显得更加具有挑战性,更有意思的是,每当这个时候,各 ...