Python中的高效排班计划

如何解决Python中的高效排班计划

| 我目前正在为一家模型出租车公司做一些排班调度模拟。该公司经营350辆出租车,并且在任何一天都在使用。司机每个工作5班,每班12小时,每天有四个重叠的班次。从3:00-15:00、15:00-3:00、16:00-4:00和4:00-16:00轮班。我最初是用Python开发的,因为需要快速开发它,我认为性能可以接受。原始参数一天只需要轮班两次(3:00-15:00和15:00-3:00),虽然性能不是很好,但对于我的使用来说已经足够了。它可以使用简单的蛮力算法为驾驶员制定每周约8分钟的时间表(评估所有可能的调换以查看情况是否可以得到改善。) 在四个重叠的转变中,性能绝对糟糕。每周计划需要一个多小时。我已经使用cProfile进行了一些性能分析,看来罪魁祸首是两种方法。一种方法是确定将驾驶员换档时是否存在冲突。确保他们不在同一天的轮班中服务,也不在之前或之后的轮班中服务。每天只需两班,这很容易。只需确定驾驶员是否已经安排好直接在上班之前或之后上班。随着四个重叠的转变,这变得更加复杂。第二个罪魁祸首是确定轮班是白天还是夜班的方法。同样,对于原始的两个班次,这就像确定班次号是偶数还是奇数一样简单,班号从0开始。第一个班次(班次0)被指定为夜班,第二个班次被指定为白天,等等等等。现在,前两个是晚上,接下来的两个是,依此类推。这些方法相互调用,因此我将其身体放在下面。
def conflict_exists(shift,driver,shift_data):
    next_type = get_stype((shift+1) % 28)
    my_type = get_stype(shift)

    nudge = abs(next_type - my_type)

    if driver in shift_data[shift-2-nudge] or driver in shift_data[shift-1-nudge] or driver in shift_data[(shift+1-(nudge*2)) % 28] or driver in shift_data[(shift+2-nudge) % 28] or driver in shift_data[(shift+3-nudge) % 28]:
        return True
    else:
        return False
请注意,get_stype返回班次的类型,其中0表示夜班,而1表示日班。 为了确定班次类型,我使用以下方法:
def get_stype(k):
    if (k / 4.0) % 1.0 < 0.5:
        return 0
    else:
        return 1
这是cProfile的相关输出:
     ncalls  tottime  percall  cumtime  percall
     57662556   19.717    0.000   19.717    0.000 sim8.py:241(get_stype)
     28065503   55.650    0.000   77.591    0.000 sim8.py:247(in_conflict)
