洗牌算法的golang实现,顺便学习几个知识点

洗牌算法

洗牌我们首先想到的是使用随机数,每次获取一个1-54范围的随机数,直到所有的编号都被分配到,但是这有一个问题,就是随机数的产生可能有大量的重复,或者极端一点,某一个编号一直也获取不到,这样做显然是不合理的。

我们改进一下,从[0, 53]取一个数,去掉之后在剩下的数字中再取,直到取完。这种思想是合理的。但是实现需要加点技巧。直接看代码:

func shuffle(arr []int) {
    rand.Seed(time.Now().UnixNano())
    for i := 0; i < 54; i++ {
        arr[i] = i
    }
    for i := 53; i > 0; i-- {
        position := rand.Intn(i + 1) // [0,54) [0,53) [0,52) [0,51)... [0,2)
        arr[position], arr[i] = arr[i], arr[position]
    }
}

首先,我们将arr切片中每个位置添加对应牌号,0-53,然后我们在[0,54)中生成一个随机数,然后将其对应位置的值——牌号,和最后一个位置53的牌号互换,这就相当于是将数字取出了;然后下一个迭代,我们从[0, 53)中取随机数,它的位置的值与52位置的值互换;直到范围缩小到[0,1)停止循环。

从编号到花色

我们知道一副牌中,a desk of playing cards 有54张牌,其中除了两张大小王牌joker和black joker之外,其余每一种牌都有4个花色 heart spade club和diamond,分别对应ace 2 3 4 ... 10 jack queen king 共13种。我们想要通过卡牌的牌号0-53来打印出对应的花色应该怎么做呢?golang里面是没有枚举类型的,我们可以使用一个map来实现int和string的对应:

func showCard(n int) {
    suit := map[int]string{
        0: "heart",
        1: "spade",
        2: "\u9ED1\u6843", // 黑桃的Unicode码点
        3: "diamond",
    }
    num := map[int]string{
        0:  "A",
        1:  "2",
        2:  "3",
        3:  "4",
        4:  "5",
        5:  "6",
        6:  "7",
        7:  "8",
        8:  "9",
        9:  "10",
        10: "J",
        11: "Q",
        12: "K",
        13: "Joker1",
        14: "Joker2",
    }
    if n >= 52 {
        fmt.Printf("%-13s", num[n-39])
        return
    }
    fmt.Printf("%-2s %-10s", num[n/4], suit[n%4])
}

我们运行主程序看一下:

func main() {
    cards := make([]int, 54, 54)
    shuffle(cards)
    fmt.Println(cards)
    for i := 0; i < 54; i++ {
        if i%4 == 0 {
            fmt.Println()
        }
        showCard(i)
    }
}
[3 21 44 0 12 42 34 29 1 46 10 18 51 52 24 45 37 25 17 26 53 9 50 30 47 40 4 14 8 36 28 13 33 32 31 39 43 11 2 49 16 15 7 38 41 20 23 5 35 27 48 6 22 19]

A  heart     A  spade     A  黑桃        A  diamond
2  heart     2  spade     2  黑桃        2  diamond
3  heart     3  spade     3  黑桃        3  diamond
4  heart     4  spade     4  黑桃        4  diamond
5  heart     5  spade     5  黑桃        5  diamond
6  heart     6  spade     6  黑桃        6  diamond
7  heart     7  spade     7  黑桃        7  diamond
8  heart     8  spade     8  黑桃        8  diamond
9  heart     9  spade     9  黑桃        9  diamond
10 heart     10 spade     10 黑桃        10 diamond
J  heart     J  spade     J  黑桃        J  diamond
Q  heart     Q  spade     Q  黑桃        Q  diamond
K  heart     K  spade     K  黑桃        K  diamond
Joker1       Joker2

几个小知识

随机数生成

rand.Intn(int)会生成一个[0,n)的随机数,但是如果我们每次不修改随机数种子,他产生的随机数其实是固定顺序的

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    for i := 0; i < 8; i++ {
        fmt.Print(rand.Intn(5), " ")
    }
}

我们运行两次看一下结果:

E:\go\src\00test>go run main.go
1 2 2 4 1 3 0 0 
E:\go\src\00test>go run main.go
1 2 2 4 1 3 0 0 

因此,我们每次运行的时候要修改稿随机数种子,time.Now().UnixNano()方法会获取一个从时间戳到当前时间的纳秒数值,随时都在改变,可作为随机数种子。

// UnixNano returns t as a Unix time, the number of nanoseconds elapsed
// since January 1, 1970 UTC. The result is undefined if the Unix time
// in nanoseconds cannot be represented by an int64 (a date before the year
// 1678 or after 2262). Note that this means the result of calling UnixNano
// on the zero Time is undefined. The result does not depend on the
// location associated with t.
func (t Time) UnixNano() int64 {
    return (t.unixSec())*1e9 + int64(t.nsec())
}

Unicode码点

Unicode( http://unicode.org ),它收集了这个世界上所有的符号系统,包括重音符号和其它变音符号,制表符和回车符,还有很多神秘的符号,每个符号都分配一个唯一的Unicode码点,Unicode码点对应Go语言中的rune整数类型(译注:rune是int32等价类型)。通用的表示一个Unicode码点的数据类型是int32,也就是Go语言中rune对应的类型;它的同义词rune符文正是这个意思。

Go语言字符串面值中的Unicode转义字符让我们可以通过Unicode码点输入特殊的字符。有两种形式:\uhhhh对应16bit的码点值,\Uhhhhhhhh对应32bit的码点值,其中h是一个十六进制数字;一般很少需要使用32bit的形式。每一个对应码点的UTF8编码。例如:下面的字母串面值都表示相同的值:

"世界"
"\xe4\xb8\x96\xe7\x95\x8c"
"\u4e16\u754c"
"\U00004e16\U0000754c"

上面我们就通过黑桃的码点得到了黑桃汉字的string表示

格式化输出时设置每个字段占用空格数

fmt.Printf("%-2s %-10s", num[n/4], suit[n%4])

%-10s表示一个string类型的字段占用2个空格,多出来的部分用空格填充。-表示左对齐,不加为右对齐。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容

  • 1.安装 https://studygolang.com/dl 2.使用vscode编辑器安装go插件 3.go语...
    go含羞草阅读 1,553评论 0 6
  • Python标识符 在 Python 里,标识符有字母、数字、下划线组成。在 Python 中,所有标识符可以包括...
    tianyi6677阅读 1,092评论 0 3
  • 额,其实是个很简单的代码,只不过刚了解到,还是记录一下吧需要导入的包有两个,"math/rand"实现了洗牌算法的...
    Sixah阅读 2,546评论 0 2
  • 面向对象Java中的数据类型分为:原始数据类型和引用数据类型在数据类型转换中,由低类型转至高类型,将会自动(隐式)...
    pure_joy阅读 230评论 0 0
  • 1 一个Java源程序是由若干个类组成。 2 class 是Java的关键字,用来定义类。 3 Java应用程序中...
    少帅yangjie阅读 2,391评论 1 8