简体   繁体   English

如何提高课程安排问题中循环检测的 Python 实现的运行时间?

[英]How can I improve the runtime of Python implementation of cycle detection in Course Schedule problem?

My aim is to improve the speed of my Python code that has been successfully accepted in a leetcode problem, Course Schedule .我的目标是提高我的 Python 代码的速度,该代码已在leetcode 问题 Course Schedule中成功接受。

I am aware of the algorithm but even though I am using O(1) data-structures, my runtime is still poor: around 200ms.我知道该算法,但即使我使用 O(1) 数据结构,我的运行时间仍然很差:大约 200 毫秒。

My code uses dictionaries and sets:我的代码使用字典和集合:

from collections import defaultdict


class Solution:
    def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
        course_list = []
        pre_req_mapping = defaultdict(list)
        visited = set()
        stack = set()
        def dfs(course):
            if course in stack:
                return False
            stack.add(course)
            visited.add(course)
            for neighbor in pre_req_mapping.get(course, []):
                if neighbor in visited:
                    no_cycle = dfs(neighbor)
                    if not no_cycle:
                        return False
            stack.remove(course)
            return True
        # for course in range(numCourses):
        #     course_list.append(course)
        for pair in prerequisites:
            pre_req_mapping[pair[1]].append(pair[0])
        for course in range(numCourses):
            if course in visited:
                continue
            no_cycle = dfs(course)
            if not no_cycle:
                return False
        return True

What else can I do to improve the speed?我还能做些什么来提高速度?

You are calling dfs() for a given course multiple times.您多次为给定course调用dfs() But its return value won't change.但它的返回值不会改变。 So we have an opportunity to memoize it.所以我们有机会记住它。 Change your algorithmic approach (here, to dynamic programming) for the big win.改变你的算法方法(这里是动态编程)以获得巨大的胜利。 It's a space vs time tradeoff.这是空间与时间的权衡。 EDIT: Hmmm, you are already memoizing most of the computation with visited , so lru_cache would mostly improve clarity rather than runtime.编辑:嗯,你已经visited记住了大部分计算,所以lru_cache主要会提高清晰度而不是运行时间。 It's just a familiar idiom for caching a result.这只是缓存结果的一种熟悉的习惯用法。

It would be helpful to add a # comment citing a reference for the algorithm you implemented.添加#注释引用您实现的算法的参考会很有帮助。

This is a very nice expression, with defaulting: pre_req_mapping.get(course, []) If you use timeit you may find that the generated bytecode for an empty tuple () is a tiny bit more efficient than that for an empty list [] , as it involves fewer allocations.这是一个非常好的表达式,默认为: pre_req_mapping.get(course, [])如果你使用timeit ,你可能会发现为空元组()生成的字节码比为空列表[] ,因为它涉及较少的分配。 Ok, some style nits follow, unrelated to runtime.好的,一些风格尼特遵循,与运行时无关。

As an aside, youAreMixingCamelCase and_snake_case.顺便说一句,youAreMixing CamelCase 和_snake_case。 PEP-8 asks you to please stick with just snake_case. PEP-8 要求你坚持使用 snake_case。

This is a fine choice of identifier name:这是标识符名称的一个很好的选择:

    for pair in prerequisites:

But instead of the cryptic [0] , [1] dereferences, it would be easier to read a tuple unpack:但不是神秘的[0][1]解引用,而是更容易阅读元组解包:

    for course, prereq in prerequisites:

if not no_cycle: is clumsy. if not no_cycle:是笨拙的。 Consider inverting the meaning of dfs' return value, or rephrasing the assignment as:考虑反转 dfs 的返回值的含义,或将赋值改写为:

        cycle = not dfs(course)

I think that you are doing it in good way, but since Python is an interpreted language, it's normal to have slow runtime compared with compiled languages like C/C++ and Java, especially for large inputs.我认为您做得很好,但是由于 Python 是一种解释性语言,与 C/C++ 和 Java 等编译语言相比,运行时间很慢是正常的,尤其是对于大输入。

Try to write the same code in C/C++ for example and compare the speed between them.例如,尝试用 C/C++ 编写相同的代码并比较它们之间的速度。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM