全网首发:12306抢票算法大曝光?(十张图搞定)

file

前言

本文收录于专辑:http://dwz.win/HjK,点击解锁更多数据结构与算法的知识。算法

你好,我是彤哥,一个天天爬二十六层楼还不忘读源码的硬核男人。数组

相信你们都有过抢票、刷票的经验,每一年年末,这都是一场盛宴。数据结构

然而,你有没有想过12306的抢票算法是怎么实现的呢?架构

没有吧,想过,仍是没有头绪?并发

今天,咱们就来曝光让人又爱又恨的12306是如何实现抢票的。学习

位运算回顾

咱们知道计算机只能识别0和1,要操做这些0和1,只能经过位运算来进行,那么,一共有几种位运算呢?线程

让咱们来回顾一下:code

运算 符号 举例 结果
& 1101 & 0110 0100
| 1101 & 0110 1111
异或 ^ 1101 ^ 0110 1011
取反 ~ 1101 0010
左移 << 1101 << 1 11010
带符号右移(高位补1) >> 1101 >> 1 1110
不带符号右移(高位补0) >>> 1101 >>> 1 0110

以上位运算以Java为例,其余语言中可能没有 >>> 操做。blog

OK,位运算的简单回顾就到这里,还有不懂的同窗能够自行百度一下。递归

位图

虽然大部分语言都有提供位运算,可是,并无提供一种相似于位数组的类型,要使用这些位运算,咱们只能经过数字类型来实现,好比Java中的int/long等类型。

而这些数字类型的数组,咱们通常能够称之为“位图”(BitMap)。

好比,咱们须要使用128位的内存,能够申请包含两个long类型的数组long[] bitmap = new long[2];

不过,位图有什么用呢?

有大用处哦,好比,咱们要统计某个用户一年的活跃度,就可使用位图来实现。

一年有365天,一个long类型能够表示64位,365/64=6,只须要6个long类型就能够记录一个用户一年的活跃状况,怎么记录呢?

很简单,初始时,位图中全部位都是0,当这个用户某天登陆了,就在位图中找到这天,把其位变成1,一年下来,这张位图就记录了这个用户哪些天登陆了,统计这个位图中1的数量,除以365,就获得了他的活跃度。

file

OK,这只是位图的一个很简单的用法,位图还有不少高级的用法,好比统计活跃用户数、限流、权限控制等,固然,还有咱们今天要曝光的12306抢票算法。

12306抢票算法

咱们知道,一列火车,有不少个座位,能够到不少站,以北京到广州的一列火车G67为例:

file

G67次列车一共有18个站,有的人可能到武汉就下车了,有的人可能到长沙下车,还有的人可能从武汉上车从衡山西下车,甚至还有的人从北京一直坐到广州,咱们假设这趟列车一共有200个座位。

那么,如何实现合理的抢票策略,才能保证这趟列车可以坐最多的人?(没有站票)

什么叫作“坐最多的人”呢?假设针对10号位置,一我的从北京到武汉,另外一我的从武汉到长沙,再一我的从长沙到广州,那针对这个位置全程能够坐3我的;针对另外一个位置,一我的从北京到广州,那这个位置全程只能坐一我的。简单点说,就是针对一个特定的位置,两我的之间不能有交集,好比一我的从北京到长沙,另外一我的从武汉到广州,那这两我的不能安排到同一个位置上。

file

OK,先给你一分钟时间思考一下,先别急着往下看哦。


好了,一分钟时间已到,让咱们继续。

首先,让咱们回顾下G67这趟列车的信息:一共18个站,一共200个座位。

为了方便讲解和画图,咱们假设它只有 北京、信阳、武汉、岳阳、长沙、广州 6个站,一共有8个座位。

针对这样的信息,咱们能够这样来实现抢票策略:

  1. 建立5个位图,每一个位图的大小为8位,初始时,每一个位的值都是0。

    为何是5个位图呢?由于到站就下车了,而广州站是终点站,到站所有人都得下车。好比,一我的从北京到武汉,他到武汉就下车了,因此,它不会占用武汉的位置。

file

  1. 把抢票逻辑放在单线程中来处理,单线程的好处是不用考虑并发问题,没有CPU上下文切换的问题等,并且整个操做都是CPU操做,速度很快,使用单线程效率更高。

    固然,咱们还有更好的选择——Redis,Redis自己就是单线程处理的,并且它自然支持BitMap,速度又快又好,有兴趣的同窗能够了解一下Redis中的BitMap。

  2. 假设第一我的的请求过来了,他要抢从北京到武汉的票,此时,咱们只须要把北京和信阳两个位图作“或”运算,结果中,全部0的位置都表示可抢的位置,在这些位置中随机返回一个便可,并把此位置在北京和信阳这两个位图中标记为1,表示此位置在北京和信阳有人占用了。(武汉为何不参与运算了,前面讲过了,这我的到武汉就下车了。)

    假设,此人最后的座位是2号,那么运算以后,各位图的状况以下:

    file

  3. 接着,第二我的的请求过来了,假设他要从信阳到长沙,此时,须要把信阳、武汉和岳阳三个位图作“或”运算:

    假设,此人最后的座位是4号,那么,运算以后,各位图的状况以下:

    file

  4. 而后,第三我的的请求来了,假设他要从北京到广州,此时,把全部5个位图作“或”运算:

    假设,此人最后的座位是1号位,那么,运算以后,各位图的状况以下:

    file

  5. OK,通过了多我的的请求以后,假设位图的状况变成了下面这样:

    file

    请思考,此时,还能抢到从北京到广州的票吗?

    能?不能?回答能的同窗,请从头再看一遍^^

好了,关于抢票算法咱们就介绍到这里,你有没有Get到呢?或者你有没有更好的实现方法呢?

后记

本节,咱们一块儿重温了位运算的操做,并学习了如何使用位图实现12306的抢票算法,关于位图,其实还有不少用途,好比,各类统计、限流、权限控制等。

彤哥收到最新情报:全部的递归均可以改写成非递归,怎么实现呢?有没有什么套路呢?下一节,咱们一块儿来聊下这个话题。

关注公号主“彤哥读源码”,解锁更多源码、基础、架构知识。

相关文章
相关标签/搜索