北京小汽车摇号分析

/文章过时/ :摇号和排队已经变化,本文已经不适用。


北京汽车摇号到底是怎么摇的,很多人只知道自己摇了多长时间,倍数是多少。这里我帮大家看了一下。

摇号规则

1、摇号基数序号分配方法:首先,将当期所有审核通过的编码按从小到大的顺序分配序号;然后,第二阶梯及以上的编码按从小到大的顺序,接在后面继续分配序号;再然后,第三阶梯及以上的编码按从小到大的顺序,接在后面继续分配序号;以此类推。因此,高阶梯的编码所对应的多个摇号基数序号是不连号的,当期摇号基数序号总数=第一阶梯人数+第二阶梯人数×2+第三阶梯人数×3…… 2、摇号方法:摇号程序从当期所有摇号基数序号中随机抽取中签者,高阶梯的编码对应多个摇号基数序号,于是享受了多倍的中签概率。摇号程序确保高阶梯编码的多个摇号基数序号最多只能摇中一个,当其中一个摇号基数序号中签,该编码即中签。

看到上面这一段会有点懵,大部分人会被阶梯和乘数搞混乱。 这里我来帮大家解释一下。

白话解释

首先,每个人在注册的时候会分配一个序号。 假设有 1000 个人摇号,每次摇 50 个人, 大概是像下面

如果按照每人一次的摇号规则,第一次摇号后,列表里会剩下950人。 这个时候又加入了100人。 如果前面950人的摇号倍数增加了一次。列表会变成如下(假设2号李四中号了)。

当然,表里的申请编码只是模拟的,和真实的申请编码无关。

如果所有人倍数都是一样,那么摇号表和上面是一致的。当有人有2倍的话,会把2 倍的人在摇号表追加一次。 真实的摇号表就成了如下:

这里,序号 1 和序号 1051 都是张三。 如果出现3倍,4倍,甚至是10倍,按照同样的方法往后面追加。这样就实现了一个人多倍摇号。

那么在摇号的时候,摇到 1 或者 1051 的话,张三都会中号。 也就是说序号1和1051 对应的申请编码都是 000001 。

这里会出现一个问题,如果在一次摇号中 1 和 1051 都中号了怎么办。在实际的摇号中不会出现这种情况。摇号程序会自动跳过。

这上面的排序方法就是每个月会提前放出来的小客车摇号的摇号池编码文件。 小客车的摇号数据文件可以在北京市小客车摇号网站里下载到

https://apply.bjhjyd.gov.cn/apply/user/person/ballotProgram.do?issueType=PERSON

程序的运行

摇号池编码文件发布后会公布文件 MD5 码

MD5 码是一种消息摘要算法,一种被广泛使用的密码散列函数,可以产生出一个128位 的散列,用于确保信息传输完整一致。

简单的说,MD5 码是利用文件的内容生成的一个值,如果文件中有一个字被修改了,计算出来的MD5 都是不一样的,确保了文件不会被修改。

到这里已经提供了一份不会被修改的列表。接下来就是随机数出场了。每次摇号的时候由公证人员使用随机的方式提供一个6位的随机种子。 这个随机种子是如何工作的呢。

摇号程序是使用 .net 开发的程序,使用了 .Net中Random类生成的随机数。 具体可以查看 :

https://docs.microsoft.com/en-us/dotnet/api/system.random?view=netframework-4.8

如果是.net 的开发者都会知道 random 生成的随机数是伪随机数,就是说在随机种子固定的情况下,生成的随机数是一致的,这也是有同样的摇号池编码文件和一个随机数后,就能够确定所有中号人员名单的原因。

如果对摇号程序有兴趣可以去下载后,使用 ILSpy 来做反编译查看其中的代码。

https://github.com/icsharpcode/ILSpy

摇号程序的逻辑基本就是以上的内容。在计算摇号的时候也完全的按照以上的内容来做的。 这里不贴代码,只说一下代码逻辑。

首先,程序确定了总的摇号数 N ,真实的个人摇号编码是一个13位的数。程序中初始化的时候会实例化一大块内存。长度为 N*13 个字节 。 然后按顺序将所有人的编码写到这块内存中。 类似于如下(忽略空格) 。

1
2
000000000001 000000000002   ..... 00000012345

随机数的生成区间是 1 到 N ,随机种子固定,如果摇到的号位 m ,那么对应的摇号编码的位置是 m*13 开始的字节后的 13 位,就是对应的中号编码。

然后将这中号编码加入到一个字典中。如果这个中号编码在字典中存在,说明已经中号,直接跳过。

程序中几个有意思的地方

  1. 包含有三个入口文件 FromMain.cs FromMainB.cs FromMainC.cs 见证了不同时期不同版本。 FromMain.cs 是没有加倍的入口文件,FromMainC 是现在正在使用的,FromMainB 是中间某个时间的。
  2. 能够容纳的最大抽签人数是 4294967296L ,这个数字大概与全亚洲人口差不多。
  3. 按照程序内存初始化,1G 内存能支持900W人。

如何保证公正

摇号程序是简单粗暴,直接有效。 利用了计算机的随机数是伪随机的方式,来保证了任何计算机在得到数据文件和随机种子后得到的结果都是一致的。

有时候可能会听到找黄牛可以帮摇到号的,有没有这个可能。从数据角度来说是可能的,从实际的角度来说是不可能的。

从前面的内容我们可以知道摇号结果是有两个因素决定的。

  1. 摇号文件
  2. 随机种子

摇号文件提供了一个摇号列表,列表记录了每个人的位置,和摇号总人数。这份文件是提前开放下载。按照顺序排序,位置是由数据库的位置决定,人工无法干预。

那剩下一个变量随机种子,那么我给你一个机会让你自己去填一个随机种子。你赶紧用摇号软件去暴力循环,找到了能够让你自己中号的那个随机种子。这个是可能的。

但是现在要去找随机种子,让100个人都中号,这个是不可能的,随机区间太大,而你并不能控制随机数。 也就是说,即使给你自定义随机种子的权利,你也只能帮个位数的人来摇到号,而无法批量中号。

倍数的欺骗

在所有人都开始摇号后,不管有没有需求,反正先去那里站个坑。当到达一定的时候,倍数会变成一个毫无意义的数字。 如果每个人都开始有倍数后,这个倍数就没有意义了。如果5个人摇号,每个人有1个号,过几个月好这5个人变成了每个人3个号,和拿一个号没有任何的区别。在真实的摇号场景下,每个月都会多出几万甚至是几十万的摇号编码,而真实的中签概率是 2000 分之一。即使是增加了倍数,我们摇到号的概率依然在不断的下降。所以在摇号的绝大部分人都只是陪衬。像笔者这种重来没有中过奖的人,基本与之无缘。

如果我们按每次中号的几率为 2000 分之一,中签率是怎么样的呢?

下图是使用每次中签率 1/2000 计算,按每年 6 次,计算 300 次(50年)绘制的中签概率图:

x轴(年)

y轴(概率)

可以看到 5 年中号的概率是 0.06 ,10年 是 0.11 ,20年 0.21 。

但是实际上每个人会存在倍数,所以看看 2 倍,5 倍和 10 倍的概率图是什么样的。年头太长也没有什么意义,下面只统计 10 年

2倍

2倍10年中号概率0.25左右

5倍

5倍10年中号概率 0.5 左右

10 倍

20190620111846.png

10倍10年中号概率0.7

所以在摇号难度不上升的情况下(怎么可能), 希望还是很大的。

作者

张巍

发布于

2019-06-17

更新于

2019-06-17

许可协议

评论