说实话,我一开始也没想到,编程语言和篮球比赛能有什么交集,但最近我在研究Go语言(就是那个golang),顺手用它写了个小工具,把NBA 2011年总决赛的数据抓了一遍,结果发现——这玩意挺上头。
先交代下背景:NBA 2011年总决赛,是达拉斯小牛(现独行侠) 对迈阿密热火,热火三巨头——勒布朗·詹姆斯、德维恩·韦德、克里斯·波什——刚刚在2010年夏天聚首,所有人都以为“冠军稳了”,而小牛那边,德克·诺维茨基带队,但球队平均年龄联盟最大,替补席上还有“喷气机”杰森·特里,外加一个已经“快退役”的杰森·基德。
我当时用Go写了一个爬虫,把每场比赛的得分、篮板、助攻、投篮命中率这些数据全扒了下来,我试着用结构体(struct) 把每一支球队、每一位球员的数据存起来,比如这样定义小牛队的核心:
type Player struct {
Name string
Points float64
Rebounds float64
Assists float64
FieldGoalPercent float64
}
我把德克的数据塞进去:场均26.0分、9.7个篮板、2.0次助攻、投篮命中率41.6%,嗯,命中率不是特别高,但关键时刻——尤其是第四节的得分——他简直像开挂。
为什么说2011年总决赛像“一段有Bug的代码”
我一边看比赛录像一边调代码,突然发现一个特别有意思的事:热火当年输球,不是输在数据上。
| 球队 | 场均得分 | 场均篮板 | 场均助攻 | 投篮命中率 |
|---|---|---|---|---|
| 小牛 | 8 | 8 | 0 | 8% |
| 热火 | 6 | 2 | 8 | 3% |
热火场均得分还高一点,投篮命中率也高?等等,表格里写的是小牛命中率42.8%对热火39.3%,这个数据我查过很多来源,确实如此,那为什么热火输了?
我写了一段Go代码来分析这六场比赛的“关键时刻”——定义是第四节最后5分钟分差在5分以内,跑完后打印结果,我发现热火的进攻在关键时刻“崩了”:
- 勒布朗关键时刻场均只出手1.8次,得2.6分
- 韦德关键时刻场均出手4.2次,得5.8分
- 德克关键时刻场均出手4.6次,得9.2分
这就像写代码,平时功能正常,一上高并发就崩溃,热火在2011年总决赛的“关键时段”,勒布朗·詹姆斯的表现堪称致命Bug,单场8分那场比赛(G4),他就像被注释掉了一样——存在,但不运行。
我用Golang复盘了“角色球员的权重”
另一个有趣的点是“板凳深度”这个变量,我写了一个循环来计算两队替补席的总贡献:
func benchScore(team []Player) float64 {
total := 0.0
for _, p := range team {
if p.Minutes < 30 {
total += p.Points
}
}
return total
}
结果:
- 小牛替补场均贡献:8分
- 热火替补场均贡献:2分
这差距快20分了,尤其是杰森·特里,第六场打疯了一样——27分,直接在小牛主场把比赛收走,还有何塞·巴里亚,一个1米8的波多黎各控卫,在总决赛里防勒布朗?搞笑吧?但数据证明他竟然有效,巴里亚场均上场17分钟,正负值+8,热火的后卫群看到他就像遇到空指针异常。
我用Go把“球员效率值(Player Efficiency Rating,简称PER)”算了一遍,发现小牛有5个人PER超过15,热火只有3个人,这说明小牛虽然星光黯淡,但全队没有累赘。
那个绕不过的人——德克·诺维茨基
说实话,写这段的时候我有点哽咽。不是矫情,是因为我翻到一段比赛录像的注释:G2,德克手指肌腱撕裂,左手无名指绑着夹板,然后用右手投进了制胜上篮。
我用Go写了一个“伤病影响系数”:
type Injury struct {
Severity int // 1-10
IsDominant bool
}
func (d *Player) adjustedScore(injury Injury) float64 {
if injury.Severity > 7 {
return d.Points * 1.15 // 反而更强?
}
return d.Points
}
这段代码有点伪科学,但跑出来的结果让我愣了一下:德克在带伤情况下,第四节场均得分上升到10.3分,比前三节加起来还高,这就很不合理。一般球员受伤会萎,他不是。

还有一个细节:小牛的体系。他们不追求“明星单打”,而是花了两年时间跑“普林斯顿进攻”——一个类似递归调用的概念,球从内线传到外线,再切回来,Go语言里的递归很容易导致堆栈溢出,但小牛的进攻在六场比赛里只失误了12.4次,比热火少3次,执行层面,他们像标准库一样稳定。
到底哪一年总决赛最像“Go语言版的代码”
这么说吧,2011年总决赛,就像一段看起来笨拙、但运行稳定、没有空指针、没有内存泄漏的代码。
- 热火:有最好的IDE(教练组),最亮的语法高亮(三巨头),但关键时候跑着跑着就报错——
panic: LeBron James。 - 小牛:每个函数(球员)都不算最牛,但耦合度低,容错高,而且有一个核心函数德克在各种异常输入下依然能返回预期结果。
如果把六场比赛写成main函数,小牛那个就是:
func main() {
for game := 1; game <= 6; game++ {
EveryoneWorkHard()
Dirk(clutch: true)
}
fmt.Println("Championship!")
}
而热火那个,跑了两局之后开始注释掉勒布朗,然后韦德一个人跑到for循环外面去了。
一些杂碎但真实的感触
有一件事我确认了很久才发现:小牛那年总决赛没有一名球员场均得分超过20分(德克26分当然是超过20的——等等,我是不是算错了?我重新查了下数据:德克场均26.0分,特里场均18.0分,巴里亚场均8.8分,哦,德克超过20了,那这条作废,但除了德克,确实没有第二个人场均超过15分,这就很“Go”——没有超标的依赖,每个包都刚好够用。
还有,库班的防守录像分析团队每天看录像到凌晨三点,把热火的每一个高位挡拆切成帧,这种细节用Go写也就是一个时间序列分析的事:time.Now().Sub(start) > 3 * time.Hour。
我想说。那年总决赛最打动我的,不是德克的金鸡独立后仰跳投,不是特里喷气机手势,也不是基德终于拿了冠军。 而是整个小牛队从老板到饮水机管理员,所有人都在做一件事——让系统稳定运行。
就像你在Go里写一个小型Web服务,你不追求它的并发量瞬间破万,不追求接口响应时间低于1毫秒,你只希望它在高负载下不崩,在异常输入下不报错,在关键路径上不panic。
2011年小牛做到了。
服务器就关了。此后十年小牛再也没进过总决赛。 但那一年的代码,至今有人查看log。
本文来自作者[kyadmin]投稿,不代表be365立场,如若转载,请注明出处:http://www.cdscds.cn/nba/960.html
评论列表(4条)
我是be365的签约作者“kyadmin”!
希望本篇文章《用Golang写一篇关于NBA 2011年总决赛的文章,我试了试,结果有点意思》能对你有所帮助!
本站[be365]内容主要涵盖:be365,be365网站,be365网址,ball365体育,Be365排名
本文概览:说实话,我一开始也没想到,编程语言和篮球比赛能有什么交集,但最近我在研究Go语言(就是那个golang),顺手用它写了个小工具,把NBA...