std :: unordered_set的病理输入如何存在?

问题描述

我正在解决一个基本问题,即查找给定数组中不同整数的数量

我的想法是声明一个std::unordered_set,将所有给定的整数插入到集合中,然后输出集合的大小。这是我实现此策略的代码

#include <iostream>
#include <fstream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <unordered_set>

using namespace std;

int main()
{
    int N;
    cin >> N;
    
    int input;
    unordered_set <int> S;
    for(int i = 0; i < N; ++i){
        cin >> input;
        S.insert(input);
    }
    
    cout << S.size() << endl;

    return 0;
}

该策略几乎适用于所有输入。在其他输入情况下,它超时。

我很好奇看到为什么为什么我的程序超时,所以我在for循环中添加cout << i << endl;行。我发现,当我输入输入用例时,循环的第一个53000迭代几乎会立即通过,但此后每秒只会发生几个100迭代。

我已经阅读了如果发生很多冲突,哈希集将如何以O(N)插入而结束,所以我认为输入在std::unordered_set中引起了很多冲突。 / p>

但是,这是不可能的。 std::unordered_set用于整数的哈希函数将它们映射到自身(至少在我的计算机上),因此不同整数之间不会发生冲突。我使用写在this link上的代码访问了哈希函数

我的问题是,输入本身击中插入的std::unordered_set个元素后,输入本身是否可能导致53000变慢?如果是这样,为什么?

Here是测试程序的输入用例。它很大,可能会有些滞后。

解决方法

您提供的输入文件由以1为模107897的连续整数组成。因此,最有可能发生的事情是,在负载因子超过某个阈值时,您正在使用的特定库实现会调整表的大小,并使用带有107897项的表,以便具有哈希值的键h将被映射到存储桶h % 107897。由于每个整数的哈希值本身,这意味着表中到目前为止的所有整数都突然映射到同一存储桶。调整大小本身只需要线性时间。但是,此点之后的每个后续插入将遍历包含所有现有值的链表,以确保它不等于任何现有值。因此,每次插入将花费线性时间,直到下次调整表大小为止。

原则上,unordered_set实现可以通过在任何一个存储桶变得太长时也调整表的大小来避免此问题。但是,这引起了一个问题,即这是否是具有合理哈希函数的哈希冲突(因此需要调整大小),还是用户被误导并将每个密钥哈希为相同的值(在这种情况下,无论表格大小)。所以也许这就是为什么在这种特定的库实现中没有做到这一点。

另请参见https://codeforces.com/blog/entry/62393(此现象在Codeforces竞赛中的应用)。

,

您的程序运行正常。哈希算法,冲突或类似的东西都没有错。

当您尝试将200000数字粘贴到窗口中时,看到的声音来自控制台I / O。这就是为什么它窒息。从文件重定向,它可以正常工作并几乎立即返回结果。

C:\Users\selbie\source\repos\ConsoleApplication126\Debug>ConsoleApplication126.exe  < d:/test.txt
200000

测试输入文件中的所有数字都是唯一的,因此输出为200000