【问题标题】:Space efficiency of algorithms算法的空间效率
【发布时间】:2009-09-08 10:37:18
【问题描述】:

似乎没有一本算法教科书如此提及空间效率,所以当我遇到要求只需要恒定内存的算法的问题时,我真的不明白。

什么是使用常量内存的算法和不使用常量内存的算法的几个示例?

【问题讨论】:

  • 实际上内存使用是算法设计中的一个重要考虑因素。这是合并排序和快速排序之间的重要区别之一。获得更好的教科书。
  • 公平地说,我认为教科书并没有“尽可能多地”提及它。也许是因为它通常更明显,特别是如果您更喜欢迭代而不是递归解决方案。即使是一本模棱两可的算法教科书,也足以说明它的含义和重要性。

标签: algorithm language-agnostic space-efficiency


【解决方案1】:

如果一个算法:

a) 递归若干层,深度取决于 N,或

b) 分配的内存量取决于 N

那么它不是恒定的记忆。否则它可能是:形式上它是常量内存,如果算法使用的内存量有一个恒定的上限,无论输入的大小/值如何。输入占用的内存不包括在内,因此有时需要明确的是,您会谈论恒定的“额外”内存。

所以,这里有一个常量内存算法,用于在 C 中找到整数数组的最大值:

int max(int *start, int *end) {
    int result = INT_MIN;
    while (start != end) {
        if (*start > result) result = *start;
        ++start;
    }
    return result;
}

这是一个非常量内存算法,因为它使用的堆栈空间与输入数组中的元素数量成正比。但是,如果编译器能够以某种方式将其优化为非递归等价物,它可能会变成常量内存(C 编译器通常不会打扰它,除非有时使用尾调用优化,这在这里不起作用):

int max(int *start, int *end) {
    if (start == end) return INT_MIN;
    int tail = max(start+1, end);
    return (*start > tail) ? *start : tail;
}

这是一个常数空间排序算法(这次是在 C++ 中),它是 O(N!) 时间左右(可能是 O(N*N!)):

void sort(int *start, int *end) {
    while (std::next_permutation(start,end));
}

这里是一个O(N)空间排序算法,时间为O(N^2):

void sort(int *start, int *end) {
    std::vector<int> work;
    for (int *current = start; current != end; ++current) {
        work.insert(
            std::upper_bound(work.begin(), work.end(), *current),
            *current
        );
    }
    std::copy(work.begin(), work.end(), start);
}

【讨论】:

  • 实际上 - 因为您首先需要有 n 个值,所以不会找到不需要 O(n) 空间的 n 个值的最大值吗?我猜一个常量算法需要一个输入流。
  • 没有。 “输入占用的内存不包括在内,所以有时需要明确的是,您会谈论恒定的“额外”内存。”
【解决方案2】:

非常简单的例子:计算字符串中的字符数。它可以是迭代的:

int length( const char* str )
{
    int count = 0;
    while( *str != 0 ) {
       str++;
       count++
    }
    return count;
}

或递归:

int length( const char* str )
{
    if( *str == 0 ) {
        return 0;
    }
    return 1 + length( str + 1 );
}

第一个变体只使用几个局部变量,不管字符串长度如何——它的空间复杂度是O(1)。第二个如果在没有递归消除的情况下执行,则需要一个单独的堆栈帧来存储与每个深度级别相对应的返回地址和局部变量——它的空间复杂度是O(n),其中n 是字符串长度。

【讨论】:

  • 你没有解释什么是恒定记忆,什么不是。对于初学者来说可能并不明显。
  • 非常正确。更新了答案。
  • 所以基本上恒定内存算法只是非递归算法?
  • 不一定。您需要根据输入参数查看消耗了多少内存。很容易想象字符串长度计算被重写为直接使用堆栈而不是递归调用。我提供的示例只是一个非常简单的示例,也是我的最爱。
  • 递归算法在内存中是非常量的。普通算法可以分配内存,因此你不能说当一个函数不是递归时,它使用的是常量内存。
【解决方案3】:

以数组上的排序算法为例。您可以使用与将排序元素放入 (Θ(n)) 的原始数组长度相同的新数组。或者您对数组 in-place 进行排序,然后使用一个额外的临时变量来交换两个元素 (Θ(1))。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-16
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多