使用什么数据结构? (哈希地图与特里与?)

时间:2011-06-19 06:40:11

标签: c data-structures hashmap trie

我有一个C函数,可生成大约600万个唯一数组。这些数组每个元素总共有17个元素,每个元素都是0到16之间的整数。我还有一个稍微修改过的函数版本,它也会产生大约600万个相同类型的独特数组。我的问题是第二个产生的结果比第一个少了45,000个,我想看看这些结果是什么。

所以我的方法是简单地存储第二个函数的所有结果(计算器告诉我这不应该超过400 MB,这样可以保留在内存中)然后查找第一个结果,打印出来那些不存在的。

假设一般方法有意义(如果没有,请告诉),我正在寻找的是一个合适的数据结构(理想情况下在C中有一个很好的实现),可以容纳大约600万个独特的排列

[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

(或其某些转换)然后对它们执行快速成员资格测试。正如标题所说,我确实怀疑哪些数据结构可以完成这项工作,但我不确定尝试或哈希图是最好的选择。

这是一种检测另一种算法中的缺陷的算法,而不是将用于生产的算法。我感兴趣的是这样做的方式将被编码并在人类方面相对快速地返回结果,不一定要刮掉毫秒,因此存在易于完成大部分工作的grok库绝对是一个优点。

6 个答案:

答案 0 :(得分:5)

最优性取决于排列的分布方式以及插入与搜索的比率。由于你关注最优性,但只是想要一种直接的方法来测试一个假设而不用等待整晚的结果,我的直觉说:

整数[0,16]可以表示为5位数,因此其中17个可以表示为85位(11字节)二进制字符串。因此,您可以使用许多可用于存储已排序/散列字符串集的库中的一个,并对其进行成员资格测试,然后完成。它不会像调优的trie那样快速或缓存一致,但是在几秒钟内完成66mb的数据研磨就足够了,你将在午餐时间完成。

如果没有这样的库是方便的,你必须从头开始工作,我只是制作一个排序的字符串列表,然后通过二进制搜索进行成员资格测试。这有点像O( n log n + m n log n ))= O(2× mn log n 例如二次时间为m→n。如果这只是在生产过程中作为离线作业运行一次或两次,那可能就足够了;如果你每天不止一次这样做,我会担心缓存局部性并使用trie或B树。

答案 1 :(得分:3)

我认为你需要权衡C中的这个值,以避免沟通。

我会逐行打印每个数组作为空格分隔的整数。然后从文件中加载它以创建一组像这样的字节数组(F#代码):

let load file =
  System.IO.File.ReadAllLines file
  |> Array.Parallel.map (fun s -> s.Split[|' '|] |> Array.map (int >> byte))
  |> set

然后计算两个文件之间的集合差异,如下所示:

load "file1.txt" - load "file2.txt"

这可能需要几分钟才能运行。

答案 2 :(得分:2)

保持简单:

  • 将每个排列表示为17个字节的数组
  • 将整个较小的集合存储为上述数组(17 * 6M <98MB)
  • 按字典顺序排序,因此qsort的比较器只调用memcmp(left, right, 17)
  • 对于较大集合的每个元素,使用二进制chop在有序数组中查找(使用与之前相同的比较器,这次使用bsearch)。

最后两个步骤中的每个步骤将执行6M * log(6M)比较的量级,大约为138M。这可能比编写代码所花费的时间还少。这不长,因为一切都很简单: - )

答案 3 :(得分:1)

取决于您的情况下哪一个会获得更好的内存性能。还有你使用的哈希函数,如何解决冲突等。如何查看Hash Array Mapped Trie (HAMT)

答案 4 :(得分:1)

@Steve Jessop你可以在线性时间做最后一步,通过删除我们正在搜索的数组中不需要的值来进行更智能的搜索:

假设n的大小为A,m的大小为B,

int counter_A = 0;
int counter_B = 0;
int counter_C = 0;
while(counter_A != n){
    int temp = A[counter_A];
    counter_A++;
    //Removes all elements at the beginning of B since they are inferior than all
    //elements in A because they are inferior than the minimum of A
    for(;counter_B < m && B[counter_B] < temp;counter_B++);
    if((counter_B < m && B[counter_B] > temp) || counter_B == m){
        C[counter_C] = temp;
        counter_C++;
    }
}

这应该在O(n + m)时间内执行,因为算法的每一步都至少执行一次计数器递增。

答案 5 :(得分:0)

a)创建一个包含两个64位int

的结构

b)因为每个结果都有17个元素,将前8个乘以第一个int,然后将另一个7乘以第二个int。

c)创建一个运算符&lt;为你的结构

d)创建一组结构并插入第一次运行的所有结果

e)迭代你的第二次运行结果并做一个set :: find()

class Result
{
public:
    Result(int arr[17]);              // Fill-in _n1 and _n2

    bool operator < (const Result& r) const  // Compare
    { 
        if (_n1 != r._n1)
           return _n1 < r._n1;
        return _n2 < r._n2;
    }

protected:
    int _n1;
    int _n2;
};

typedef std::set< Result > SetResult;
SetResult setResult;

埃德