浅述预约用车

发布于:2024-05-05 ⋅ 阅读:(27) ⋅ 点赞:(0)

背景

网约车这个极大提高人们出行效率的工具,已经进入中国接近20年了。现在人们在手机上打车,已经成为一种常见的出行方式。对于实时用车的场景,人们下完单后希望司机立刻来接自己,并且难以忍受过长的等待时间,所以实时用车时一般就是给用户派送周边的司机,并且剥夺司机的挑单权益,让司机立刻来服务乘客。

而预约用车则不同,乘客只需要感知到有司机接单即可,到了用车时间司机能够及时过来履约即可。所以,针对预约用车的场景,一般都是把订单派发给多个司机,让司机自己选择是否抢单。当出现多个预约用车订单,给每个订单挑选多个接单意愿度最高的司机接单,这就是一个多对多的组合优化问题,如何确保每次整体的预约用车订单,能够最优的给到接单意愿度最高的一批司机,就会涉及到预约用车派单算法。

预约派单算法

先给出结论预约用车的派单算法,使用的是爬山算法。至于为什么要使用爬山算法? 先给出爬山算法的一个基本时间线。

爬山退火算法.jpeg

模拟退火算法介绍

爬山算法其实只是爬山退火算法中的一个部分。

爬山算法是一种贪心搜索算法,它每次从当前解的临近解空间中选择一个最优解作为当前解,直到达到一个局部最优解。这种算法的主要缺点在于,它可能会陷入局部最优解,而无法找到全局最优解。

image.png

这里我们举个一维的例子。假设我们有个函数f(x) 和图上的一样,我们想找到一个X使得f(x) 最大。当然从图上我们一眼就能看出B点事最大值点,对应的X就是使得f(x)值最大的解。同时一般方法是对f(x) 求导数使得导数等于0时候的x值,这里的x值可能有一个解也可能有多个解,一个解时就是我们要求的,多个解时我们就要比较不同解那个解更加符合我们的要求,我们就选取哪一个。那么爬山算法是怎么运作的呢?

我们首先随机选取一个点,也就是图中所对应的那个点,然后分别计算x为x+delta和x-delta时的f(x)的值,经过计算我们得到当x = x1+delta时,其f(x)大于x1以及x1-delta,所以我们将原来的x1换成x1+delta,在进行上面的计算,所以后面我们会得到x2,x3,x4....直到f(xn)均大于f(xn-delta)和f(xn+delta) ,也就是说在xn这个点上,其周围得到的f(x) 都小于f(xn),我们就说这个解是局部最大解了。关于他的缺点也是很明显,只能找到局部的最优解,小概率找到全局的最优解(和你的初始的随机位置有关系)。

模拟退火算法则是对爬山算法的一种改进。它来源于金属冶金中的先加热再降温的过程,模拟了热力学系统中的退火过程。在模拟退火算法中,每次找到一个临近空间的最优解后,会给予一定的概率跳出这个最优解,从而有可能跳出局部最优解,达到全局最优解。这个概率会随着时间的增加而递减,以保证最后的稳定性。

旅行商问题

为什么会出现模拟退火算法,开始是为了解决旅行商问题。旅行商问题(Travelling Salesman Problem,TSP)是一个经典的组合优化问题,也是运筹学和理论计算机科学中的重要问题。该问题可以描述为:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。从图论的角度来看,该问题实质是在一个带权完全无向图中,寻找一个权值最小的Hamilton回路。

旅行商问题是一个NP困难问题,意味着目前没有找到多项式级别的算法来解决它,只有指数级别的算法。随着城市数量的增加,问题的可行解(即所有顶点的全排列)数量会急剧增长,导致计算复杂度迅速上升,产生所谓的“组合爆炸”。

预约单匹配问题

预约单匹配问题和旅行商相似。

旅行商问题如果使用穷举的方式,把所有的情况都列举一遍,然后选择最优的,如果城市个数为N,这个算法的时间复杂度为N!。

预约单匹配问题采用穷举的方式也一样,如果预约单的个数为N,每个预约单可以匹配的司机数是M,这个算法的时间复杂度为: C N M C_{N}^{M}

对于一个派单系统,如果预约单量一旦过大,这个算法的时间复杂度成指数级增长,会拖慢整个派单系统,从而影响整个系统的稳定性。

爬山算法

预约用车可以采用爬山算法的原因是:

  1. 爬山算法可以得到一个局部最优解,并且整体耗时不多,对于工程后续的维护和开发效率,都相对友好。
  2. 预约用车,乘客可以接受一个相对较长的时间,可以有更多的时间挑选司机,也就不需要立刻找到最合适的司机。
  3. 在正常情况下,预约用车数量不多时,局部最优解其实也就是全局最优解。对比上面爬山算法的示图,如果函数只有一个最高峰,那你局部找到的那个最高峰就是全局的最高峰。

