一组(非不相交)集合的数据结构

8
我正在寻找一种数据结构,大致对应于(在Java术语中)Map<Set<int>, double>。本质上是一组带标签的弹珠集合,其中每个弹珠集合都与一个标量相关联。我希望它能够有效地处理以下操作:
  • 将给定整数添加到每个集合中。
  • 删除包含(或不包含)给定整数的每个集合,或者至少将关联的double设置为0。
  • 合并两个映射,对于同时出现的集合,将它们的double相加。
  • 将所有double乘以给定的double。
  • 很少遍历整个映射。

在以下条件下:

  • 整数将落在受限范围内(大约在1到10,000之间),确切的范围将在编译时知道。
  • 范围内的大多数整数(80-90%)永远不会使用,但哪些整数将不容易确定,直到计算结束。
    • 使用的整数数量几乎总是超过100。
  • 许多集合非常相似,仅由少数元素不同。
  • 可能可以识别经常按顺序出现的某些整数组:例如,如果一个集合包含整数27和29,则它(几乎?)肯定也包含28。
    • 可能可以在运行计算之前识别这些组。
    • 这些组通常会有大约100个整数。

我考虑过tries,但我没有看到处理“删除包含给定整数的每个集合”操作的好方法。

这种数据结构的目的是表示离散随机变量并允许对它们进行加法,乘法和标量乘法操作。每个这些离散随机变量最终都是通过将这些操作应用于一组独立伯努利随机变量(即每个随机变量以某些概率取值1或0)而创建的(在编译时)固定集合。

被建模的系统与时间不齐的马尔可夫链非常接近(这当然会极大地简化此过程),但不幸的是,跟踪各种转换的持续时间是至关重要的。


听起来你已经知道你想要什么了。你会用什么语言?Java?你可以开始实现你建议的数据结构,看看你喜欢和不喜欢它的哪些方面(插入/更新/删除/其他操作)。 - AndyG
这是一个很好的观点,也许是我最终要做的。我还没有这样做的主要原因是该项目现在只处于推测阶段。如果没有办法在不使其变得非常缓慢的情况下完成此操作,那么我可能会选择更少野心的设计,以换取表达能力。关于语言,可能会使用C#和F#的某种组合。 - Alex Godofsky
1
至少“添加到每个集合”和“乘以每个标量”可以由地图的一个成员set/double处理,只有在需要时才会被评估;懒惰地说。也许记住这一点,你可以专注于优化其他操作?你要追求什么样的复杂性,空间和时间? - G. Bach
1
除了@G.Bach的建议之外,如果您可以快速支持“查找集合”操作(可能通过保留Int->Set映射并结合线性搜索实现),您还可以通过使用按权重合并来快速合并两个映射。只需完全评估较小的映射(解决惰性)并将较小的一个中的集合逐个插入到较大的一个中即可。为什么这很快的论据可以在此处找到:http://cs.stackexchange.com/questions/22725/merge-by-weight-to-solve-reachability-problems-in-trees-and-dags。 - Niklas B.
2
你是如何生成这些非常相似的集合的?平均有多少个集合?平均集合大小是多少?另外,与其使用 Map<Set<int>, double>,你可能想考虑只是继承 Set<int> 并添加一个 Weight 字段。 - Andy Jones
显示剩余14条评论
1个回答

1
这是一种数据结构,可以非常有效地执行所有操作:我将在本说明中称之为 BitmapArray 。考虑到你所描述的操作,一个带有位图作为键和权重(双精度浮点数)作为值的排序数组将非常有效。位图用于维护集合中的成员资格。由于您说集合中的整数范围在1-10,000之间,因此我们可以使用长度为10,000的位图来维护有关任何集合的信息。尽管键可以达到2 ^ 10000那么大,但您可以通过以下方式聪明地实现比较函数来进行排序:从左到右迭代两个位图,在每个索引上XOR位,假设您在第i个位置得到1,具有1的位图更大,如果您从未得到1,则它们相等。

