3

我有一个生成大约 600 万个唯一数组的 C 函数。这些数组每个总是有 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 库绝对是一个加分项。

4

6 回答 6

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-tree。

于 2011-06-19T07:03:45.747 回答
3

我认为您需要权衡在 C 中执行此操作的价值,以避免交流。

我会将 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"

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

于 2012-07-18T09:36:58.053 回答
2

保持简单:

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

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

于 2011-06-19T10:31:49.933 回答
1

取决于您的情况下哪一个会获得更好的内存性能。还有你使用什么散列函数,你如何解决冲突等等。如何检查一个散列数组映射特里(HAMT)

于 2011-06-19T06:51:42.137 回答
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) 时间内执行,因为算法的每一步都至少执行一次计数器的递增。

于 2011-08-02T03:54:54.900 回答
0

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

b) 由于每个结果有 17 个元素,因此将前 8 个元素相乘并将结果放在第一个 int 上,将其他 7 相乘并将结果放在第二个 int 上。

c)为您的结构创建一个运算符 <

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;

埃德温

于 2013-05-23T18:24:11.000 回答