O(NlogN)算法比O(n)运行得快...等等,那是什么?

时间:2018-07-23 13:33:43

标签: c++ algorithm sorting search time-complexity

说实话,我有点困惑。我正在研究经典算法问题之一。给定一个整数集合,找到是否有2个元素加和成给定的数字。

因此,我已经实现了2个解决方案。

bool find1(std::vector<int>& V, int sum) 
{
    std::unordered_set<int> hashTable;
    for (int i = 0; i < V.size(); ++i) 
    {
        if (hashTable.find(V[i]) != hashTable.end()) 
        {
            return true;
        }
        hashTable.insert(sum - V[i]);
    }
    return false;
}

bool find2(std::vector<int>& V, int sum) 
{
    for (int i = 0; i < V.size() ; ++i) 
    {
        if (std::binary_search(V.begin(), V.end(), sum - V[i])) 
        {
            return true;
        }
    }
    return false;
}

Find1有望成为一种线性算法(取决于存储桶的负载和哈希函数的效率)。

Find2预期为NlogN,我们循环并为每次迭代进行二进制搜索。

实现此功能后,我尝试在相对较大的集合上测试这些算法的运行时间,结果使我感到困惑。

int main() 
{
    std::vector<int> V(10000,0);

    std::chrono::system_clock::time_point now1 = std::chrono::system_clock::now();

    for (int i = 0; i < 100; ++i) 
    {
        bool b = find1(V, 1000);
    }

    std::chrono::system_clock::time_point then1 = std::chrono::system_clock::now();
    std::cout <<"Linear with hashing = "<< std::chrono::duration_cast<std::chrono::microseconds>(then1 - now1).count()<<std::endl;

    std::chrono::system_clock::time_point now2 = std::chrono::system_clock::now();
    std::sort(V.begin(), V.end());
    for (int i = 0; i < 100; ++i)
    {
        bool b = find2(V, 1000);
    }

    std::chrono::system_clock::time_point then2 = std::chrono::system_clock::now();
    std::cout <<"NlogN with binary_search = " <<std::chrono::duration_cast<std::chrono::microseconds>(then2 - now2).count() << std::endl;

    system("pause");
}

在这里,我将vector初始化为0,以确保两个算法都在最坏的情况下运行。
 该程序的输出为:

Linear with hashing = 6759245         
NlogN with binary_search = 4508025

这怎么可能?有人可以向我解释一下吗?

3 个答案:

答案 0 :(得分:9)

一种算法的渐近复杂度的上限小于另一种算法,但这并不意味着它对于任何任意输入都更快。这仅意味着存在一定大小的输入N',超过此大小,较简单的算法将更快。此大小将特定于运行该程序的每个特定系统。

将渐近复杂算法测得更快意味着,您的测试小于N'。但是,这假定您的复杂性分析首先适用于该程序。例如,如果程序使用最佳情况下的输入来测试算法,则分析最坏情况下的复杂性是错误的,反之亦然。

对于它的价值,在我的系统上的结果是:

Linear with hashing = 9557
NlogN with binary_search = 15828

答案 1 :(得分:6)

您创建的哈希表没有预期的大小。然后,将元素逐一插入。这会导致哈希表的大小一遍又一遍地调整,从而导致系统调用分配更多的内存。

虽然每次插入时这些费用全部摊销O(1),但系统调用的隐藏常量足够大,可以使二进制搜索更快。

尝试将哈希表的预期大小设置为sizeof(V) * 1.2左右,以避免重新哈希。如果还不够,则将计时与100000, 1000000, 10000000, ...值进行比较。您应该会看到随着N变大,哈希表获胜。

注意:使用V.end() == 0进行二进制搜索将在第一次比较时终止,这不是最坏的情况。最好的情况。可能是更快的原因。

答案 2 :(得分:2)

O(N)比O(N Log N)渐近快。这并不意味着它会更快。

查看Big-O表示法的定义。

相关问题