我知道这个比较还是有些慢。 但并不是太慢,这里有一个我在长度为10000的位图上做的基准测试。 这是用Javascript编写的,如果你要用Java编写,它会表现得更好。

    function runTest() {
    var num = document.getElementById("txtValue").value;
    num = isNaN(num * 1) ? 0 : num * 1;

    /*For integers in the range 1-10,000 the worst case for comparison are any equal integers which will cause the comparision to iterate over the whole BitArray*/
    bitmap1 = convertToBitmap(10000, num);
    bitmap2 = convertToBitmap(10000, num);

    before = new Date().getMilliseconds();
    var result = firstIsGreater(bitmap1, bitmap2, 10000);
    after = new Date().getMilliseconds();
    alert(result + " in time: " + (after-before) + " ms");

}


function convertToBitmap(size, number) {
    var bits = new Array();
    var q = number;
    do {
        bits.push(q % 2);
        q = Math.floor(q / 2);
    } while (q > 0);


    xbitArray = new Array();
    for (var i = 0; i < size; i++) {
        xbitArray.push(0);
    }

    var j = xbitArray.length - 1;
    for (var i = bits.length - 1; i >= 0; i--) {
        xbitArray[j] = bits[i];
        j--
    }
    return xbitArray;
}

function firstIsGreater(bitArray1, bitArray2, lengthOfArrays) {
    for (var i = 0; i < lengthOfArrays; i++) {
        if (bitArray1[i] ^ bitArray2[i]) {
            if (bitArray1[i]) return true;
            else return false;
        }
    }
    return false;
}

document.getElementById("btnTest").onclick = function (e) {
    runTest();
};

此外,请记住,您只需要在构建BitmapArray(或合并时)执行一次此操作,然后对于您经常执行的操作,它将变得非常高效:
注意:N是BitmapArray的长度。
为每个设置添加整数:最坏/最佳情况下为O(N)时间。在每个位图中将0翻转为1。
删除包含给定整数的所有集的最坏情况是O(N)时间。 对于每个位图,请检查表示给定整数的位,如果为1,则标记其索引。 通过删除所有标记的索引来压缩数组。
如果您只想将权重设置为0,那么它将更加高效。如果您想删除具有给定集合中任何元素的所有集,则这也使得它非常容易。
两个地图的联合:最坏情况下为O(N1 + N2)时间。就像合并两个排序数组一样,除了您必须再次聪明比较。
将所有双倍乘以给定的双倍:最坏/最佳情况下为O(N)时间。迭代并将每个值乘以输入双倍。

迭代 BitmapArray:下一个元素的最坏/最优时间复杂度为 O(1)。


谢谢!我一直在考虑使用位图;现在我正在使用一个Dictionary<MySet<int>,double>,其中MySet是一个不可变集合,实现为整数数组,并仅支持联合和集合相等操作。排序后的整数数组具有类似的渐近性能,但位图应该会大大加快速度。(高达32倍!)我一直在对我的应用程序进行性能分析,大约70%的时间都花在联合操作上。 - Alex Godofsky
考虑到所提到的操作中,联合操作可能是最难的一个。我认为,在任何数据结构的有序联合运算中,O(N1+N2) 的时间似乎是渐近最优的。如果你找到了更多的常数因子速度优化方法,请告诉我。我猜测,如果需要维护顺序,那么这些方法应该是联合操作的唯一可能优化。 - aa333
很多联合涉及到一个集合是另一个集合的真子集,或者仅涉及将一些元素附加到链的末尾。我从检测前一种情况中获得了巨大的加速(在其中,只返回较大的集合而不进行任何新分配)。我的代码已经到了自动数组边界检查和内存分配等方面占总运行时间相当大比例的地步。实际上,我打算在某个时候将一些代码包装在“unsafe”块中以跳过边界检查。 - Alex Godofsky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接