zoukankan      html  css  js  c++  java
  • 用erlang求解经典数学问题(2)电梯调度问题

    【题目】

        假设电梯只在某一楼层停,然后乘客下电梯步行至各自目的楼层,求电梯应停在哪一层能使乘客步行的楼层数和最少。

    【问题分析】

    总论:

        采用归纳总结的方法,先分析简单的情况,总结出规律,再推演出算法。

    步骤1:分析题目中的输入、输出

        假设楼层有7层,在2至7层下电梯的乘客数量分别为a、b、c、d、e、f,电梯停靠在x层,则所有乘客步行的总层数
            y = |x-2|*a + |x-3|*b + |x-4|*c + |x-5|*d + |x-6|*e + |x-7|*f
        此函数是一个区间段函数,在相邻整数区间段内事一条斜率为正或为负的线段

    步骤2:分析区间段

        下面对各个区间段进行分析
        当2=<x<3,y = (x-2)*a - (x-3)*b - (x-4)*c - (x-5)*d - (x-6)*e - (x-7)*f        (1)
                            = (a-(b+c+d+e+f))*x + (3b+4c+5d+6e+7f) - 2a
        当3=<x<4,y = (x-2)*a + (x-3)*b - (x-4)*c - (x-5)*d - (x-6)*e - (x-7)*f        (2)
                            = ((a+b)-(c+d+e+f))*x + (4c+5d+6e+7f) - (2a+3b)
        ......
        当6=<x<7,y = (x-2)*a + (x-3)*b + (x-4)*c + (x-5)*d + (x-6)*e - (x-7)*f      (6)
                            = ((a+b+c+d+e)-f)*x + 7f - (2a+3b+4c+5d+6e)
        由于a、b、c、d、e、f为不小于0的整数,

        如果a > b+c+d+e+f,则函数式(1)~(6)中x的一次项的值都大于0,即斜率为正,在各个整数区间,y都为向上的折线段,因此当x=2时,y为最小值;

        如果a = b+c+d+e+f,则函数式(1)~(6)中x的一次项的值都大于等于0,且前n(1=<n<=6)个算式中x的一次项的值为0,即斜率为0,线段为平行线,后6-n个算式中x的一次项的值大于0,即斜率为正,y为向上的折线段,因此x为集合{x|2=<x<=6, x的一次项的值为0}中的任一值时,y为最小值;

        如果a < b+c+d+e+f,即斜率为负,y为向下的折线段,没有最小值,继续分析a+b与c+d+e+f的大小

    步骤3:演绎归纳   

        楼层总楼层为m,在2至m层下电梯的乘客数量分别为n2、n3、n4、…、nm
        当  n2+n3…+np-1               <    np+np+1…nm
             n2+n3…+np-1+np         >  np+1+np+2…nm
        x=p(2<=p<m)时,y为最小值ymin=((n2+…+np) - (np+1+…+nm))*p + np+1*(p+1) + np+2*(p+2) + … + nm*m - n2*2 - n3*3 - … - np*p
        当  n2+n3…+np-1               <    np+np+1…nm
             n2+n3…+np-1+np         =  np+1+np+2…nm
               ……
             n2+n3…+nq-1+nq          =  nq+1…nm
             n2+n3…+nq+nq+1        >  nq+2…nm
        x为集合{x|p=<x<=q, 2=<p=<q<=m, p到q的一次项的值等于0}中的任一值时,y为最小值ymin=((n2+…+np) - (np+1+…+nm))*p + np+1*(p+1) + np+2*(p+2) + … + nm*m - n2*2 - n3*3 - … - np*p
        当  n2                   <  n3+…nm
               ……

             n2+n3…+nm-1   <  nm
        x=m时,y为最小值ymin=((n2+…+np) - (np+1+…+nm))*p + np+1*(p+1) + np+2*(p+2) + … + nm*m - n2*2 - n3*3 - … - np*p

    结论:

        函数在各个区间的斜率是单调递增的,如果在两个端点出没有最小值,则函数内必然存在最小值的拐点

    【解决方案】

       

     1 -module(elevatorberth).
     2 -export([start/1]).
     3 
     4 %% 入口函数
     5 %% Passengers:形如[{2,3}, {3,5}, {4,2}...]的元祖列表,
     6 %% 元祖的第一个参数表示楼层,第二个参数表示在该楼层下电梯的乘客数量
     7 start(Passengers) ->
     8     [First | Remain] = Passengers,
     9     {Floors, MinSteps} = calu([First], Remain, []),
    10     io:format("elevator should berth in ~p~n", [Floors]),
    11     io:format("min steps ~p~n", [MinSteps]).
    12 
    13 %% 计算停靠层的集合和最小的步行总数
    14 %% 算法:将全部楼层分为一段段的射线,元祖表示端点的x、y坐标,
    15 %%       将各条射线连接成一条折线,求折线的拐点
    16 %% CaluedRaysList:计算过的射线端点列表
    17 %% CaluedRaysList:待计算的射线端点列表
    18 %% Floors:停靠层的集合
    19 
    20 %% CaluedRaysList为空,全部楼层计算完毕,
    21 %% Floors为空,表示折线单调递减
    22 calu(CaluedRaysList, [], []) ->
    23     [CurrnetRay | _] = CaluedRaysList,
    24     {Floor, _} = CurrnetRay,
    25     {Floor, min_steps(CaluedRaysList, Floor, 0)};   %停靠在最高楼层
    26     
    27 %% CaluedRaysList为空,全部楼层计算完毕,
    28 %% Floors不为空,表示最后一条射线斜率为0
    29 calu(CaluedRaysList, [], Floors) ->    
    30     [CurrnetRay | _] = CaluedRaysList,
    31     {Floor, _} = CurrnetRay,
    32     {Floors ++ [Floor], min_steps(CaluedRaysList, Floor, 0)};
    33 calu(CaluedRaysList, RemainRaysList, Floors) ->
    34     RaySlope = calu_values_sum(CaluedRaysList, 0) - calu_values_sum(RemainRaysList, 0),
    35     [CurrnetRay | _] = CaluedRaysList,
    36     {Floor, _} = CurrnetRay,
    37     [NextRay | NewRaysList] = RemainRaysList,
    38     if
    39         RaySlope > 0 ->        %斜率大于0
    40             {Floor, min_steps(CaluedRaysList ++ RemainRaysList, Floor, 0)};
    41         RaySlope =:= 0 ->    %斜率等于0
    42             calu([NextRay] ++ CaluedRaysList, NewRaysList, Floors ++ [Floor]);
    43         RaySlope < 0 ->        %斜率小于0    
    44             calu([NextRay] ++ CaluedRaysList, NewRaysList, Floors)
    45     end.
    46             
    47 %% 计算元祖列表中值的算术和
    48 calu_values_sum([], Sum) ->
    49     Sum;
    50 calu_values_sum(Passengers, Sum) ->
    51     [Current | Remain] = Passengers,
    52     {_, Num} = Current,
    53     calu_values_sum(Remain, Sum + Num).
    54 
    55 %% 最小的步行总数    
    56 min_steps([], _, Value) ->
    57     Value;
    58 min_steps(Passengers, Floor, Value) ->
    59     [Current | Remain] = Passengers,
    60     {CurFloor, CurNum} = Current,
    61     min_steps(Remain, Floor, Value + abs(Floor-CurFloor) * CurNum).
    运行时结果如下:
    16> elevatorberth:start([{2,2},{3,1},{4,1},{5,2},{6,4},{7,10}]).
    elevator should berth in [6,7]
    min steps 25
    ok
    上善若水
  • 相关阅读:
    再谈Linux内核中的RCU机制
    Linux下的crontab定时执行任务命令详解
    Linux 2.6内核中新的锁机制--RCU
    linux内核自锁旋spinlock常用宏解释
    Linux内核list/hlist解读
    Linux下0号进程的前世(init_task进程)今生(idle进程)----Linux进程的管理与调度(五)
    Linux下的进程类别(内核线程、轻量级进程和用户进程)以及其创建方式--Linux进程的管理与调度(四)
    Linux进程ID号--Linux进程的管理与调度(三)
    内核源码阅读(三)进程命名空间和用户命名的实现
    Linux的命名空间详解--Linux进程的管理与调度(二)
  • 原文地址:https://www.cnblogs.com/netbuddy/p/2799364.html
Copyright © 2011-2022 走看看