淺談滴滴派單演算法
1.
為什麼我們需要更好的派單演算法
說到滴滴的派單演算法,大家可能感覺到既神秘又好奇,從揚召到搶單到派單,我們又是如何演進到今天大家的叫車體驗的呢,我們首先來看一看,好的派單演算法為什麼是出行行業不可或缺的能力?
回想幾年前,當我們還沒有滴滴的時候,只能在寒風或者酷暑中等待可能有、可能沒有的揚招計程車,到後來可以從滴滴上呼叫一輛計程車,乘客可以在室內相對舒適的等待車輛的到達,從線上到線下,乘客的確定性得到第一次的提升,然而這還不夠,搶單的模式註定我們的應答率天花板不會太高,在15年,滴滴上線快車業務,我們從搶單演進到了派單模式,乘客的應答率有了20個點以上的提升,很多時候能夠全天能夠高達90+(高峰&區域性供需緊張應答率會相對吃緊),乘客確定性再一次得到大幅的提升,由此可見,派單模式為滴滴創造了巨大使用者價值。
再看近年來不斷興起的O2O業務,從國內外的網約車公司,包括我們的友商Uber、Lyft都基於派單的產品形態進行司機和乘客之間的交易撮合,Uber上市的時候把派單引擎也作為核心技術能力放在了招股書中;再看我們的國內的外賣平臺,核心派單系統的優劣也決定了整個平臺的交易效率(單均配送成本)和使用者體驗(配送時長);最後,整個大物流行業近年來也不斷在進行線上化的改造,如何撮合貨物和司機,以及更好的拼單能力也是整個交易環節的關鍵和商業模式是否成立的前提。從運人到運物,派單引擎目前越來越多的被應用在現實的商業和生活中。
2.
派單問題初探
言歸正傳,這裡我們也來看一下,滴滴網約車平臺到底是怎麼派單的。首先,我們來看下我們面對的是什麼樣的問題?
“訂單分配 即是在派單系統中將 乘客發出的訂單 分配給 線上司機 的過程”
這是一個看似簡單的,但實際上非常複雜的問題。說到這,可能有很多人就會問,能否就把 我的訂單分配給離我最近的司機就好了?
的確啊,實際上目前滴滴的派單演算法最大的原則就是 “就近分配” (70%~80%的訂單就是分配給了最近的司機),據我所知,目前世界上其他的競品公司(包括Uber),也均是基於這個原則分單的。
我們進一步來看這個問題,如果我們只按照就近分配,先到先得的貪心策略,是不是能最好的滿足平臺所有乘客和司機的訴求呢?答案是否定的,原因就在於,如果我們只基於當前時刻和當前區域性的訂單來進行決策,忽視了未來新的訂單&司機的變化,還忽視了和你相鄰的其他區域甚至整個城市的需求(注:在時序上來看,新的司機&訂單的出現會導致,貪心策略反而違背了就近分配的目標)。這就是為什麼這個問題依然是非常複雜的原因。
這裡稍微有點抽象了,不過沒關係,我們再來一步一步的拆解一下訂單分配的問題,讓大家有個更好的理解:
簡單看,在我們的平臺上,每一個時刻,都有N個訂單在被乘客建立,同時有M個司機可以被我們用來進行分配,我們強大的平臺能夠為派單演算法給出司機的實時的地理位置座標,以及所有訂單的起終點位置,並且告訴我們每一個司機接到訂單的實時導航距離。
▍如果是1個訂單、1個司機
看上去似乎就非常簡單了,我們直接把這個訂單指派給這個司機就好了嘛。
“那麼為什麼有時候附近有輛空車卻不能指派給你呢?”
實際線上的系統會比這裡稍微複雜一點,原因一方面有可能是司機正好網路出現故障,或者正在和客服溝通等等導致司機無法聽單,另一方面的原因是並不是所有的車都能夠符合服務你訂單的要求,最基本的策略其實是人工設定的規則過濾。舉幾個最基礎的例子:
規則A:快車司機不能接專車訂單 規則B:保證司機接單後不會透過限行限號區域 規則C:為設定實時目的地的司機過濾不順路區域 規則D:為只聽預約單司機過濾實時訂單 規則E:同一個訂單隻會發給一個司機一次 ......
必須澄清的一點是這裡的規則並不會造成分單時不公平的效果,而完全是為了業務能正常執行而設立的,這些策略承擔著保證業務正確性的重要職責。
▍如果是1個訂單和2個司機
假設這兩個司機都能夠分配給這個訂單,那麼我們來看系統應該是如何分配的。
首先第一種情況是,同一時刻下,這兩個司機和訂單的距離都完全一樣的情況下,系統應該如何分配?
剛才也說到,我們平臺訂單分配最大的原則是就近分配,當距離完全一樣的情況下,當前我們系統上會主要考慮司機的服務分的優劣,服務分較高的司機會獲取到這個訂單(注:服務分對分單的影響,簡單的理解可以換算為多少分可以換成多少米距離的優勢,這塊不是今天的重點就不展開介紹),再說明一下,系統用到的是地圖的導航距離,而非人直觀看到的直線距離,有時候差一個路口就會因為需要掉頭導致距離差異很大;並且如果司機的定位出現問題,也會出現分單過遠的情況。
那麼我們來看第二種情況,如果A司機離的近,B司機離的遠,系統怎麼派?
這就簡單了,根據就近分配的原則,我們會把A司機分配給這個訂單。嘿嘿~~,假設我們再把問題設定的更加實際一點,當訂單發出時,B司機已經線上並空閒,但是A司機還沒有出現(沒有上線,或者還在送乘客),但再過1s,離得更近的A司機突然出現可被分單了,假設我們使用先到先得的貪心策略,那麼B司機就會被分給這個訂單,那就違揹我們希望就近分單的目標了:(。所以看上去簡單,但實際情況下,演算法還需要變的更好一些,這個問題我們把它叫做派單中的時序問題,我們後面再來看怎麼解決。
▍如果有N個乘客、M個司機
最後我們來考慮最複雜的多對多的情況,這也是線上系統每天高峰期都需要面對的挑戰,我們一般把這種情況會形式化為一個二部圖的匹配問題,在運籌領域也叫做matching的問題,如圖所示:
我們再把這個問題具象一點,假設這個時候我們有20個乘客,有20個司機,這些乘客都可以被這20個司機中的一個接駕,我們的系統需要把這20個乘客都分配出去,並且讓大家的總體接駕的時長最短。聽上去是不是有點複雜?我們套用下組合數學的知識,這其中可能的解法存在20的階乘那麼多,20的階乘是什麼概念呢?20*19*18*…*1= 2432902008176640000,這個數巨大無比,想要完全的暴力搜尋是絕對不可能的。這裡需要更聰明的辦法。
▍如果有N個乘客、M個司機,一會再來幾個乘客和司機?
這就是派單問題最大的挑戰,我們不僅僅需要當前這個時刻的最優,我們要考慮未來一段時間整體的最優,新來的司機和乘客會在整個分配的網路中實時插入新的節點,如何更好的進行分配也就發生了新的變化,所以如何考慮時序對我們非常重要,這個問題在業內也被稱為Dynamic VRP問題,這個Dynamic也就是隨時間時序變化的意思,這也就是為什麼,滴滴的派單問題遠複雜於物流行業的相對靜態的貨物和路線的規劃問題。假設我們知道了未來供需的完全真實的變化,模擬告訴我們,我們的系統有可能可以利用同樣的運力完成1.2~1.5倍的需求量,這也是派單演算法的同學持續為之努力的方向。
想起前段時間的吐槽大會,大家提到文嵩曾說我們的派單問題比alpha go還要難,其實這兩個問題還確實有點相似,都是在超大的搜尋空間中找到一個近似最優的解,而alpha go則會在一個更加明確的遊戲規則和環境中進行求解,它的難點在於博弈,而我們的派單問題難點在於未來供需不確定性&使用者行為的不確定性。近年來在學界,已經有不少嘗試在利用類似alpha go的技術進行VRP&TSP等方向的探索,強化學習結合運籌理論是未來運籌界非常前沿的方向之一(非技術同學可以跳過此處:))
3.
派單演算法簡介
上面我們已經描述了什麼是訂單分配問題,並且它所面臨的各種挑戰,那麼在這裡我們來聊一聊我們線上的派單策略是如何解決其中一部分問題的。
在介紹具體策略之前,首先我們來說一下派單演算法大的原則,目前派單策略主要的原則是:站在全域性視角,儘量去滿足儘可能多的出行需求,保證乘客的每一個叫車需求都可以更快更確定的被滿足,並同時盡力去提升每一個司機的接單效率,讓總的接駕距離和時間最短。
如何理解這個原則呢?我們說策略會站在全域性的角度去達成全域性最優,這樣對於每一個獨立的需求來看,派單可能就不是“區域性最優 ”,不過可以告訴大家的是,就算在這個策略下,仍然有70%~80%的需求也是符合當前距離最近的貪心派單結果的。
接下來,這裡會拿兩個重要的派單策略的來進行介紹。(這裡的內容主要是講清楚策略的motivation為主哈,細節不再展開)
▍批次匹配(全域性最優)
派單策略中最為基礎的部分,就是為了解決上一節所提到的時序問題。這個演算法幾乎是所有類似派單系統為了解決這個問題的最基礎模型,在Uber叫做Batching Matching,我們內部也叫做“全域性最優” 或者 “延遲集中分單”。
這個Idea其實也非常直觀,由於使用者訂單的產生和司機的出現往往並不在同一時間點,在時間維度上貪婪的分單方式(即每個訂單出現時即選擇附近最近的司機派單)並不能獲得全域性最優的效果。一個自然的想法就是先讓乘客和司機稍等一會,待收集了一段時間的訂單和司機資訊後,再集中分配。這樣,有了相對較多、較密集的訂單、司機後,派單策略即可找到更近更合理的派單方式了。
找尋司機和訂單分配的全域性最優是一個 二分圖匹配問題 (bipartite graph matching) ,一邊是乘客、一邊是司機,可用運籌最佳化中各種解決Matching問題的方法進行求解。
和再大家澄清一下,我們所採用的批次匹配的模式和大家所希望的,“把離我最近的司機派給我”的「就近派單模式」並不矛盾,我們也是尋求“乘客接駕時長最短”的最優解,大多數情況下也是指派離你最近的司機,但充分滿足每一個乘客的“把離我最近的司機派給我”的個體需求, 有些時候反而會導致部分乘客的需求無法得到滿足,比如說下面這種情況:
當編號1和2兩個乘客同時叫車, 如果完全按照“就近派單”的模式, 雖然可以讓1號乘客先被接單, 但是2號乘客會因為接駕距離較遠, 導致等待時間變長, 甚至因為最近的司機超出平臺派單距離, 導致2號乘客叫不到車。1、2號乘客總等待時長15分鐘, 平均等待時長7.5分鐘。
我們採取的做法是, 把距離較遠的2號車派給1號乘客。
把1號車派給2號乘客, 這樣一來, 1號乘客和2號乘客, 平均等待時長縮短為5分鐘, 比就近派單,縮短了2.5分鐘, 總等待時長縮短為10分鐘, 比就近派單, 縮短了足足5分鐘。
透過提升全域性的效率,才能轉化為讓更多乘客的需求得到滿足。
▍基於供需預測的分單
“如果有先知告訴我們未來每一個訂單的生成時間&地點,每一個司機的上線時間&地點,派單就會變成非常輕鬆的一件事”
剛才所說的批次匹配的方法,理論上能夠保證那一個批次的匹配是最優的。但是這樣就夠了嗎?
很遺憾,以上所述的延遲集中分單的策略只能解決部分的問題,仍不是一個完全的方案。其最大的問題,在於使用者對系統派單的 響應時間 容忍度有限,很多情況下短短的幾秒鐘即會使使用者對平臺喪失信心,從而取消訂單。故實際線上我們只累積了幾秒鐘的訂單和司機資訊進行集中分單,而這在大局上來說仍可近似看做時間維度上的貪婪策略。
若想即時的獲得最優派單結果,唯一的方法是利用對未來的預測,即進行基於供需預測的分單。這種想法說來玄妙,其實核心內容也很簡單:如果我們預測出未來一個區域更有可能有更多的訂單/司機,那麼匹配的時候就讓這個區域的司機/訂單更多去等待匹配這同一個區域的訂單/司機。
▍連環派單
基於供需預測的分單有很大意義,但由於預測的不確定性,其實際效果很難得到保證。為此,我們使用了一種更有確定性的預測方式來進行派單,即 連環派單。
“連環派單,即將訂單指派給 即將結束服務 的司機,條件為如果司機的終點與訂單位置很相近”
與預測訂單的分佈相反,連環派單預測的是下一時刻空閒司機的所在位置。由於高峰期空閒司機多為司機完成訂單後轉換而來,預測司機的位置就變成了一個相對確定性的問題,即監測司機到目的地的距離和時間。當服務中的司機距終點很近,且終點離乘客新產生的訂單也很近時,便會命中連環派單邏輯。司機在結束上一單服務後,會立刻進入新訂單的接單過程中,有效地壓縮了訂單的應答時間、以及司機的接單距離。
▍如何做的更好
整個派單演算法核心克服的是未來供需的不確定性,動態的時空結構的建模,以及使用者行為的不確定性,對於這些不確定性我們現在更多采用深度學習方法對我們的時空資料&使用者行為進行建模預測。
另外,我們的問題相對於傳統推薦搜尋領域,多了一層匹配決策,我們到底積攢多久的訂單進行分配,對於每一個分配來說我們都面臨著分或者不分,現在分還是未來分配,並且分給誰的問題,這個問題天生就可以建模為強化學習問題,目前在我們的系統中也引入了強化學習方法來最佳化更長期的收益。
除了不斷去最佳化之前說到的派單問題,整個派單系統還面臨著大量其他的挑戰,包括如何利用快車優享等多個品類的運力進行跨層的最優分配,如何同時對使用者&司機&平臺短期長期等多個目標進行最佳化,如何同時最佳化預約&實時訂單,如何在具備網路效應的場景下對演算法進行評估,如果建立一個較為精準的模擬系統等等,這裡既是挑戰,也是AI For Transportation中大量新的重新定義問題和創新演算法的機會。
4.
總結
每天, 我們的派單系統要面對超過3000萬使用者的叫車需求, 高峰期每分鐘接收超過6萬乘車需求,平均每兩秒就需要匹配幾百到上千的乘客和司機 。我們當前的派單策略相對於最初的派單策略版本,每天能夠多滿足百萬以上乘客的出行需求。為了讓更多人能更快、更確定的打到車,我們的交易策略團隊將在更好的公平感知的前提下,不斷地最佳化和打磨我們的派單演算法,為乘客&司機創造更多價值。
當然當前的派單策略還有很多不夠完善和完備的地方,本身也是一個相當複雜的問題和系統,一方面藉此機會讓大家對派單有更好的理解和認識,另一方面,也更歡迎大家對我們提出更多的寶貴意見,幫助我們進一步成長。
最後,滴滴技術祝大家中秋節快樂!
本文作者 ▬ 王犇 滴滴 | 首席演算法工程師
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69908606/viewspace-2657171/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 淺談單調棧
- 淺談滴滴需求響應式公交背後的技術
- 淺談國密演算法演算法
- 淺談遞迴演算法遞迴演算法
- 淺談垃圾回收演算法演算法
- 淺談指數退避演算法演算法
- 淺談表單設計
- 深度聚類演算法淺談聚類演算法
- 淺談 SKU、SPU 和單品
- 淺談前端單元測試前端
- 單頁應用SEO淺談
- 淺談擴充套件歐幾里得演算法套件演算法
- AI演算法測試之淺談AI演算法
- 淺談設計模式——單例模式設計模式單例
- (轉)採購訂單接收淺談
- 淺談 CAP 和 Paxos 共識演算法演算法
- 喚醒玩家的少女心——淺談Galgame與萌系文化的點點滴滴GAM
- 單頁應用SEO淺談(轉載)
- 淺淺談ReduxRedux
- 淺談負載均衡演算法與實現負載演算法
- 淺談iOS中常用加密演算法的使用iOS加密演算法
- 淺談.Net Core後端單元測試後端
- 淺談 Node.js 單執行緒模型Node.js執行緒模型
- 簡單就是美!淺談java各層框架。Java框架
- 淺談網路爬蟲中深度優先演算法和簡單程式碼實現爬蟲演算法
- 【演算法學習筆記】淺談懸線法演算法筆記
- 淺淺淺談JavaScript作用域JavaScript
- 淺談演算法和資料結構(2):基本排序演算法演算法資料結構排序
- 淺談bootstrap表單驗證外掛BootstrapValidatorboot
- 淺談Javascript單執行緒和事件迴圈JavaScript執行緒事件
- 滴滴 曾奇:談談我所認識的分散式鎖分散式
- Celery淺談
- 淺談flutterFlutter
- 淺談JMM
- 淺談反射反射
- 淺談mockMock
- 淺談SYNPROXY
- 淺談Disruptor