【算法】洗牌算法

news2024/11/15 11:04:16

目录

  • 1.概述
  • 2.代码实现
    • 2.1.暴力法
    • 2.2.Fisher-Yates 洗牌算法
  • 3.应用

本文参考:
LeetCode 384. 打乱数组

1.概述

(1)洗牌算法可以理解为:设计算法来打乱一个没有重复元素的数组 nums,并且打乱后,数组的所有排列应该是等可能的

(2)假设数组 nums 的长度为 n,数组 nums 的全排列有 n! 种,也就是说打乱后的结果总共有 n! 种。因此设计的洗牌算法必须能够产生的结果必须有 n! 种可能,否则就是错误的。

2.代码实现

下面分别使用暴力法和 Fisher-Yates 洗牌算法来实现实现 Solution class:

Solution(int[] nums); 	//使用整数数组 nums 初始化对象
int[] reset(); 			//重设数组到它的初始状态并返回
int[] shuffle(); 		//返回数组随机打乱后的结果

2.1.暴力法

(1)暴力法的基本思想如下:

  • 将长度为 n 的数组 nums 中所有的数都放到 list 中,并初始化打乱后的数组 shuffle;
  • 循环 n 次,在第 i 次循环中 (0 ≤ i < n):
    • 在 list 中随机抽取一个数 num,将其作为打乱后的数组 shuffle 的第 i 个元素;
    • 从 list 中移除 num;

(2)对于原数组 nums 中的数 num 来说,被移动到打乱后的数组的第 i 个位置的概率为:

在这里插入图片描述

因此,原数组 nums 中的任意一个数被移动到打乱后的数组的任意一个位置的概率都是相同的。

(3)具体的代码实现如下:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

class Solution1 {
    //当前数组
    int[] nums;
    //初始数组
    int[] oriNums;
    
    //构造函数:使用整数数组 nums 初始化对象
    public Solution1(int[] nums) {
        this.nums = nums;
        this.oriNums = new int[nums.length];
        System.arraycopy(nums, 0, oriNums, 0, nums.length);
    }
    
    //重设数组 nums 到它的初始状态并返回
    public int[] reset() {
        System.arraycopy(oriNums, 0, nums, 0, nums.length);
        return nums;
    }
    
    //返回数组 nums 随机打乱后的结果
    public int[] shuffle() {
        // shuffled 为打乱后的数组
        int[] shuffled = new int[nums.length];
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < nums.length; ++i) {
            list.add(nums[i]);
        }
        Random random = new Random();
        for (int i = 0; i < nums.length; ++i) {
            //nextInt(bound):随机生成 [0, bound) 之间的一个整数并返回
            int j = random.nextInt(list.size());
            shuffled[i] = list.remove(j);
        }
        System.arraycopy(shuffled, 0, nums, 0, nums.length);
        return nums;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(nums);
 * int[] param_1 = obj.reset();
 * int[] param_2 = obj.shuffle();
 */

2.2.Fisher-Yates 洗牌算法

(1)考虑通过调整 list 的实现方式以上述方法:

  • 我们可以在移除 list 的第 k 个元素时,将第 k 个元素与数组的最后 1 个元素交换,然后移除交换后数组的最后 1 个元素,这样我们只需要 O(1) 的时间复杂度即可完成移除第 k 个元素的操作。此时,被移除的交换后数组的最后 1 个元素即为我们根据随机下标获取的元素。
  • 在此基础上,我们也可以不移除最后 1 个元素,而直接将其作为乱序后的结果,并更新待乱序数组的长度,从而实现数组的原地乱序。因为我们不再需要从数组中移除元素,所以也可以将第 k 个元素与第 1 个元素交换。

(2)具体地,实现算法如下:

  • 设待打乱的数组为 nums,其长度为 n。
  • 循环 n 次,在第 i 次循环中 (0 ≤ i < n):
    • 在 [i,n) 中随机抽取一个下标 j;
    • 将第 i 个元素与第 j 个元素交换;

其中数组 nums 中的 nums[i … n−1] 为待打乱的部分,其长度为 n - i;nums[0 … i−1] 则为打乱后的部分,其长度为 i。

(3)具体的代码实现如下:

class Solution {
    //当前数组
    int[] nums;
    //初始数组
    int[] oriNums;
    