伪代码示例

场景设置:比如有2个用户预约用车,2个用户都匹配了3个一样的司机,3个司机抢2个订单的意愿度也不同,如何分配3个司机给这2个用户,让整体订单的接起概率更大。总体示图如下:

image.png

  • 司机1:接用户1的意愿度是 0.3, 接用户2的意愿度是 0.6
  • 司机2:接用户1的意愿度是 0.5, 接用户2的意愿度是 0.8
  • 司机3:接用户1的意愿度是 0, 接用户2的意愿度是 0.4
1,先让每个司机都去接意愿度最大的订单。则得到如下结果:
    (司机1 、司机2、 司机3) -> 用户2
    
2,计算每个订单不被接单概率:
     p(order1) = 1, (没有匹配司机,所有肯定不被接单)
     p(order2) = (1 - 0.6)*(1 - 0.8)*(1 - 0.4) = 0.048
     
3,基于12的方式,用户2大概率是会被司机接单,用户1完全没有机会,这样整体不是最优,需要进行调整(类似爬山)。整体是否需要调整判定条件:
    3.1、调整次数是否达到上限。(防止调整耗时过长)
    3.2、调整前和调整后,整体订单被接单的期望值差值小于一定阈值,即可停止调整,因为调整的价值不大了
    E(整体订单被接单的期望值) = (( 1 - 1) + ( 1 - 0.048))/2 = 0.476
    
4, 每个订单是否需要调整的判定条件:
        如果订单被接单的概率 < E, 则进行调整;
        否则,不需要调整。
        类似于贪心算法,对于上面的分配就是用户1需要调整,用户2不需要调整。

5, 对需要调整的订单进行如下处理:
    5.1、遍历需要调整订单,可以匹配的司机列表。比如:用户1的订单,就是司机1和司机25.2、对于司机1,接用户1的意愿度是0.3, 接用户2的意愿度是 0.6;判定调整前后整体是否更优,更优则调整,否则不调。
        调整前: E(整体订单被接单的期望值) = (( 1 - 1) + ( 1 - 0.048))/2 = 0.476
        调整后: 就是司机1从接用户2,调整为接用户1。此时每个订单不被接单概率如下:
            p(order1) = 1 - 0.3 = 0.7
            p(order2) = (1 - 0.8)*(1 - 0.4) = 0.12
            E(整体订单被接单的期望值) = (( 1 - 0.7) + ( 1 - 0.12))/2 = 0.59 > 0.476
        说明调整后, 整体订单被接单的期望值更高,订单更容易被接起。
        
  • 第一步: 确定爬山的初始位置。
  • 第二步: 基于初始位置,确定每个订单的初始条件信息。
  • 第三步: 判定整体是否需要进行调整。
  • 第四步: 判定每个订单是否需要进行调整。
  • 第五步: 判定每个订单能够匹配的司机是否需要进行调整。

3、4、5会循环多次判定,直到满足设定目标。

总结

爬山算法来源于对旅行商问题的求解,那么对于旅行商问题的解法肯定不止一种算法。

旅行商问题在交通运输、电路板线路设计以及物流配送等领域有着广泛的应用,国内外学者对其进行了大量的研究。早期的研究者使用精确算法求解该问题,如分枝定界法、线性规划法、动态规划法等。然而,随着问题规模的增大,精确算法变得无能为力。因此,后来的研究重点转向了近似算法或启发式算法,如遗传算法、模拟退火法、蚁群算法、禁忌搜索算法、贪婪算法和神经网络等。

  • 遗传算法(Genetic Algorithm,GA)最早是由美国的John Holland于20世纪70年代提出,它是根据大自然中生物体进化规律而设计的一种优化算法。遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。
  • 蚁群算法(Ant Colony Optimization, ACO)是一种模拟自然界中蚂蚁觅食行为的优化算法。该算法由Marco Dorigo于1992年在他的博士论文中提出,其灵感来源于蚂蚁在寻找食物过程中发现路径的行为。
  • 禁忌搜索(Tabu Search,TS)算法是一种元启发式(meta-heuristic)随机搜索算法,旨在找到全局最优解。它从一个初始可行解出发,选择一系列的特定搜索方向(移动)作为试探,选择实现让特定的目标函数值变化最多的移动。为了避免陷入局部最优解,TS搜索中采用了一种灵活的“记忆”技术,这就是Tabu表的建立。

作为一个工程师,我们很多时候采用一些方法解决了一些业务问题,就以为搞明白了事情的因果来源。其实要去深究一件事情,从研究的角度可以进入底层深入探索,从业务的角度可以往上找寻商业价值。一个是往下深入原理,一个是往上贴近商业价值,两个方向都需要深入去思考,比简简单单做完一个事情,要思考和成长的都更多。