RANDOMIZE-IN-PLACE随机排列算法

news2025/1/10 10:30:46

给定一个长度为 n n n的数组,如何构造出一个随机排列呢?《算法导论》给了我们一个名为RANDOMIZE-IN-PLACE的随机算法,该算法在数组原址上进行排序,时间复杂度为 O ( n ) O(n) O(n)。下面本文将介绍RANDOMIZE-IN-PLACE的设计思想及代码实现。

1. 背景知识

在介绍RANDOMIZE-IN-PLACE的设计思想之前,我们首先来了解一些背景知识,什么是随机算法,什么是随机排列数组,以及为什么要随机排列数组。

1.1 随机算法

如果一个算法的行为不只是由输入决定,同时也由随机数生成器所产生的数值决定,则称这个算法是随机的。

1.2 随机排列数组

随机排列数组的目标是产生一个均匀随机排列,使得 n ! n! n!种排列中的每一种被取到的概率相等,等于 1 n ! \frac{1}{n!} n!1

许多随机算法通过排列给定的输入数组使得输入随机化。

2. 随机排列数组的两种算法

PERMUTE-BY-SORTING和RANDOMIZE-IN-PLACE是实现随机排列数组的两种算法。

2.1 PERMUTE-BY-SORTING

设数组 A = [ 1 , 2 , … , n ] A=[1, 2, …, n] A=[1,2,,n],PERMUTE-BY-SORTING为其中的每个元素 A [ i ] A[i] A[i]赋一个随机数优先权 P [ i ] P[i] P[i],然后根据这些优先权对数组 A A A进行排序。伪码如下:

PERMUTE-BY-SORTING(A)
n = length[A]
for(i=1; i<=n; i++)
	P[i] = RANDOM(1, n^3)
sort A, using P as sort keys
return A

在上述伪码中,使用 1 ~ n 3 1~n^3 1n3间的 n n n个随机优先权的原因有两点:

  1. 能在 O ( n log ⁡ n ) O(n\log{n}) O(nlogn)时间内排序完成。
  2. 以很高的概率使得 n n n个优先权都不相同。

2.2 RANDOMIZE-IN-PLACE

RANDOMIZE-IN-PLACE也称为Fisher-Yates Shuffle或Knuth Shuffle算法,与PERMUTE-BY-SORTING算法相比,它有三方面优势:

  1. 省去了排序的代价,时间复杂度为 O ( n ) O(n) O(n)
  2. 随机数产生器所需范围更小(从 1 ~ n 3 1~n^3 1n3降为 1 ~ n 1~n 1n)。
  3. 不需要额外的辅助空间。

伪码如下:

RANDOMIZE-IN-PLACE(A, n)
for(i=1; i<=n; i++)
	swap(A[i], A[RANDOM(i, n)])

即在第 i i i次迭代时,从 A [ i … n ] A[i…n] A[in]随机选取元素与 A [ i ] A[i] A[i]交换位置;在第 i i i次迭代后, A [ i ] A[i] A[i]不再发生改变。

3. RANDOMIZE-IN-PLACE的正确性证明

下面我们来证明RANDOMIZE-IN-PLACE确实可以产生一个均匀随机排列。

证明:
循环不变式:for循环的第 i i i次迭代之前,对每个可能的 ( i − 1 ) (i-1) (i1)-排列,子数组 A [ 1 … i − 1 ] A[1…i-1] A[1i1]包含这个 ( i − 1 ) (i-1) (i1)-排列的概率为 ( n − i + 1 ) ! / n ! (n-i+1)! / n! (ni+1)!/n!

初始化: i = 1 i=1 i=1 A [ 1 … 0 ] A[1…0] A[10]包含某个0排列的概率为 ( n − i + 1 ) ! / n ! = 1 (n-i+1)!/n!=1 (ni+1)!/n!=1,其中 A [ 1 … 0 ] A[1…0] A[10],0排列则是不包含任何元素的序列。

