RANDOMIZE-IN-PLACE随机排列算法

给定一个长度为 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.mfbz.cn/a/451924.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的水下目标检测系统(深度学习模型+UI界面+训练数据集)

摘要&#xff1a;本研究详述了一种采用深度学习技术的水下目标检测系统&#xff0c;该系统集成了最新的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行了性能评估对比。该系统能够在各种媒介——包括图像、视频文件、实时视频流及批量文件中——准确地识别水…

HarmonyOS NEXT应用开发之多层嵌套类对象监听

介绍 本示例介绍使用Observed装饰器和ObjectLink装饰器来实现多层嵌套类对象属性变化的监听。 效果图预览 使用说明 加载完成后显示商品列表&#xff0c;点击刷新按钮可以刷新商品图片和价格。 实现思路 创建FistGoodsModel类&#xff0c;类对象是用Observed修饰的类Secon…

Linux运维:磁盘分区与挂载详解

Linux运维&#xff1a;磁盘分区与挂载详解 1、磁盘分区的原理2、查看系统中所有的磁盘设备及其分区信息3、进行磁盘分区&#xff08;对于sdb新磁盘&#xff09;4、格式化分区5、挂载分区&#xff08;临时挂载、永久挂载&#xff09;6、取消挂载分区7、删除分区 &#x1f496;Th…

pytorch激活函数

目录 1.激活函数由来2. 常见激活函数2.1 Sigmoid2.2 Tanh2.3 relu 1.激活函数由来 科学家对青蛙的神经元进行研究的时候发现&#xff0c;只有超过一定的阈值青蛙才会有反应&#xff0c;因此不能将多个输入做简单的加权平均&#xff0c;而需要一个阶梯函数也就是激活函数&#…

软考75-上午题-【面向对象技术3-设计模式】-设计模式的要素

一、题型概括 上午、下午题&#xff08;试题五、试题六&#xff0c;二选一&#xff09; 每一个设计模式都有一个对应的类图。 二、23种设计模式 创建型设计模式&#xff1a;5 结构型设计模式&#xff1a;7 行为设计模式&#xff1a;11 考试考1-2种。 三、设计模式的要素 3…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的烟雾检测系统详解(深度学习模型+UI界面升级版+训练数据集)

摘要&#xff1a;本研究详细介绍了一种集成了最新YOLOv8算法的烟雾检测系统&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期算法进行性能评估对比。该系统能够在包括图像、视频文件、实时视频流及批量文件中准确识别烟雾。文章深入探讨了YOLOv8算法的原理&#xff0c;提供了Pyth…

cocos2d-x-3.17 android升级 gradle NDK_DEBUG=0 -o NDK_DEBUG=1 -o cocos2dlua_shared

由于需要升级sdk版本 需要对应升级gradle版本 记录下升级内容 externalNativeBuild { ndkBuild { - //arguments NDK_DEBUG0 -o 修改成下面 arguments NDK_DEBUG0 } } debug { …

抓取Instagram数据:Fizzler库带您进入C#爬虫程序的世界

引言 在当今数字化的世界中&#xff0c;数据是无价之宝。社交媒体平台如Instagram成为了用户分享照片、视频和故事的热门场所。作为开发人员&#xff0c;我们可以利用爬虫技术来抓取这些平台上的数据&#xff0c;进行分析、挖掘和应用。本文将介绍如何使用C#编写一个简单的Ins…

JavaWeb-Maven

一、Maven概述 Maven是专门用于管理和构建Java项目的工具&#xff0c;它的主要功能有&#xff1a; 提供一套标准化的项目结构提供一套标准化的构建流程&#xff08;编译&#xff0c;测试&#xff0c;打包&#xff0c;发布......&#xff09;提供一套依赖管理机制 二、Maven简…

Mysql数据库问题

一、索引 索引分类&#xff1a;主键索引&#xff0c;普通索引&#xff0c;复合索引&#xff0c;唯一索引技术名词&#xff1a;回表&#xff0c;最左匹配&#xff0c;索引覆盖&#xff0c;索引下推 二、explain 之前已有文章讲解&#xff1a;优化器-SQL语句分析与优化 这里我…

爬虫案例1

通过get请求直接获取电影信息 目标页面: https://spa6.scrape.center/在network中可以看到是通过Ajax发送的请求&#xff0c;这个请求在postman中也可以直接请求成功&#xff0c;这只是一个用来练习爬虫的&#xff0c;没有达到js逆向的过程&#xff0c;需要通过分析js 代码来获…

C++开发基础——IO操作与文件流

一&#xff0c;基础概念 C的IO操作是基于字节流&#xff0c;并且IO操作与设备无关&#xff0c;同一种IO操作可以在不同类型的设备上使用。 C的流是指流入/流出程序的字节序列&#xff0c;在输入操作中数据从外部设备(键盘&#xff0c;文件&#xff0c;网络等)流入程序&#x…

lnmp环境部署-im

安装nginx 配置nginx源 vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck1 enabled1 gpgkeyhttps://nginx.org/keys/nginx_signing.key module_hotfixestrue安装nginx yum …

【开源】SpringBoot框架开发假日旅社管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统介绍2.2 QA 问答 三、系统展示四、核心代码4.1 查询民宿4.2 新增民宿评论4.3 查询民宿新闻4.4 新建民宿预订单4.5 查询我的民宿预订单 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的假日旅社…

产品推荐 - ALINX XILINX FPGA开发板 Artix-7 XC7A100T-2FGG484I

01开发板介绍 此款开发板采用核心板扩展板的模式&#xff0c;方便用户对核心板的二次开发利用。FPGA使用的是Xilinx公司的ARTIX-7系列的芯片&#xff0c;型号为XC7A100T-2FGG484I。在核心板使用了2片MICRON公司的MT41J256M16HA-125 DDR3芯片&#xff0c;组合成32bit的数据总线…

Java日志框架Log4j 2详解

目录 一、什么是日志&#xff1f; 二、日志的主要用途 三、常用日志框架 1、Apache Log4j 2、Commons Logging 3、SLF4J 4、Logback 5、JUL(Java Util Logging) 6、Log4j 2 四、log4j 2 的优点 五、Log4j 2下载和配置 1、访问Log4j – 下载 Apache Log4j™ 2官网&a…

Linux内核之kstrdup代码实例(二十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

【数据结构】线性表的定义及基本操作

文章目录 前言线性表的定义线性表的基本操作基本操作其他常用操作 总结 前言 数据结构的三要素是逻辑结构、数据的运算、存储结构&#xff08;物理结构&#xff09;&#xff0c;存储结构不同&#xff0c;运算的实现方式也不同。 本次文章包括线性表的定义和基本操作&#xff0…

rancher里的ingress如何配置gzip压缩

方案一&#xff0c;未试验成功&#xff0c;但配置过程值得记录一下 通过配置configmap&#xff0c;然后在ingress的deployment里引用configmap实现。 参考文章 创建configmap apiVersion: v1 kind: ConfigMap metadata:name: nginx-ingress-controllerannotations:{} # k…

Mybatis的XML配置文件

Xml文件中写SQL 为什么要学? 学习了Mybatis中XML配置文件的开发方式了&#xff0c;大家可能会存在一个疑问&#xff1a;到底是使用注解方式开发还是使用XML方式开发&#xff1f; 官方说明&#xff1a;https://mybatis.net.cn/getting-started.html 结论&#xff1a;使用Myba…
最新文章