洗牌算法 -javascript实现

时间:2021-06-01 03:39:58

比如我们有52张牌,现在的需求就是洗牌(俗名打乱顺序 - -!)

先构造一个数组:

const nums = [ ' A ' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ' J ' , ' Q ' , ' K ']
const signs = [ ' ♥️ ' , ' ♦️ ' , ' ♣️ ' , ' ♠️ ']
const cards = []

signs . forEach( sign => {
nums . forEach( num => {
cards . push( {
kind : sign ,
num : num
})
})
})

这样cards 就构造完成了,但是JS这样写很low,但是我觉得很快就想出来了,我们换一种其他优雅一点的方法(下面这段代码的作者是 月影):


function * getCards () {
const nums = [ ' A ' , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , ' J ' , ' Q ' , ' K ']
yield * nums . map( num =>( {key : ' ♥️ ' , num : num }))
yield * nums . map( num =>( {key : ' ♠️ ' , num : num }))
yield * nums . map( num =>( {key : ' ♦️ ' , num : num }))
yield * nums . map( num =>( {key : ' ♣️ ' , num : num }))
}
const cards = [ ... getCards()]


上面生成器的Key Point有两点:


  // Key Point -01

...扩展运算符 内部调用的是数据结构的Iterator接口,生成器返回的就是一个Iterator对象,即遍历器对象
对这个遍历器对象执行扩展运算符 ... 就会将内部遍历到的值转为一个数组

// Key Point -02

任何数据结构只要有Iterator接口,就可以用yield * 来遍历
let readStr = (function* (){ yield* 'hello from other side' }())
readStr.next().value 这一句会输出 字符'h'
如果你写一个执行器把这个生成器执行完毕,你会发现会依次将这个字符串的单个字符给输出


好到现在为止,准备工作做完了,cards数组我们已经有了,现在我们来洗牌


// 洗牌
function shuffle ( cards ) {
for( let i = cards .length - 1 ; i >= 0 ; i --) {
var randomIndex = Math . floor(i * Math . random())
// 从 index 为 51 开始, 51 * Math.random() 的范围是 (0, 51)
// Math.floor 之后的整数范围是 [0, 50]
// 我们将 cards[51] 和 前面的随机索引位置的值交换
// 然后到 50个的时候,和 前面 [0, 49] 中索引取一个随机的 然后交换值
// 这样下去,交换位置之后 索引为51 和 50 的值就不会变动了
const temp = cards[randomIndex]
cards[randomIndex] = cards[i]
cards[i] = temp
}
return cards
}


其实感觉在遍历数组的时候改变数组不太好,可以返回一个新的数组更好一点,当然也看实际的需求。


// 强制复习一下
const params = ( function * (){
yield 1 ;
yield 2 ;
yield * ( function * (){ yield 3 ; yield 4 ; })()
yield * [ 5 , 6 , 7]
yield * ' abc '
// yield * { length: 9, name: 'Ariza' }
})()
console . log([ ...params])