全網首發: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的搶票算法,關於位圖,其實還有不少用途,好比,各類統計、限流、權限控制等。

彤哥收到最新情報:全部的遞歸均可以改寫成非遞歸,怎麼實現呢?有沒有什麼套路呢?下一節,咱們一塊兒來聊下這個話題。

關注公號主「彤哥讀源碼」,解鎖更多源碼、基礎、架構知識。

相關文章
相關標籤/搜索