电梯算法求解最小距离

8
我有一个只有一部电梯的大楼,需要为此电梯找到一种算法。我们会得到这样一组对象列表:{i->j},其中i是居民要从哪一层楼搭乘电梯,j是他想要下电梯的楼层。
无限数量的人可以同时使用电梯,并且不管人们在电梯里停留多长时间都不相关。电梯从第一层开始。
我在网上查了一下,发现了“电梯算法”,但它并没有真正帮助到我。它说我应该一路向上,然后一路向下。但是请考虑当一个居民要从1层到100层,另一个居民要从50层到49层时。如果使用上述算法,则需要经过151层。如果我遵循以下路线:1->50->49->100,那么只需要102层,更好。
那么我应该使用什么算法呢?

对于两个项目,您的方法更有效,但我认为对于需要运输更多人的较大列表,网络上的算法最好。 - SamV
我需要一个适用于任何情况的好算法。 - user76508
2
所以你想要最小化电梯的运行路程?无论居民等待多长时间、在电梯内停留多长时间都是无关紧要的,而且你假设无限数量的人可以同时使用电梯吗? - Bergi
@Dukeling 它从第一层开始。 - user76508
完成时考虑最后回到一楼是否重要? - גלעד ברקן
显示剩余7条评论
4个回答

2

以下是一种将此问题制定为基于时间的整数规划的方法。(生成所有约束可能看起来有些过度,但它保证能产生最优解)

假设电梯从楼层FF+1F-1需要1个时间单位。

另一个关键点:在任何时刻t,只需做出一项决策,即是向上还是向下移动。这是我们问题的决策变量。如果电梯在t时刻向上运动,则DIR_t = +1,否则为-1。

我们希望尽可能缩短所有乘客到达目的地所需的时间。

下表可以更清晰地说明问题:

Time    FLOOR_t   Dir_t
1        1          1
2       2           1
3       3           1
4       4           1
...    ...          ...
49      49          1
50      50          -1
51      49          1
52      50          1
...     
100     99          1
101     100         NA

现在,让我们带乘客进来。有 P 名乘客,每个人都想从SF到达EF(他们的起始楼层和目标楼层)。因此,对于每个乘客p,我们都知道他们的起点和终点:(SF_p, EF_p)

约束条件

我们知道电梯在时间 t 时所在的楼层是:
 F_t = F_t-1 + DIR_t-1

(F0 = 0,DIR_0 = 1,F1 = 1只是为了开始事情。)
现在,让ST_p成为乘客p开始他们的电梯旅程的时间点。让ET_p成为乘客p结束他们的电梯旅程的时间点。 请注意,SFEF是给我们的输入参数,但STET变量,由IP在解决时设置。也就是说,楼层已经给出,我们必须想出时间。
   ST_p = t if F_t = SF_p  # whenever the elevator comes to a passenger's starting floor, their journey starts.       
   ET_p = t if F_t = EF_p AND ST_p > 0 (a passenger cannot end their journey before it commenced.)
   This can be enforced by introducing new 0/1 indicator variables.

   ETp > STp # you can only get off after you got on
   

最后,让我们介绍一个数字T,它是整个行程集完成的时间。它是每个p的所有ET中的最大值。这就是需要最小化的内容。

   T > ET_p for all p # we want to find the time when the last passenger gets off.

制定方案

将所有内容整合在一起:

   Min T
   
   T > ET_p for all p
   F_t = F_t-1 + DIR_t-1
   ETp > STp # you can only get off after you got on 
   ST_p = t if F_t = SF_p  # whenever the elevator some to a passenger's starting floor, their journey starts.
   ET_p = t if F_t = EF_p AND ST_p > 0
   ET_p >= 1 #everyone should end their journey. Otherwise model will give 0 as the obj function value.
   DIR_t = (+1, -1) # can be enforced with 2 binary variables if needed.

现在解决了IP问题后,可以使用每个tDIR_t值来准确追踪行程。


1
有一个多项式时间的动态规划程序,其运行时间不取决于楼层数。如果我们贪心地接载乘客并让他们等待,那么相关状态就是电梯已经到达的楼层间隔(因此已接载的乘客),电梯最近一次接载或卸载的楼层,以及两个可选值:为了当前内部乘客的卸客目的而必须访问的最低楼层和最高楼层。所有这些状态都可以通过五个乘客的身份和一定数量的比特位来描述。
我相信这里还有改进的空间。

0
在C.B.的回答评论中,OP评论说:“请求是静态的。一开始我就得到了完整的列表。”如果我们提前获得所有旅行计划,我会欢迎反例和/或其他反馈,因为我认为如果我们考虑以下内容,问题可以大大减少:
  1. 由于电梯容量无限,任何向上的旅行终点低于我们将要访问的最高楼层的旅行都与我们的计算无关。由于我们保证经过所有这些接送点,我们可以在考虑下降旅行后将它们安排在我们的时间表中。

  2. 任何包含在同方向的其他旅行中的旅行也是无关紧要的,因为我们将在“最外层”旅行期间通过那些接送点,并且可以在考虑这些旅行后适当地安排它们。

  3. 任何重叠的下降旅行都可以合并,原因很快就会显现。

  4. 任何下降旅行都发生在达到最高点之前或之后(不包括达到最高楼层的接载)。我们确定发生在最高点之前的所有下降旅行(仅考虑“外部容器”类型和两个或更多重叠的旅行作为一个旅行)的最佳时间表是在我们上升时逐个进行,因为我们无论如何都要上升。

如何确定最高点后应该发生哪些下降行程?

我们将计算基准点 TOP 作为参考。我们称包含达到的最高楼层的行程为 H,最高楼层到达 HFR。如果 HFR 是上车点,则 H 是下降的,TOP = H_dropoff。如果 HFR 是下车点,则 H 是上升的,TOP = HFR

