使用分而治之方法的最大函数比线性慢?

Posted

技术标签:

【中文标题】使用分而治之方法的最大函数比线性慢?【英文标题】:Max function using divide and conquer approach is slower than linear? 【发布时间】:2021-08-21 09:03:56 【问题描述】:

我使用两个函数实现了 max 函数(给定一个数组,找到最大值):max,它是 O(n) 和 mergeMax,我希望它是 O(lg n),通过使用分而治之的方法。我原以为随着 n 的增加,mergeMax 会胜出,但令我惊讶的是,它每次都被 max 函数击败。我说 mergeMax 是 O(lg N) 有错吗?这是C中的代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int maxValue = 0;

void mergeMax(int items[], int low, int high)

    if (high == low)
    
        if (items[low] > maxValue) maxValue = items[low];
        return;
    
    if (high - low == 1)
    
        if (items[low] > items[high])
        
            if (items[low] > maxValue) maxValue = items[low];
        
        else
        
            if (items[high] > maxValue) maxValue = items[high];
        
        return;
    
    int middleValue = (low + high) / 2;
    mergeMax(items, low, middleValue);
    mergeMax(items, middleValue + 1, high);


int max(const int items[], int itemCount)

    int max = 0;
    for (int i = 0; i < itemCount; i++)
    
        if (items[i] > max) max = items[i];
    
    return max;

int main(void)

    int itemCount = 2000000;
    printf("Generating...\n");
    int items[itemCount];
    srand(time(0));
    for (int i = 0; i < itemCount; i++)
    
        items[i] = rand() % ((4294967295/2) + 1);
    
    struct timespec start, end;

    clock_gettime(CLOCK_MONOTONIC_RAW, &start);
    mergeMax(items, 0, itemCount - 1);
    clock_gettime(CLOCK_MONOTONIC_RAW, &end);
    uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
    printf("O(lg n) time in microseconds: %lu. Result: %d\n", delta_us, maxValue);

    clock_gettime(CLOCK_MONOTONIC_RAW, &start);
    int maximum = max(items, itemCount);
    clock_gettime(CLOCK_MONOTONIC_RAW, &end);
    delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
    printf("O(n) time in microseconds: %lu. Result: %d\n", delta_us, maximum);

【问题讨论】:

提示:你确定你可以在亚线性时间内找到数组中的最大值吗?没有特殊结构?你不应该至少看看所有元素吗? 因为你需要检查所有元素(假设数组没有排序),那么你不能做得比 O(n) 更好。 @Someprogrammerdude 哎呀!你说得对。我编辑了它。 mergeMax()max() 都是 O(n)...max()mergeMax() 更简单,所以需要的时间更少。 【参考方案1】:

考虑有 1024 个项目的情况。当mergeMax被1024个item调用时,它会将它们分成两段,调用mergeMax两次,每次512个。

这两个mergeMax 调用将分别调用mergeMax 两次,因此总共四次,每个有256 个项目。这四个电话将分别拨打两次mergeMax,因此总共八次,每个电话有 128 个项目。这八个调用将产生 16 个调用,每个调用包含 64 个项目。这 16 个调用将生成 32 个调用,每个调用包含 32 个项目。这 32 个调用将产生 64 个调用,每个调用 16 个项目。这 64 个调用将产生 128 个调用,每个调用 8 个项目。这 128 个调用将产生 256 个调用,每个调用 4 个项目。这 256 个调用将产生 512 个调用,每个调用 2 个项目。

在 2 项时,mergeMax 直接处理这两项,不再产生任何调用。

在这 512 次调用中,最多考虑 2 项。所以找到最大值的工作量仍然是 1024 次测试。什么都没有保存。

此外,有 512 个最终调用,在此之前是 256 个,在此之前是 128 个,然后是 64、32、16、8、4、2 和 1。总共有 1023 个调用到mergeMax,超过 1022 个只需致电max。因此,除了 1024 次测试之外,您还增加了 1022 次函数调用的开销。

当然mergeMax 更慢。

mergeMax 除了调用深度(即一次嵌套调用的数量)之外没有任何对数。

【讨论】:

【参考方案2】:

您正在寻找具有 O(log n) 性能的函数。我可以告诉你,找到最大值并不是一个很好的例子,因为这两个原因:

要么您的列表未排序,因此您需要调查列表中的所有项目 => 性能至少为 O(n)。 您的列表已排序,然后您只需取最后一个值 => 性能为 O(1)。

你可以用 O(log n) 的性能做些什么吗?是的,有,让我给你下面的例子:你有一个排序列表,你想插入一个新项目,确保列表保持排序。然后,您可以使用二分搜索算法来查找需要插入该新项目的位置。

【讨论】:

以上是关于使用分而治之方法的最大函数比线性慢?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用分而治之的方法解决“固定大小的最大子数组”?

使用分而治之的最大连续和

c_cpp 使用分而治之的方法查找未排序数组中的最小值和最大值

具有分而治之的阵列中的最大值

递归线性搜索的复杂性是多少(使用分而治之的技术)?

使用分而治之的最大子阵列产品有人吗?