    //构造函数:使用整数数组 nums 初始化对象
    public Solution(int[] nums) {
        this.nums = nums;
        this.oriNums = new int[nums.length];
        //将 nums 中的元素复制到 oriNums 中
        System.arraycopy(nums, 0, oriNums, 0, nums.length);
    }
    
    //重设数组 nums 到它的初始状态并返回
    public int[] reset() {
        //将 oriNums 中的元素复制到 nums 中,即完成回到初始化状态
        System.arraycopy(oriNums, 0, nums, 0, nums.length);
        return nums;
    }
    
    //返回数组 nums 随机打乱后的结果
    public int[] shuffle() {
        Random random = new Random();
        int length = nums.length;
        //洗牌算法
        for (int i = 0; i < length; i++) {
            //nextInt(bound):随机生成 [0, bound) 之间的一个整数并返回
            int j = i + random.nextInt(length - i);
            //交换 nums[i] 和 nums[j]
            int tmp = nums[i];
            nums[i] = nums[j];
            nums[j] = tmp;
        }
        return nums;
    }
}

/**
 * Your Solution object will be instantiated and called as such:
 * Solution obj = new Solution(nums);
 * int[] param_1 = obj.reset();
 * int[] param_2 = obj.shuffle();
 */

3.应用

大家可以去 LeetCode 上找相关的洗牌算法的题目来练习,或者也可以直接查看LeetCode算法刷题目录 (Java)这篇文章中的洗牌算法章节。如果大家发现文章中的错误之处,可在评论区中指出。

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

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

相关文章

使用C++实现学委作业管理系统

开发环境学委作业管理系统在 Microsoft Visual Studio 2013 编译器开发的 MFC 项目&#xff0c;计算机使用的系统是 window10。1.2 基本原理与技术要求熟悉文件读写、mfc 基本知识、c 类运用、链表使用、排序算法、Microsoft Visual Studio 2013 编译器的使用。1.3 需求说明学委…

【数据结构】二叉搜索树的实现

目录 一、二叉搜索树的概念 二、二叉搜索树的中序遍历用于排序去重 三、二叉搜索树的查找 1、查找的非递归写法 2、查找的递归写法 四、二叉搜索树的插入 1、插入的非递归写法 2、插入的递归写法 五、二叉搜索树的删除 1、删除的非递归写法 2、删除的递归写法 六、…

autojs模仿QQ长按弹窗菜单(二)

牙叔教程 简单易懂 上一节讲了列表和长按事件 autojs模仿QQ长按弹窗菜单 今天讲弹窗菜单 由粗到细, 自顶向下的写代码 我们现在要修改的文件是showMenuWindow.js function showMenuWindow(view) {let popMenuWindow ui.inflateXml(view.getContext(),<column><bu…

基于双层优化的微电网系统规划设计方法(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

机制设计原理与应用(一)机制设计基础

什么是机制设计&#xff1f; 微观经济学和CS /EE的交叉学科。它采用了一种工程方法来设计激励机制&#xff0c;以实现战略环境中不完全信息的预期目标。机制设计具有广泛的应用,特别是在资源管理方面。 文章目录1 机制设计的基础1.1 简介1.2 机制设计与博弈及优化的关系1.3 机…

手撕Pytorch源码#4.Dataset类 part4

写在前面手撕Pytorch源码系列目的&#xff1a;通过手撕源码复习了解高级python语法熟悉对pytorch框架的掌握在每一类完成源码分析后&#xff0c;会与常规深度学习训练脚本进行对照本系列预计先手撕python层源码&#xff0c;再进一步手撕c源码版本信息python&#xff1a;3.6.13p…

大数据之HBase集群搭建

文章目录前言一、上传并解压HBase安装包二、修改HBase配置文件&#xff08;一&#xff09;hbase-env.sh&#xff08;二&#xff09;hbase-site.xml三、配置环境变量四、复制jar包到lib文件夹五、修改regionservers文件六、分发安装包和配置文件七、启动Hbase八、验证HBase是否启…

尚硅谷前端ES6-ES11

ECMAScript 是由 Ecma 国际通过 ECMA-262 标准化得脚本程序设计语言。 1.let变量声明以及变量声明特性 <body><script>//let的声明let a , b10;//特性1&#xff1a;变量不能重复声明&#xff0c;避免命名污染// let star "罗翔"// let star "张…

Java | 浅谈多态中的向上转型与向下转型

文章目录&#x1f333;向上转型&#x1f4d5;概念明细&#x1f4aa;使用场景1&#xff1a;直接赋值&#x1f4aa;使用场景2&#xff1a;方法传参&#x1f4aa;使用场景3&#xff1a;方法返回&#x1f4aa;向上转型的优缺点&#x1f333;向下转型&#x1f529;向下转型解决【调用…

程序员拯救了一次地球

流浪地球2&#xff1a;程序员拯救了一次地球 顺便给我们讲了一个道理&#xff1a; 人类会谋划未来&#xff0c; 但关键的一步是靠勇气迈出去的 趣讲大白话&#xff1a;算得好不如胆量好 *********** 电影工业的皇冠是特效 国产电影的特效进步不小 时时刻刻&#xff0c;分分秒秒…

用户画像计算更新

3.1 用户画像计算更新 目标 目标 知道用户画像建立的流程应用 无 3.1.1 为什么要进行用户画像 要做精准推送同样可以使用多种推荐算法&#xff0c;例如&#xff1a;基于用户协同推荐、基于内容协同的推荐等其他的推荐方式&#xff0c;但是以上方式多是基于相似进行推荐。而构…

ROS移动机器人——ROS基础知识与编程

此文章基于冰达机器人进行笔记整理&#xff0c;使用的环境为其配套环境&#xff0c;可结合之前的ROS&#xff0c;赵虚左老师的文章结合进行观看&#xff0c;后期也会进行整合 1. ROS安装 &#xff08;1&#xff09;配置ubuntu的软件和更新&#xff0c;允许安装不经认证的软件…

JS手动触发PWA安装窗口

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的博客 &#x1f34a;个人信条&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;…

仿写Dubbo-初识Dubbo

概念 Dubbo 在Dubbo官网介绍到&#xff0c;Apache Dubbo 是一款 RPC 服务开发框架&#xff0c;用于解决微服务架构下的服务治理与通信问题。 RPC RPC&#xff08;Remote Procedure Call&#xff09;远程过程调用协议&#xff0c;一种通过网络从远程计算机上请求服务&#xff0c…

【Android】手机安装Termux运行nodejs学习Javascript编程入门

Termux 是运行在Android手机上的一个 Linux 终端模拟器&#xff0c;干什么都要输入命令执行&#xff0c;不像 Windows 操作系统桌面用鼠标点点点&#xff0c;这里主要介绍用它来学习Javascript编程入门&#xff0c;当然&#xff0c;这和小时候学过的C语言编程课入门一样的&…

C语言之程序设计概述

1.1.1 程序的概念 程序&#xff1a;算法 数据结构 程序设计方法 语言工具和环境数据结构&#xff1a;数据的类型和数据的组织方式算法&#xff1a;对数据操作的方法和步骤 1.1.2 程序设计语言的种类 第一代语言&#xff08;机器语言&#xff09;&#xff1a;执行效率高、…

【Leetcode每日一题】35.搜素插入位置|二分查找数组下标

&#x1f331;博主简介&#xff1a;大一计科生&#xff0c;努力学习Java中!热爱写博客~预备程序媛 &#x1f4dc;所属专栏&#xff1a;LeetCode每日一题–进击大厂 ✈往期博文回顾: 【JavaSE】保姆级教程|1万字10张图学会类与对象–建议收藏 &#x1f575;️‍♂️近期目标&…

【题解】2023牛客寒假算法基础集训营2

目录A. Tokitsukaze and abn (easy)思路B. Tokitsukaze and abn (medium)思路Tokitsukaze and abn (hard)思路D. Tokitsukaze and Energy Tree思路bfsdfsE. Tokitsukaze and Energy Tree思维F. Tokitsukaze and Gold Coins (easy)思路G. Tokitsukaze and Gold Coins (hard)H. T…

高效团队的gitlab flow最佳实践

当前git是大部分开发团队的首选版本管理工具&#xff0c;一个好的流程规范可以让大家有效地合作&#xff0c;像流水线一样有条不紊地进行团队协作。 业界包含三种flow&#xff1a; Git flowGithub flowGitlab flow 下面我们先来分析&#xff0c;然后再基于gitlab flow来设计一…

19、Javaweb案例-登录功能

项目导入 选择travel项目的pom.xml文件&#xff0c;点击ok&#xff0c;完成项目导入。需要等待一小会&#xff0c;项目初始化完成。 启动项目 方式一&#xff1a; 方式二&#xff1a;配置maven快捷启动 技术选型 Web层 Servlet&#xff1a;前端控制器html&#xff1a;视图Fi…