此功能是否可以比分别使用最小值和最大值同时检索最小值和最大值?

时间:2014-03-09 14:20:17

标签: python max min

我有一个同时检索列表的最小值和最大值的函数:

def min_max(iterable, key=None):
    """Retrieve the min and max values of `iterable` simultaneously."""
    if key is None: key = lambda x: x
    if not iterable:
        return None, None
    min_ = max_ = key(iterable[0])
    for i in iterable[1:]:
        if key(i) > key(max_): max_ = i
        if key(i) < key(min_): min_ = i
    return min_, max_

但它让我感到疑惑,因为我在for循环中进行了两次比较,单独使用minmax会不会更快?如果是,我如何编辑此功能以提高效率?

3 个答案:

答案 0 :(得分:6)

找出列表中的最小值或最大值的昂贵部分不是比较。比较值非常快,并且不会在这里产生问题。相反,影响运行时间的是循环。

当您使用min()max()时,其中每个都必须迭代迭代一次。它们分别执行此操作,因此当您需要最小值和最大值时,通过使用内置函数,您将迭代两次。

您的函数只需迭代一次,因此其理论运行时间更短。现在作为评论中提到的chepter,minmax已实现in native code,因此它们肯定比自己在Python代码中实现它更快。

现在,它很大程度上取决于你的迭代是否两个本机循环将比你的Python函数更快。对于更长的列表,迭代它已经很昂贵,迭代一次肯定会更好,但对于较短的列表,你可能会使用本机代码获得更好的结果。我无法确定确切的阈值在哪里,但您可以轻松地测试实际数据的速度。但在大多数情况下,它很少会因为min / max不会成为你的应用程序的瓶颈,所以你不应该担心它会成为一个问题。


顺便说一下。你的实现现在有一些问题,如果你想使用它你应该修复:

  • 它需要iterable序列,而不是可迭代(因为您在其上使用索引)
  • 您还要求它至少有一个项目 - 技术上也不需要。当您检查not iterable时,这不一定会告诉您序列/可迭代的长度。自定义类型可以轻松提供自己的布尔值和/或序列行为。
  • 最后,使用可迭代项的键控值初始化_min_max,但稍后(正确)只是从可迭代项中分配原始项目。

所以我建议你改用迭代器,并修复那个关键的东西 - 你也可以存储关键结果,以便为更复杂的关键函数保存一些计算:

it = iter(iterable)
try:
    min_ = max_ = next(it)
    minv = maxv = key(min_)
except StopIteration:
    return None, None

for i in it:
    k = key(i)
    if k > maxv:
        max_, maxv = i, k
    elif k < minv:
        min_, minv = i, k

我对此做了一些测试,事实证明 - 没有自定义键功能 - 使用内置的max / min是不可能的。即使对于非常大的列表,purce C实现也太快了。但是,只要添加一个键函数(用Python代码编写),情况就会完全颠倒过来。使用键功能,您可以获得与单个minmax调用完全相同的时序结果,而完整功能同时执行两者。因此,使用Python编写的解决方案很多更快。

因此,这导致了这样的想法,即,Python中的实现可能不是实际问题,而是使用的key函数。事实上,实际的关键功能是使Python实现成本高昂的原因。它也很有道理。即使使用identity-lamba,你仍然有函数调用的开销; len(iterable)许多函数调用(使用上面的优化变体)。函数调用非常昂贵。

在我的测试中,由于支持取出的关键功能,实际预期的结果出现了:迭代一次比两次快。但对于非常大的迭代,差异非常小。因此,除非迭代迭代是非常昂贵的(尽管你可以使用tee并仍然迭代两次)或者你想要遍历它(在这种情况下你将它与min / max检查结合),使用内置的max()min()功能将分别更快,也更容易使用。并且,如果您没有指定关键函数,它们都会带有内部优化功能。

最后,您如何在代码中添加关键函数优化?好吧,不幸的是,只有一种方法可以做到这一点,这涉及到复制代码。您基本上必须检查是否指定了键功能,并在不执行时跳过函数调用。所以,像这样:

def min_max(iterable, key=None):
    if key:
        # do it with a key function
    else:
        # do it without

答案 1 :(得分:0)

请点击此处:

这不是你正在寻找的,但我可以减少循环:

def min_max(iterable):
    if not iterable:
        raise Exception('Required iterable object')
    _min = _max = iterable[0]
    ind = 0
    if len(iterable) & 1:
        ind = 1
    for elm in iterable[1::2]:
        ind += 2
        try:
            if iterable[ind] < iterable[ind + 1]:
                if _min > iterable[ind]:
                    _min = iterable[ind]
                if _max < iterable[ind + 1]:
                    _max = iterable[ind + 1]
            else:
                if _min > iterable[ind + 1]:
                    _min = iterable[ind + 1]
                if _max < iterable[ind]:
                    _max = iterable[ind]
        except:
            pass
    return _min, _max

print min_max([11,2,3,5,0,1000,14,5,100,1,999])

输出:

(0, 1000)

答案 2 :(得分:-1)

使用此代码:

for i in iterable[1:]:
    if key(i) > key(max_): 
        max_ = i
    elif key(i) < key(min_):
        min_ = i