应该在访问最高楼层之后安排的下降行程是从 TOP 下一个更低的下降行程开始向下继续,其中它们的组合距离加倍大于从 TOP 到它们最后一个下车点的总距离的相邻下降行程的最大组合(仅考虑“外部容器”类型和两个或多个重叠的行程作为一个行程)。也就是说,当 (D1 + D2 + D3...+ Dn) * 2 > TOP - Dn_dropoff 时。

以下是在 Haskell 中的粗略尝试:

import Data.List (sort,sortBy)

trips = [(101,100),(50,49),(25,19),(99,97),(95,93),(30,20),(35,70),(28,25)]

isDescending (a,a') = a > a'

areDescending a b = isDescending a && isDescending b

isContained aa@(a,a') bb@(b,b') = areDescending aa bb && a < b && a' > b'

extends aa@(a,a') bb@(b,b') = areDescending aa bb && a <= b && a > b' && a' < b'

max' aa@(a,a') bb@(b,b') = if (maximum [b,a,a'] == b) || (maximum [b',a,a'] == b')
                              then bb
                              else aa

(outerDescents,innerDescents,ascents,topTrip) = foldr f ([],[],[],(0,0)) trips where
  f trip (outerDescents,innerDescents,ascents,topTrip) = g outerDescents trip ([],innerDescents,ascents,topTrip) where
    g [] trip (outerDescents,innerDescents,ascents,topTrip) = (trip:outerDescents,innerDescents,ascents,max' trip topTrip)
    g (descent:descents) trip (outerDescents,innerDescents,ascents,topTrip)
      | not (isDescending trip) = (outerDescents ++ (descent:descents),innerDescents,trip:ascents,max' trip topTrip)
      | isContained trip descent = (outerDescents ++ (descent:descents),trip:innerDescents,ascents,topTrip)
      | isContained descent trip = (trip:outerDescents ++ descents,descent:innerDescents,ascents,max' trip topTrip)
      | extends trip descent = ((d,t'):outerDescents ++ descents,(t,d'):innerDescents,ascents,max' topTrip (d,t'))
      | extends descent trip = ((t,d'):outerDescents ++ descents,(d,t'):innerDescents,ascents,max' topTrip (t,d'))
      | otherwise = g descents trip (descent:outerDescents,innerDescents,ascents,topTrip)
     where (t,t') = trip
           (d,d') = descent

top = snd topTrip

scheduleFirst descents = (sum $ map (\(from,to) -> 2 * (from - to)) descents)
                       > top - (snd . last) descents

(descentsScheduledFirst,descentsScheduledAfterTop) =
  (descentsScheduledFirst,descentsScheduledAfterTop) where
    descentsScheduledAfterTop = (\x -> if not (null x) then head x else [])
                           . take 1 . filter scheduleFirst
                           $ foldl (\accum num -> take num sorted : accum) [] [1..length sorted]
    sorted = sortBy(\a b -> compare b a) outerDescents
    descentsScheduledFirst = if null descentsScheduledAfterTop
                                 then sorted
                                 else drop (length descentsScheduledAfterTop) sorted

scheduled = ((>>= \(a,b) -> [a,b]) $ sort descentsScheduledFirst) 
         ++ (if isDescending topTrip then [] else [top])
         ++ ((>>= \(a,b) -> [a,b]) $ sortBy (\a b -> compare b a) descentsScheduledAfterTop)

place _      []             _    _     = error "topTrip was not calculated."
place floor' (floor:floors) prev (accum,numStops)
  | floor' == prev || floor' == floor = (accum ++ [prev] ++ (floor:floors),numStops)
  | prev == floor                     = place floor' floors floor (accum,numStops)
  | prev < floor                      = f
  | prev > floor                      = g
 where f | floor' > prev && floor' < floor = (accum ++ [prev] ++ (floor':floor:floors),numStops)
         | otherwise                       = place floor' floors floor (accum ++ [prev],numStops + 1)
       g | floor' < prev && floor' > floor = (accum ++ [prev] ++ (floor':floor:floors),numStops)
         | otherwise                       = place floor' floors floor (accum ++ [prev],numStops + 1)

schedule trip@(from,to) floors = take num floors' ++ fst placeTo
  where placeFrom@(floors',num) = place from floors 1 ([],1)
        trimmed = drop num floors'
        placeTo = place to (tail trimmed) (head trimmed) ([],1)

solution = foldl (\trips trip -> schedule trip trips) scheduled (innerDescents ++ ascents) 

main = do print trips
          print solution

输出:

*Main> main
[(101,100),(50,49),(25,19),(99,97),(95,93),(30,20),(35,70),(28,25)]
[1,25,28,30,25,20,19,35,50,49,70,101,100,99,97,95,93]

0

你的问题类似于磁盘头调度算法。

可以查看最短寻道时间优先扫描、循环扫描等的比较。

有些情况下,最短寻道时间优先会胜出,但如果是50到10,同时还有2到100、3到100、4到100、5到100、6到100等请求,你会发现这会给其他人增加额外开销。此外,如果新请求的寻道时间更短,可能会出现饥饿(类似于进程调度)。

在你的情况下,真正取决于请求是静态还是动态。如果想要最小化方差,就选择扫描/循环扫描等。


请求是静态的。一开始我会获取完整列表。 - user76508
似乎不同的原因是您每个请求有两个点,即入口和出口。对于最短寻道时间,您只有一个请求列表,如果我理解正确的话。 - Niklas B.
@NiklasB。是的,你似乎是对的。如果他事先知道他将在50楼接用户1,那么这很容易成为一个图问题,就像你在评论中指出的那样。 - C.B.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接