是否有人对我如何改进此脚本的性能有任何明智的建议或技巧?任何帮助将不胜感激! 干杯, 提姆 编辑:对不起,我应该澄清每个班次的数据都存储为一个集合,即shift_data [k]是设置的数据类型。 编辑2: 根据下面的请求添加主循环,以及其他调用的方法。有点混乱,对此我深表歉意。
def optimize_schedule(shift_data,driver_shifts,recheck):
    skip = set()

    if len(recheck) == 0:
        first_run = True
        recheck = []
        for i in xrange(28):
            recheck.append(set())
    else:
        first_run = False

    for i in xrange(28):

        if (first_run):
            targets = shift_data[i]
        else:
            targets = recheck[i]

        for j in targets:
            o_score = eval_score = opt_eval_at_coord(shift_data,i,j)

            my_type = get_stype(i)
            s_type_fwd = get_stype((i+1) % 28)

            if (my_type == s_type_fwd):
                search_direction = (i + 2) % 28
                end_direction = i
            else:
                search_direction = (i + 1) % 28 
                end_direction = (i - 1) % 28 

            while True:
                if (end_direction == search_direction):
                    break
                for k in shift_data[search_direction]:

                    coord = search_direction * 10000 + k 

                    if coord in skip:
                        continue

                    if k in shift_data[i] or j in shift_data[search_direction]:
                        continue

                    if in_conflict(search_direction,j,shift_data) or in_conflict(i,k,shift_data):
                        continue

                    node_a_prev_score = o_score
                    node_b_prev_score = opt_eval_at_coord(shift_data,search_direction,k)

                    if (node_a_prev_score == 1) and (node_b_prev_score == 1):
                        continue

                    a_type = my_type
                    b_type = get_stype(search_direction)

                    if (node_a_prev_score == 1):
                        if (driver_shifts[j][\'type\'] == \'any\') and (a_type != b_type):
                            test_eval = 2
                        else:
                            continue
                    elif (node_b_prev_score == 1):
                        if (driver_shifts[k][\'type\'] == \'any\') and (a_type != b_type):
                            test_eval = 2
                        else:
                            test_eval = 0
                    else:
                        if (a_type == b_type):
                            test_eval = 0
                        else:
                            test_eval = 2

                    print \'eval_score: %f\' % test_eval

                    if (test_eval > eval_score):

                        cand_coords = [search_direction,k]
                        eval_score = test_eval
                        if (test_eval == 2.0):
                            break
                else:
                    search_direction = (search_direction + 1) % 28
                    continue

                break

            if (eval_score > o_score):
                print \'doing a swap: \',print cand_coords,shift_data[i].remove(j)
                shift_data[i].add(cand_coords[1])

                shift_data[cand_coords[0]].add(j)   
                shift_data[cand_coords[0]].remove(cand_coords[1])

                if j in recheck[i]:
                    recheck[i].remove(j)

                if cand_coords[1] in recheck[cand_coords[0]]:               
                    recheck[cand_coords[0]].remove(cand_coords[1])

                recheck[cand_coords[0]].add(j)
                recheck[i].add(cand_coords[1])

            else:
                coord = i * 10000 + j
                skip.add(coord)

    if first_run:
        shift_data = optimize_schedule(shift_data,recheck)

    return shift_data



def opt_eval_at_coord(shift_data,j):
    node = j
    if in_conflict(i,node,shift_data):
        return float(\'-inf\')
    else:
        s_type = get_stype(i)

        d_pref = driver_shifts[node][\'type\']

        if (s_type == 0 and d_pref == \'night\') or (s_type == 1 and d_pref == \'day\') or (d_pref == \'any\'):
            return 1
        else:
            return 0
    

解决方法

        没有任何东西显然会减慢这些功能的速度,实际上它们并没有减慢速度。他们只是被叫很多。您说您正在使用蛮力算法-您能编写一种不会尝试所有可能组合的算法吗?还是有一种更有效的方法,例如通过驱动程序而不是通过移位存储数据? 当然,如果您需要即时加速,则可以通过在诸如PyPy的解释器中运行或使用Cython将关键部分转换为C来受益。     ,        嗯有趣且有趣的问题。我将不得不多看。现在,我要提供的是:为什么要引入浮子?我会做get_stype()如下:
def get_stype(k):
    if k % 4 < 2:
        return 0
    return 1
这不是一个大的提速,但是它更快(更简单)。另外,您每次喂
get_stype
时都不必执行mod 28,因为
get_stype
中的mod 4已经解决了这个问题。 如果有重大改进,它们将以更好的算法形式出现。 (我并不是说您的算法不好,或者有更好的算法。我还没有花足够的时间在研究它。但是,如果找不到更好的算法,那么它就更重要了。使用PyPy,Cython,Shed Skin或完全以其他(更快)语言重写将大大提高速度。)     ,我认为您的问题不是运行这两个功能所花费的时间。请注意,函数的percall值为
0.000
。这意味着每次调用该函数都需要不到1毫秒的时间。 我认为您的问题是调用函数的次数。 python中的函数调用非常昂贵。例如,调用不执行任何操作的函数57,662,556在我的计算机上花费了7.15秒:
>>> from timeit import Timer
>>> t = Timer(\"t()\",setup=\"def t(): pass\")
>>> t.timeit(57662556)
7.159075975418091
我想知道的一件事是
shift_data
变量。值是列表还是字典?
driver in shift_data[shift-2-nudge]
如果是列表,则
in
将花费
O(N)
时间,而如果是字典,则将花费
O(1)
时间。 编辑:由于设置了shift_data值,应该没问题     ,        在我看来,在两个白班之间或两个黑班之间进行交换永远无济于事。它不会改变驾驶员对变速的满意程度,也不会改变这些变速与其他变速之间的冲突。 因此,我认为您应该只能计划最初的两个班次,白天和黑夜,然后才将分配给这些班次的驾驶员分为两个实际班次。     

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-