【问题标题】:How to know if a command or method is an 'in place algorithm'?如何知道命令或方法是否是“就地算法”?
【发布时间】:2014-03-17 12:04:37
【问题描述】:

我正在学习python,如何知道命令或方法是否为'in place algorithm'?

尝试“大”输入并使用命令检查运行时是否足够好?

例如,假设我有一个包含 100,000 个元素的列表 lst。 我检查了以下两个命令,并且都“完成”了一会儿:lst = lst[ : :-1]lst.reverse()。那么这是否意味着两者都已到位?

【问题讨论】:

  • 你说的是他们的底层实现吗?
  • @devnull 我不明白这个问题。
  • 你打算如何区分 O(1) 额外空间和 O(A^(-1)(n)) 其中A^(-1) 是阿克曼函数的反函数? A^(-1)(n) 最多为 4,您可以测试 any 输入,因此在实际测试中它无论如何都等于 O(1) 但它是 not 就地考虑。
  • @Bakuriu 鉴于我听到人们对 union find 数据结构的看法,大多数人会将O(A^(-1)n) 空间视为有效的常量空间(有充分理由),而不是因为将其称为就地空间而争论不休算法。
  • @delnan 人们还说快速排序在占用 O(log(n)) 空间时就位。我的观点是,如果我们使用严格的就地定义,则根本不可能通过实验准确确定这种算法的给定实现是否有效地占用了恒定空间。

标签: python algorithm list python-3.x


【解决方案1】:

如果一个函数是就地的,那么它将修改调用它的对象。如果不是,那么它将返回结果。

sorted(lst) # returns the sorted form of lst
lst.sort() # sorts lst

仅此而已。不要试图将其与运行时间或效率联系起来。

【讨论】:

    【解决方案2】:

    就地操作通常在 Python 中返回 None,因此在您的情况下,lst.reverse() 是就地操作,因为它返回 None 并修改列表。而lst[::-1] 返回一个您重新分配给lst 的新列表。

    >>> lst  = range(1000)
    >>> id(lst)
    154457996
    >>> lst = lst[::-1]
    >>> id(lst)             #id changed.
    160699852
    >>> lst  = range(1000)
    >>> id(lst)
    160699340
    >>> lst.reverse()
    >>> id(lst)             #same id
    160699340
    

    【讨论】:

    • 是我自己还是就地算法的定义有点误导?有人说这是一种不使用任何辅助数据结构的算法。 python 中的一些方法,如 lst.sort() 返回 None 但 sort 方法可能使用辅助数据结构来执行排序(即合并排序),因此根据维基百科的定义,它不是就地的
    【解决方案3】:

    要回答这个问题,我们首先要知道什么是'in place algorithm'?

    根据维基百科,an in-place algorithm 是一种算法,它使用具有少量恒定额外存储空间的数据结构来转换输入。

    “覆盖输入”和“就地算法”有什么关系:

    1。如果一个人覆盖它的输入,它不是必须一个就地算法吗?

    不,例如,快速排序总是覆盖它的输入,但它需要 O(log(n)) 额外空间来跟踪递归函数调用。

    2。如果一个是就地算法,它是否必须覆盖它的输入?

    不,例如在数组中找到最小数的算法,它是一个就地算法,只需要 O(1) 额外的空间,但它不需要覆盖它的输入。

    因此,它们之间没有绝对关系,输入通常会被就地算法覆盖。

    以及如何知道方法是否在原地算法中?

    嗯,我想你一定要看它的实现,源码,同样的方法可能使用不同的算法,毕竟方法或函数和算法不是一回事。

    顺便说一句,有一种简单的方法可以知道方法的输入是否被覆盖:

    >>> lst = [1, 2, 3]
    >>> id(lst)`
    3070142764
    >>> lst = lst[: : -1]
    >>> id(lst)
    3070142828
    >>> lst.reverse()
    >>> id(lst)
    3070142828
    

    lst[: : -1]之后lst的id变了,所以lst[: : -1]新建了一个list对象,lst.reverse()之后lst的id没有变,所以lst.reverse()覆盖了它的输入。

    p>

    【讨论】:

      【解决方案4】:

      如果您使用的是 iPython 之类的东西,您可以询问解释器 -

      In [1]: x = [1, 2, 3]
      
      In [2]: ? x.reverse
      Type:       builtin_function_or_method
      Base Class: <type 'builtin_function_or_method'>
      String Form:<built-in method reverse of list object at 0x0362EB48>
      Namespace:  Interactive
      Docstring:  L.reverse() -- reverse *IN PLACE*
      

      所以reverse 方法就位。一般来说,像

      In [3]: x = x[::-1]
      

      不会就地,因为首先创建一个副本,然后分配给x。你知道这不可能就地完成,因为类似的任务

      In [4]: y = x[::-1]
      

      当然必须创建x 的附加副本。

      【讨论】:

        【解决方案5】:

        在我的脑海中,我认为在运行程序时查看内存消耗会是一个更好的指标。据我了解“就地”,这意味着运行算法不需要(或很少)额外的空间。 http://en.wikipedia.org/wiki/In-place_algorithm.

        对于您的特定示例,我认为快速运行时间很可能是由于使用了列表实现。我的第一个猜测是列表是使用双向链表实现的。这反过来当然意味着将在原地执行反向操作。

        如果你想确定,我认为你需要先跳入实现相关算法的代码。

        【讨论】:

        • 在解释型语言中,内存消耗不需要与算法的空间复杂度相关。例如,python 列表使用的内存比保存其内存所需的内存多,以减少关注appends 的成本。如果您不考虑这一点,则在分析内存使用情况时,您可能会发现与您对算法空间分析的期望不同的结果。
        • @Bakuriu 动态数组的过度分配与解释或对象模型完全无关,它与数据结构决策一样与语言无关(其基本原理在 RAM 计算模型中制定)。但是你说得对,测量内存消耗有很多陷阱。
        • 至于这个答案:list 不是双向链表。值得庆幸的是,它是一个动态数组。倒车确实很快,但不是恒定的时间。
        • @delnan 我可能在那里误用了“解释”,但我的意思是任何提供足够高级操作的语言。例如不是 C,因为在 C 中您显式分配了精确的内存量,因此,给定 C 程序更容易理解给定算法应该使用多少内存,这对于 python 程序来说并不容易。
        • @Bakuriu 内存管理更加明确,但仍然可以大部分隐藏在库中。考虑像@​​987654324@这样的代码
        猜你喜欢
        • 2012-09-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-01
        • 1970-01-01
        • 2012-02-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多