内容简介:—— 新年快乐,给每个有梦想的程序员小概率事件也要做的一丝不苟,大家都是程序员,为啥要用别人家写的随机代码。嘎嘎!双色球蓝号1-12、红号1-33,非常简单,只需保证生成的红号不相互重复就可以,然后就是考虑如何做到真正的
—— 新年快乐,给每个有梦想的程序员
生成随机号
小概率事件也要做的一丝不苟,大家都是程序员,为啥要用别人家写的随机代码。嘎嘎!
双色球蓝号1-12、红号1-33,非常简单,只需保证生成的红号不相互重复就可以,然后就是考虑如何做到真正的 随机
。
还有一个问题就是如何存储一组号码。首先,分成红区和蓝区两部分,最后一个号约定为蓝号。另外,为了方便存储,我们放弃了将每个数字用符号连接的方式,而是自定义了 34进制
,用于保证每组号码的长度都是7。
var redBall = map[int]rune{ 1: '1', 2: '2', 3: '3', //...... 31: 'V', 32: 'W', 33: 'X', } var redFlip = map[rune]int{ '1': 1, '2': 2, '3': 3, //... 'V': 31, 'W': 32, 'X': 33, }
我们提供一个编解码的方法,用于将字符串转换为一组号码。对应的,将一组号码转换为长度为7的字符串。
随机红号范围控制在 1-33
,蓝号控制在 1-16
。所以,我们对当前纳秒进行取余,便可以保证数据的正确。对于去重部分,通过 map
属性来达到目的, map
的 key
存储生成的随机号, value
存储对应的编码。因为 map
结构读取数据时本身也是随机的,所以在生成红号和蓝号的时候便多生成一部分,最后再取6个红号,1个蓝号。
type TwoColor struct { } //encode func (color *TwoColor) Encode(origin []int) string { runes := make([]rune, 0) for _, v := range origin { if elem, ok := redBall[v]; ok { runes = append(runes, elem) } return string(runes) } //decode func (color *TwoColor) Decode(origin string) []int { result := make([]int, 0) for _, v := range origin { if elem, ok := redFlip[v]; ok { result = append(result, elem) } } return result } //generate random numbers func (color *TwoColor) GenerateRandom() string { redResult := make(map[int]rune, 12) for len(redResult) < 12 { key := time.Now().Nanosecond()%33 + 1 redResult[key] = redBall[key] } blueBall := make(map[int]rune, 2) for len(blueBall) < 2 { key := time.Now().Nanosecond()%16 + 1 blueBall[key] = redBall[key] } index := 0 result := make([]rune, 7) for _, v := range redBall { index++ result = append(result, v) if index == 6 { break } } for _, v := range blueBall { result = append(result, v) break } return string(result) }
批量生成随机数
批量生成不重复的随机数,核心就是解决如何存储的问题。每次都得跟之前生成的记录做比较,保证当前生成的记录是不重复的。
之所以有批量生成的需求,首先是为了使生成的记录更随机。其次,试想一下,如果你内存足够大,生成 5000W
条记录,官方开奖后,看看这 5000W
会不会中一注。如果这么多都没有中,那真是一件令人伤心的事情啊。
为了简单方便,我们直接将生成的记录存储到本地内存中,这里使用前面讲到的
bigcache
。我们将生成的记录作为 Key
, Value
不存储实际的值了。
对于 ZSet
,我们这里只想体现它的唯一属性。只有当记录不存在时才进行存储。
type DBCenter struct { } func (db *DBCenter) ZSet(key string) { if db.Exists(key) { return } cache.Set(key, nil) } func (db *DBCenter) Exists(key string) bool { _, err := cache.Get(key) if err != nil { if _, ok := err.(*bigcache.EntryNotFoundError); ok { return false } } return true } func (db *DBCenter) Len() int { return cache.Len() }
批量生成的过程就是一个 for
循环的过程,并通过 Len
方法获取当前生成的记录数。
存储开奖号码
将每期的开奖号码作为下一期的参考,是每个迷信中彩人的执着。
我们选择将每期的中奖号码存储到文件中,实在没有必要使用关系型数据库。在使用过程中,我们不会部署到多台服务器上。这个项目就是在个人电脑上运行设计的,单文件存储足够了。
关于 write
方法 ,写入的内容包括日期和中奖号码,连接符使用了 SeparatorData
。在打开文件的模式中,我们指定了 O_APPEND
,将写入追加到文件末尾。这也导致最新的开奖号码出现在文件的末尾。当读取最近几期的开奖号码时,需要定位开始读取的位置。
const ( FilePath = "/Users/neojos/src/boredom/data/history.txt" SeparatorData = "\t" ) type FileData struct { } func (f *FileData) GetLatestHistory(limit int64) ([]string, error) { file, err := os.OpenFile(FilePath, os.O_CREATE|os.O_RDONLY, 0755) if err != nil { return nil, err } defer file.Close() info, err := file.Stat() if err != nil { return nil, err } rowLength := 0 scan := bufio.NewScanner(file) for scan.Scan() { rowLength = len(scan.Text()) + 1 //'\n' break } if err = scan.Err(); err != nil { return nil, err } result := make([]string, 0) file.Seek((info.Size()/int64(rowLength)-limit)*int64(rowLength), 0) scan = bufio.NewScanner(file) for scan.Scan() { tmp := strings.Split(scan.Text(), SeparatorData) result = append(result, tmp[len(tmp)-1]) } if err = scan.Err(); err != nil { return result, err } reverseResult := make([]string, limit) for i, v := range result { reverseResult[int(limit)-i-1] = v } return reverseResult, nil } func (f *FileData) Write(date, number string) error { file, err := os.OpenFile(FilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0755) if err != nil { return err } defer file.Close() line := fmt.Sprintf("%s\t%s\n", date, number) _, err = file.WriteString(line) return err }
GetLatestHistory
用于获取最近几期的开奖号码。因为每行写入的长度相同,所以通过计算,可以得出当前文件的行数,并将文件的 offset
定位到指定的位置,从具体的 offset
开始读取。另外,因为我们写入的模式是 APPEND
,所以将获取到的 slice
再做一次顺序反转,便得到最近几期的开奖记录了。
生成图
通过
plot
来绘制一些简易的图形。将最近的开奖号码作为数据源,来观察数据的走势。虽然确实没啥用处。
这段代码直接运行会 panic
,因为修改了 plot
中的部分代码。
type Curve struct { elements [][]int } func (curve *Curve) SetElem(input [][]int) { curve.elements = input } func (curve *Curve) GetBaseImg() error { pic, err := plot.New() if err != nil { return err } pic.X.Label.Text = "ball" pic.Y.Label.Text = "number" // Draw a grid behind the data pic.Add(plotter.NewGrid()) customTick := commaTicks{ CustomTicks: 7, } pic.X.Tick.Marker = customTick customTick.CustomTicks = 33 pic.Y.Tick.Marker = customTick picElem := make(map[string]plotter.XYer) for index, element := range curve.elements { points := plotter.XYs{} for k, v := range element { points = append(points, plotter.XY{X: float64(k + 1), Y: float64(v)}) } picElem[fmt.Sprintf("%d", index)] = points } plotutil.AddLinePoints(pic, picElem) return pic.Save(vgsvg.DefaultWidth, vgsvg.DefaultWidth, "base.png") }
生成的图片如下:
总结
项目的代码在
boredom
,代码非常简单。祝大家早日中大奖!
以上所述就是小编给大家介绍的《新年彩蛋之中大奖》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- C语言歌星大奖赛代码解析
- 腾讯开源斩获5项大奖,1项独家认证
- 国家超级计算广州中心入围OpenStack全球超级用户大奖
- Ixia斩获Interop多项Best of Show大奖
- 网易云轻舟微服务斩获“创新产品奖”等两项大奖
- 千元大奖 | 全城悬赏寻找“消失”的程序员戴码!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
疯狂Java讲义
李刚 / 电子工业出版社 / 2012-1-1 / 109.00元
《疯狂Java讲义(附光盘第2版)》是《疯狂Java讲义》的第2版,第2版保持了第1版系统、全面、讲解浅显、细致的特性,全面介绍了新增的Java 7的新特性。 《疯狂Java讲义(附光盘第2版)》深入介绍了Java编程的相关方面,全书内容覆盖了Java的基本语法结构、Java的面向对象特征、Java集合框架体系、Java泛型、异常处理、Java GUI编程、JDBC数据库编程、Java注释、......一起来看看 《疯狂Java讲义》 这本书的介绍吧!