1. 问题

给定一个全零矩阵的行和列,实现flip函数随机把一个0变成1并返回索引,实现rest函数将所有数归零。

2. 思路

拒绝采样

(1)先计算矩阵的元素个数(行乘以列),记作n,那么[0, n-1]相当于矩阵下标对应的一维索引。

(2)用一个arrays数组存放矩阵元素为1的索引。每次从 [0, n-1]取数,这个数可以表示矩阵元素的索引,如果取的数已经在这个数组里,说明这个索引对应的矩阵元素已经被flip为1了,则放弃,继续取数,直到取到的数不在数组里(对应的矩阵元素为0),就把取到的数加入arrays数组,表示已经被flip为1。

(3)取到的这个数(矩阵元素对应的一维索引)可以转换成矩阵的位置下标然后返回。

(4)关于拒绝采样的原理,我在470. Implement Rand10() Using Rand7() (拒绝采样Reject Sampling)做过阐述。简单说就是,当矩阵中含有0和1时,我想在只含有0的这些数中取样,但是这样取样比较困难,我可以直接从整个矩阵采样,当采样到1的时候拒绝,继续采样,直到采样到0为止,这样子的采样可以视为“在只含有0的这些数中取样”的一种近似,特别是1的个数远小于0的个数时,两种采样会很接近。

(5)缺点:但是当1的个数很大时,这样采样的效率就很低了,可能大部分情况采样的都是1,需要采样很多次才能采样到0。note中提到,行和列最大为1万,那么总大小就为一亿,而flip和reset的调用次数不会超过1000,只看这个最坏情况的话,这么做还挺ok的,毕竟flip这么少次,1的个数相对于0来说可以忽略不计了。

(方法二)Fisher-Yates Shuffle算法

Fisher-Yates洗牌算法是用来打乱一个随机序列的算法,主要步骤为:在0到n(索引)之间生成一个数m,交换m和n(索引对应的数),n(索引)减掉1,循环这三步,直到n等于0。主要思想就是每次采样(索引)时,当前随机采样到的数(索引对应的数)交换到最后一个数(末尾索引对应的数),然后采样池数量减一(末尾索引减一),然后继续采样和交换(不断迭代),直到采样池为空。这里参考了Fisher-Yates洗牌算法,主要步骤如下:

(1)建立一维数组,每个元素含有三个值,两个值为矩阵坐标,一个为flip值,0/1。

(2)每次flip时,按数组长度,随机抽取一个索引值,将这个索引对应的元素里的flip值从0变成1,然后把这个元素和最后一个元素交换。

(3)数组长度减1,然后返回最后一个元素(这个“最后一个元素”指的是数组长度减1之前,数组的最后一个元素)。

(4)对于reset操作,只需要把所有flip值改为0即可。

(5)然而这样做超时了,因为要建立的这个一维数组太大了,note中说到,行和列最大为1万,那么总大小就为一亿。而且flip和reset的调用次数不会超过1000,所以很多元素是不会被采样到的,用这么大的数组来维护实在是太奢侈了。实际上我们只要维护那些已采样的元素就可以了。

(6)考虑不使用这么大的数组,而是使用一个dict来映射已采样元素和最后一个元素,下面的方法三阐述了这样的做法。

(方法三)Fisher-Yates Shuffle算法,利用dict字典映射

(1)计算矩阵的元素个数记作n,那么[0, n-1]相当于矩阵下标对应的一维索引。

(2)每次采样(flip)时,维护一个字典dict,key表示采样到的索引,value表示这一次采样时的末尾索引。每次采样时末尾索引都不一样,第一次采样时末尾索引为n-1,每次采样前,我们都把末尾索引减1。

(3)这样,如果采样到“已经采样过的索引”,我们就可以从dict中根据“已经采样过的索引”(key)得到value(“采样这个已经采样过的索引时记录下来的末尾索引”)来作为我们的采样索引。

(4)如果采样到“还未采样过的索引”,则直接使用这个索引来作为我们的采样索引。

(5)在返回采样索引之前,我们需要更新dict,插入(更新)采样索引对应的value,即这次采样时的末尾索引(因为末尾索引可能也被采样过,所以要先从dict中查看是否存在“末尾索引”,如果采样则使用末尾索引对应的value)。

(6)最后返回采样索引对应的矩阵位置

3. 代码

拒绝采样

import random
class Solution(object):
def __init__(self, n_rows, n_cols):
self.n_rows, self.n_cols = n_rows, n_cols
self.n = n_rows * n_cols
self.arrays = [] def flip(self):
while True:
i = random.randint(0,self.n-1)
if i not in self.arrays:
self.arrays.append(i)
break
return [i/self.n_cols, i%self.n_cols] def reset(self):
self.arrays = []

时间复杂度O(1),最坏情况O(无穷)

空间复杂度O(n)

(方法三)Fisher-Yates Shuffle算法,利用dict字典映射

