使用分而治之方法的最大函数比线性慢?
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) 的性能做些什么吗?是的,有,让我给你下面的例子:你有一个排序列表,你想插入一个新项目,确保列表保持排序。然后,您可以使用二分搜索算法来查找需要插入该新项目的位置。
【讨论】:
以上是关于使用分而治之方法的最大函数比线性慢?的主要内容,如果未能解决你的问题,请参考以下文章