0.目录
- 1.随机算法
- 2.随机数发生器
- 3.随机算法的应用
3.1 跳跃表
3.1-1 跳跃表引申——1-2-3确定性跳跃表
3.2 素性测试
3.3 treap树
相关总结参见http://www.jianshu.com/p/60ea83ea17cc
1.随机算法
一个随机算法的最坏运行时间几乎总是和非随机化算法的最坏情形运行时间相同。重要的区别在于,好的随机化算法没有不好的输入,而只有坏的随机数(相对于特定的输入)。
比如说:对于快速排序,方法A用第一个元素作为枢纽元,方法B使用随机选出的元素作为枢纽元。
- 两种情形下,最坏情形运行时间都是Θ(n²)
- 存在特定的输入总能够出现在A中并产生不好的运行时间
- 如果B以相同的输入运行两次,那么它将有两个不同的运行时间,这依赖于什么样的随机数发生
通过随机化算法,特定的输入不再重要。重要的是随机数,我们可以得到一个期望的运行时间,此时我们是对所有可能的随机数取平均而不是对所有可能的输入求平均。
2.随机数发生器
随机数有许多已知的统计性质;伪随机数满足这些性质的大部分。
真正需要的是随机数的序列。这些数应该独立地出现。
线性同余数发生器
产生随机数的最简单的方法是线性同余数发生器,由Lehmer于1951年提出:
- 种子seed:开始这个序列,给出的x0的某个值。
如果x0=0,这个序列远不是随机的。
但如果A和M选择的正确,那么任何其他的1 <= x0 < M都是同等有效的 - 如果M是素数,那么xi就绝对不会是0
- 在M-1个元素以后,序列将重复。序列的周期是M-1,需要尽可能地大(鸽巢原理)
- 如果M是素数,那么总存在对A的一些选择能够给出整周期M-1。但是对A的有些选择则得不到整周期(周期 < M-1)
-
Lehmer建议M = 2147483647,A = 48271(能够给出整周期)
例如:M = 11,x0 = 1
A = 7: 7, 5, 2, 3, 10, 4, 6, 9, 8, 1, 7 ,5 ,2... (周期为10 = M-1)
A = 5: 5, 3, 4, 9, 1, 5, 3, 4...(周期为5 < M -1)
一个有问题的随机数发生器(返回(0,1)开区间值):乘法溢出
问题:乘法溢出为什么影响伪随机性?
溢出的部分,也即2的32次方(32位机器),不是M的倍数,因此会影响随机性。
工作在32位机器上的随机数发生器
证明:为什么δ(xi)或者是0,或者是1
为什么仅当余项的值小于0时,δ(xi)=1
说明:因为这里是对M取余,所以是M的倍数则自然消掉;并且δ(xi)两项都是取整函数,因此肯定为整数,另外xi+1总是介于1~M-1之间,因此当余项小于0,则取1
3.随机算法的应用
3.1 跳跃表
随机化的第一个用途是以O(lgn)期望时间支持查找和插入的数据结构。
每个2^i 结点就有一个指针指向下一个2^i结点,总的指针个数仅仅是加倍,但现在在一次查找中最多考察floor(lgn)个结点。因此总的时间消耗为O(lgn)。
本质上是二分查找:
放松上面数据结构的结构条件,就构成了跳跃表:
- k阶结点:带有k个指针的结点
任意k阶上的第i阶(i <= k)指针指向的下一个结点至少具有i阶。(保留该性质) - 去掉另外一个限制:第i个指针指向前面第2^i个结点
-
新插入的结点怎么确定结点的阶
大约有一半的结点是1阶;大约1/4的结点是2阶;一般的,大约1/2^i的结点是i阶结点。
按照这个概率分布随机选择结点的阶数。最容易的方法是抛一枚硬币直到正面出现并把抛硬币的总次数作为该节点的阶数。
- 跳跃表类似于散列表,需要估计表中的元素个数(从而阶的个数可以确定)
- 粗略分析,由于没有在元算法上改变每一阶的结点的期望个数,因此预计穿越该同阶的结点的总的工作量是不变的。O(lgN)
3.1.1 查找
如果我们想查找19是否存在?如何查找呢?我们从头结点开始,首先和9进行判断,此时大于9,然后和21进行判断,小于21,此时这个值肯定在9结点和21结点之间,此时,我们和17进行判断,大于17,然后和21进行判断,小于21,此时肯定在17结点和21结点之间,此时和19进行判断,找到了。
3.1.2 插入
3.1-1 跳跃表引申——1-2-3确定性跳跃表
3.1-1.1 定义
- 链接:如果至少存在一个指针从一个元素指向另一个元素,则称两个元素是链接的
- 间隙容量:两个在高度为h链接的元素间的间隙容量等于他们之间高度为h-1的元素个数
- 1-2-3确定性跳跃表:每一个间隙(除在头和尾之间可能的零间隙外)的容量为1、2、3。
因此,当我们沿任一层行进仅仅通过常数个指针后就可以下降到低一层。则在最坏的情形下查找的时间是O(lgN)。
3.1-1.2 与2-3-4树之间的关系
-
从下图可以看出,1-2确定性跳跃表与2-3树本质上是一样的
1-2-3确定性跳跃表和2-3-4树本质是一样的
3.1-1.3 1-2-3确定性跳跃表的实现以及操作
1)实现
- (直观的数组实现不可靠)当将高h的结点提升到高h+1,将花费O(h)用于将h个指针拷贝到一个新数组。而插入需要从最高依次搜索到高度为1,因此插入的时间界成为O(lg²N)。
- (直观的链表实现)另一种直观的方法是用一个链表表示高度为h的结点中的h个前向指针。由于我们是沿着各层向下进行,因此一个结点的链表是以第h层前向指针开始,以第一层前向指针结束。
- (真实的实现:另一种链表实现方法)如果一个结点在抽象表示中的高度为h,那么它的项在实际实现中就会出现在h个地方
实际实现
2)空间大小耗费
- 跳跃表最坏情况为2N: N + N*1/2 + N * 1/4 + N * 1/8 + ... + 1 = 2N
平均使用大约1.57N个结点
每个结点包含两个指针和一项(在32位的某些系统上,占12个字节,耗费16个字节,4个字节作为系统开销) - 红黑树使用N个结点,每个结点包含两个指针、一项以及一个颜色位(在32位的某些系统上,耗费13个字节(13+4>16),但是需要一个32字节块)
3)查找
1-2-3跳跃表的实现特点:
在同一层级的链表(不超过3个结点)中找到首个大于item的结点,然后向下移一层。
4)插入
必须保证当一个高度为h的新结点加入进来时不会产生具有四个高度为h的结点的间隙。
插入采用2-3-4树的自顶向下的方法(保证当前结点不是4-结点):(参考http://www.jianshu.com/p/8258ed821859)
设在第L层,并要降到下一层。如果下一层的间隙容量是3,则提高该间隙的中间项使其高度为L,从而形成两个容量为1的间隙。由于这使得朝下删除的道路上消除了容量为3的间隙,因此插入式安全的。
本质上就是2-3-4树的自顶向下插入 实际实现
5)删除
删除采用2-3-4树的自顶向下的方法(保证当前结点不是2-结点):(参考http://www.jianshu.com/p/8258ed821859)
3.2 素性测试
确定一个大数是否是素数的问题。
程序的说明:
- step1. Witness(A, N-1, N) == 1
利用的费马小定理:A^(N - 1) mod N 是否等于1 - step2. Witness(A, i, N)计算的是Ai次方的过程,利用的方法是Ai = A^(i/2) * A^(i/2),并在每一次递归计算的过程中检查X² mod N是否等于1,如果此时X不是1和N-1,则不是素数。
3.3 treap树
3.3.1 treap树的定义
- treap树是一棵二叉查找树
- 结点优先级满足堆序性质:任意结点的优先级必须至少和它的父亲的优先级一样大
结点包括:
- 左、右指针
- 优先级:该优先级是建立结点时随机指定的
具有最低优先级的结点必然是根。树是根据优先级的N!种可能的排列而不是根据项的N!中排列形成的。
3.3.2 treap树的操作
1)插入
- step1.将其新元素作为叶子插入(满足二叉搜索树性质)
- step2.将它沿着treap树向上旋转直到它的优先级满足堆序为止。
可以证明旋转的期望次数小于2
插入之后,通过左旋和右旋恢复堆序性质:
2)删除
这个删除方法也可以采用红黑树的删除方法,将删除归结为叶子节点的删除。(使用后继,这样可以节省很多旋转)
- step1.找到删除项,并将删除项的优先级增加到无穷大
- step2.沿着低优先级诸儿子的路径向下旋转,一旦旋转到树叶,则可删除。
证明:假设没有重复元,否则remove会失败。
删除程序有如下的流程:
T=SingleRotateWithLeft(T);
T = Remove(Item, T)
如果有相同的元素,可能会陷入无穷无尽的递归。
网友评论