import random
class Solution(object):
def __init__(self, n_rows, n_cols):
self.n_rows, self.n_cols = n_rows, n_cols
self.reset() def flip(self):
self.n -= 1
i = random.randint(0, self.n)
index = self.dic.get(i, i)
self.dic[i] = self.dic.get(self.n, self.n)
return [index / self.n_cols, index % self.n_cols] def reset(self):
self.n = self.n_rows * self.n_cols
self.dic = {}

时间复杂度O(1)

空间复杂度O(n)

4. 类似题目

470. Implement Rand10() Using Rand7() (拒绝采样Reject Sampling)

478. Generate Random Point in a Circle

519. Random Flip Matrix(Fisher-Yates洗牌算法)的更多相关文章

  1. 【JavaScript】数组随机排序 之 Fisher–Yates 洗牌算法

    Fisher–Yates随机置乱算法也被称做高纳德置乱算法,通俗说就是生成一个有限集合的随机排列.Fisher-Yates随机置乱算法是无偏的,所以每个排列都是等可能的,当前使用的Fisher-Yat ...

  2. 【LeetCode】519. Random Flip Matrix 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 题目地址:https://leetcode.com/problems/random-fl ...

  3. 【leetcode】519. Random Flip Matrix

    题目如下: You are given the number of rows n_rows and number of columns n_cols of a 2D binary matrix whe ...

  4. Fisher–Yates shuffle 洗牌算法(zz)

    1,缘起 最近工作上遇到一个问题,即将一组数据,比如[A,B,C,D,E]其中的两个B,E按随机排列,其他的仍在原来的位置: 原始数组:[A,B,C,D,E] 随机字母:[B,D] 可能结果:[A,B ...

  5. random array & shuffle 洗牌算法 / 随机算法

    random array & shuffle shuffle 洗牌算法 / 随机算法 https://en.wikipedia.org/wiki/Fisher–Yates_shuffle ES ...

  6. 洗牌算法及 random 中 shuffle 方法和 sample 方法浅析

    对于算法书买了一本又一本却没一本读完超过 10%,Leetcode 刷题从来没坚持超过 3 天的我来说,算法能力真的是渣渣.但是,今天决定写一篇跟算法有关的文章.起因是读了吴师兄的文章<扫雷与算 ...

  7. 洗牌算法shuffle

    对这个问题的研究始于一次在群里看到朋友发的洗牌面试题.当时也不知道具体的解法如何,于是随口回了一句:每次从剩下的数字中随机一个.过后找相关资料了解了下,洗牌算法大致有3种,按发明时间先后顺序如下: 一 ...

  8. js 随机数 洗牌算法

    function shuffle(arr){ var len = arr.length; for(var i = 0;i<len -1;i++) { var idx = Math.floor(M ...

  9. 洗牌算法Fisher_Yates原理

    1.算法 http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 简单的原理如下图所示: 2.原理 总结下,洗牌算法Fisher_Yates ...

随机推荐

  1. 【C#】基础知识

    C#常用函数表1.DateTime 数字型 System.DateTime currentTime=new System.DateTime(); 1.1 取当前年月日时分秒 currentTime=S ...

  2. backbone.js之Model篇 简单总结和深入(2)

    一.模型属性的一些操作方法 1.mmodel.get()  获取属性的值 2.mmodel.set('age',5) 更新单个属性的值  mmodel.set({name:'aaa',age:6}) ...

  3. 7.18python进程池

    主要方法  常用的就是   from multiprocessing import Pool map() 同步 apply() 同步 apply_async()  异步  手动 close()  jo ...

  4. DBCP连接池配置(DBCPUtils.java)

    配置文件 db_dbcp.properites driverClass=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/db?useSSL= ...

  5. js备忘录_1

    js没有重载,只有同名覆盖,参数任意 所有参数封装在arguments数组中 Uncaught ReferenceError: d is not defined js引擎会当做变量处理 functio ...

  6. 深圳MPD大会,五大专题一会尽享

    深圳MPD大会,五大专题一会尽享 2013年9月,深圳的高温将慢慢褪去,炎炎夏日也会变得稍微清凉一些.但9月It届的峰会活动却没有丝毫的锐减.9月7-8日深圳将迎来MPD大会2013的收官之站. MP ...

  7. Spring Boot中的自定义start pom

    start pom是springboot中提供的简化企业级开发绝大多数场景的一个工具,利用好strat pom就可以消除相关技术的配置得到自动配置好的Bean. 举个例子,在一般使用中,我们使用基本的 ...

  8. sql中varchar(max),取代text类型

    SQL Server 2005之后版本:请使用 varchar(max).nvarchar(max) 和 varbinary(max) 数据类型,而不要使用 text.ntext 和 image 数据 ...

  9. LoadRunner-创建场景

    录制完脚本,并调试运行正常后,想要模拟并发进行压力测试,需要创建场景. 1.点击Tools->Create Controller Scenario... 2.选择手工场景,并设置并发用户数.点击 ...

  10. 洛谷P4168 蒲公英 [Violet] 分块

    题解:分块+离散化 解题报告: 一个分块典型题呢qwq还是挺妙的毕竟是道黑题 然,然后发现忘记放链接了先放链接QAQ 有两三种解法,都港下qwq 第一个是O(n5/3)的复杂度,谢总说不够优秀没有港, ...