保持: 假设第 i i i次迭代前,任一可能的 ( i − 1 ) (i-1) (i1)-排列出现在 A [ 1 … i − 1 ] A[1…i-1] A[1i1]中的概率为 ( n − i + 1 ) ! / n ! (n-i+1)! / n! (ni+1)!/n!,则需证明在第 i i i次迭代后,任一 i i i-排列出现在 A [ 1 … i ] A[1…i] A[1i]中的概率为 ( n − i ) ! / n ! (n-i)! / n! (ni)!/n!

  • 考虑一个特殊的 i i i-排列 R = [ x 1 , x 2 , … , x i ] R=[x_1, x_2, … , x_i] R=[x1,x2,,xi],其中包含一个 ( i − 1 ) (i-1) (i1)-排列 [ x 1 , x 2 , … , x i − 1 ] [x_1, x_2, … , x_{i-1}] [x1,x2,,xi1],算法在 A [ i ] A[i] A[i]放置 x i x_i xi
  • 令事件 E 1 E_1 E1表示算法实际输出 ( i − 1 ) (i-1) (i1)-排列 R ∗ R^{*} R A [ 1 … i − 1 ] A[1…i-1] A[1i1],根据循环不变量, P r ( E 1 ) = ( n − i + 1 ) ! / n ! Pr(E_1)=(n-i+1)!/n! Pr(E1)=(ni+1)!/n!
  • 令事件 E 2 E_2 E2表示第 i i i次迭代后输出 A [ i ] A[i] A[i] x i x_i xi,则当且仅当 E 1 E_1 E1 E 2 E_2 E2同时发生时我们得到 i i i-排列 R R R A [ 1 … i ] A[1…i] A[1i],其概率 P r ( E 2 ∩ E 1 ) = P r ( E 2 ∣ E 1 ) ⋅ P r ( E 1 ) Pr(E_2\cap{E_1})=Pr(E_2|E_1)·Pr(E_1) Pr(E2E1)=Pr(E2E1)Pr(E1)
    • 给定 E 1 E_1 E1的条件下, A [ i ] A[i] A[i] ( n − i + 1 ) (n-i+1) (ni+1)种取法,取到 x i x_i xi的概率为 1 / ( n − i + 1 ) 1/(n-i+1) 1/(ni+1),即 P r ( E 2 ∣ E 1 ) = 1 / ( n − i + 1 ) Pr(E_2|E_1) = 1/(n-i+1) Pr(E2E1)=1/(ni+1)
    • 因此, P r ( E 2 ∩ E 1 ) = P r ( E 2 ∣ E 1 ) ⋅ P r ( E 1 ) = 1 n − i + 1 ⋅ ( n − i + 1 ) ! n ! = ( n − i ) ! / n ! Pr(E_2\cap{E_1})=Pr(E_2|E_1)·Pr(E_1)=\frac{1}{n-i+1}·\frac{(n-i+1)!}{n!}=(n-i)! / n! Pr(E2E1)=Pr(E2E1)Pr(E1)=ni+11n!(ni+1)!=(ni)!/n!

终止: 终止时, i = n + 1 i=n+1 i=n+1 A [ 1 … n ] A[1…n] A[1n]是一个给定 n n n-排列的概率为 ( n − ( n + 1 ) + 1 ) ! / n ! = 1 / n ! (n-(n+1)+1)!/n! = 1/n! (n(n+1)+1)!/n!=1/n!

4. 代码

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 10       //定义数组的长度为10 

void randomizeInPlace(int array[], int length) {  //对长度为length的数组array[]进行随机排列 
	int i;
	for (i = 0; i < length; i++) {
		int rnd = i + (rand() % (length - i));  //生成[i, length-1)范围内的一个随机下标 
		//交换 array[i]与array[rnd] 
		int temp = array[i];          
		array[i] = array[rnd];
		array[rnd] = temp;
	}	
}

int main() {
	srand(time(NULL));  //设置时间函数time(NULL)为随机数种子 
	int array[N];
	int i, j;
	for (i = 0; i < N; i++) {  //为原数组赋初值 
		array[i] = i + 1;
	}
	printf("原数组为:");      //打印输出原数组 
	for (i = 0; i < N; i++) {
		printf("%d ", array[i]);
	}
	printf("\n");
	int times;
	printf("请输入随机重排的次数:");
	scanf("%d", &times);       //用户指定随机重排的次数
	for (j = 0; j < times; j++) {
		randomizeInPlace(array, N);  //每次随机排列都是在上次随机排列的数组基础之上  
		printf("随机重排%d次的数组为:", j + 1); 
		for (i = 0; i < N; i++) {
			printf("%d ", array[i]);
		}
		printf("\n");
	}
	return 0;
} 

5. 运行结果

数组的初值为1,2,3,4,5,6,7,8,9,10。用户输入随机重排的次数k,按下回车键,程序打印输出连续k次随机重排的数组。

随机重排10次的结果如下:

随机重排10次的结果

随机重排20次的结果如下:

随机重排20次的结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1512113.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码随想录(day3)——链表

Leetcode.203 移除链表元素&#xff1a; 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 对于本题&#xff0c;难点就在于对于头部结点的删除&#xff0c;以及给定链表为空时&#xff0c;如何进行遍历。因为需要遍历链表&#xff0c;假设访问链表下一个结点所对应…

开源绘图工具 PlantUML 入门教程(常用于画类图、用例图、时序图等)

文章目录 一、类图二、用例图三、时序图 一、类图 类的UML图示 startuml skinparam classAttributeIconSize 0 class Dummy {-field1 : String#field2 : int~method1() : Stringmethod2() : void } enduml定义能见度&#xff08;可访问性&#xff09; startumlclass Dummy {-f…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的行人跌倒检测系统(深度学习+UI界面+完整训练数据集)

摘要&#xff1a;开发行人跌倒检测系统在确保老年人安全方面扮演着至关重要的角色。本篇文章详尽地阐述了如何利用深度学习技术构建一个行人跌倒检测系统&#xff0c;并附上了完整的代码实现。该系统采用了先进的YOLOv8算法&#xff0c;并对YOLOv7、YOLOv6、YOLOv5等先前版本进…

ARM64汇编05 - MOV系列指令

MOV(wide immediate) MOV 可以将一个立即数移动到寄存器中。 .text:0000000000000834 80 46 82 D2 MOV X0, #0x1234 ; Keypatch modified this from:MOV X0, #0x1234 对应的汇编代码为&#xff1a;80 46 82 D2 看手册可知&#xf…

多维时序 | Matlab实现VMD-CNN-BiLSTM变分模态分解结合卷积神经网络结合双向长短期记忆神经网络多变量时间序列预测

多维时序 | Matlab实现VMD-CNN-BiLSTM变分模态分解结合卷积神经网络结合双向长短期记忆神经网络多变量时间序列预测 目录 多维时序 | Matlab实现VMD-CNN-BiLSTM变分模态分解结合卷积神经网络结合双向长短期记忆神经网络多变量时间序列预测预测效果基本介绍程序设计参考资料 预测…

利用“定时执行专家”软件的25种任务与12种触发器,提升IT系统管理自动化水平

在IT系统管理中&#xff0c;自动化是提高工作效率、减少人为错误的关键。而《定时执行专家》这款软件&#xff0c;以其强大的功能、易用性和毫秒级的执行精度&#xff0c;成为了IT系统管理员的得力助手。今天&#xff0c;我们就来探讨一下如何利用这款软件的25种任务类型和12种…

如何在Linux系统安装SVN并配置固定公网地址远程访问【内网穿透】

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

2024-03-11,12(HTML,CSS)

1.HTML的作用就是在浏览器摆放内容。 2.HTML基本骨架 head&#xff1a;网页头部&#xff0c;是给浏览器看的代码&#xff0c;例如CSS body&#xff1a;网页主体&#xff0c;是给用户看的代码&#xff0c;例如图片&#xff0c;文字。 title&#xff1a;网页标题 3.标签的两种…

Redis中set,zset

集合类型set中的数据是无序的&#xff0c;不能重复的 SET SADD key value [value....] 将一个或者多个元素添加到集合set中&#xff0c;重复的元素是无法进行添加的 返回值为添加成功的数字smembers key 获取set中所有的元素&#xff0c;返回元素的顺序是无序的sismember key…

React Hooks 那些事儿

翻了波之前写的文章还有笔记&#xff0c;发现关于前端的文章并不多&#xff08;好歹也划水做过点前端开发&#xff09;。巧了&#xff0c;最近没什么好话题可写&#xff0c;做下 React Hooks 学习笔记吧。 Effect Hook 不得不说 Hook 的出现降低了我们在 React 中处理副作用&…

【漏洞复现】SpringBlade error/list SQL 注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

定时执行专家 —— 让工作更高效,生活更便捷

在现代社会&#xff0c;高效的时间管理已经成为我们工作和生活中不可或缺的一部分。为了实现这一目标&#xff0c;我们经常会借助各种工具和软件来辅助我们完成定时任务。今天&#xff0c;我要为大家介绍一款功能强大、操作简便的定时任务执行软件——《定时执行专家》。这款软…

上海计算机学会 2023年11月月赛 丙组T5 推箱子(数学 思维 排序)

第五题&#xff1a;T5推箱子 标签&#xff1a;排序、数学、思维题意&#xff1a;给定 t t t组数据&#xff0c;每组数据给定长度为 n n n的字符串&#xff0c; 表示箱子&#xff0c; _ \_ _表示空格&#xff0c;求把箱子都推到一起&#xff08;即两两箱子之间没有空格&#…

idea+maven+tomcat+spring 创建一个jsp项目

概述&#xff1a;我真服了&#xff0c;这个垃圾学校还在教jsp&#xff0c;这种技术我虽然早会了&#xff0c;但是之前搞的大多都是springboot web类型的&#xff0c;这里我就复习一下&#xff0c;避免以后忘记这种垃圾技术 第一步&#xff1a;创建maven项目 第二步&#xff1a…

Leetcode 3.12

leetcode hot 100 链表1.两两交换链表中的节点2.随机链表的复制3.排序链表 链表 1.两两交换链表中的节点 两两交换链表中的节点 1.必须要设置一个dummy (temp) 结点2.保存第二个节点3.先让第一个节点指向第三个节点4.再让第二个节点指向第一个节点5.最后让dummy指向第二个节点…

基于深度学习的番茄叶片病害检测系统(含UI界面、yolov8、Python代码、数据集)

项目介绍 项目中所用到的算法模型和数据集等信息如下&#xff1a; 算法模型&#xff1a;     yolov8 yolov8主要包含以下几种创新&#xff1a;         1. 可以任意更换主干结构&#xff0c;支持几百种网络主干。 数据集&#xff1a;     网上下载的数据集&#x…

宏景eHR downlawbase SQL注入漏洞复现

0x01 产品简介 宏景eHR人力资源管理软件是一款人力资源管理与数字化应用相融合,满足动态化、协同化、流程化、战略化需求的软件。面向复杂单组织或多组织客户,支持流程,B/S架构。特别适合集团化管理和跨地域使用的产品,融合了最新的互联网技术和先进的人力资源管理理念和实…

浏览器与服务器通信过程(HTTP协议)

目录 1 概念 2 常见的 web 服务器有 3 浏览器与服务器通信过程 3.1 DNS 3.2 URL 4 HTTP请求方法和应答状态码 4.1 HTTP请求报文段实例 4.2 HTTP请求方法 5 HTTP应答报头和应答状态 5.1 HTTP的应答报头结构 5.2 HTTP的应答状态 1 概念 浏览器与 web 服务器在应用层通…

【DAY11 软考中级备考笔记】数据结构 查找和排序

数据结构 查找和排序 3月12日 – 天气&#xff1a;晴 1. 顺序查找 顺序查找就是简单的从头一个一个的进行比较&#xff0c;注意它的平均查找长度 2. 折半查找 折半查找和二叉排序树一致&#xff1a; 优点&#xff1a;查找效率很高 缺点&#xff1a;要求必须是循序存储并且表中…

UVC 设备框架在 Linux 4.15 内核的演变

1. 概述 发现之前的uvc框架和现在的还是有一些差别的&#xff08;比如从videobuf 过渡到videobuf2&#xff09;&#xff0c;写个blog记录一下&#xff0c;方便以后查询&#xff0c;我的内核版本&#xff1a;Linux 4.15 UVC&#xff08;USB Video Class&#xff09;设备框架是…