我试图通过例子解释:
想象一下编号元素的列表E = [elem0,elem1,elem2,...]。
现在一个索引集可以{42,66,128}引用E中的元素。这个集合中的排序并不重要,所以{42,66,128} == {66,128,42},但是每个元素在任何给定的索引集中最多只有一次(因此它是一个实际的集合)。
我现在想要的是一个节省空间的数据结构,它给我另一个有序列表M,其中包含引用E中元素的索引集.M中的每个索引集只会出现一次(因此M就是这方面的一个集合)但是M必须是可索引的(因此M在这个意义上是List,因此精确的索引并不重要)。如有必要,可以强制索引集包含相同数量的元素。
例如,M可能看起来像:
0: {42, 66, 128}
1: {42, 66, 9999}
2: {1, 66, 9999}
我现在可以执行以下操作:
for(i in M[2]) { element = E[i]; /* do something with E[1],E[66],and E[9999] */ }
你可能会看到它的发展方向:你现在可能有另一个地图M2,它是指向M的有序列表,最终指向E中的元素。
正如你在这个例子中看到的那样,索引集可以相对相似(M [0]和M [1]共享前两个条目,M [1]和M [2]共享最后两个)这使得我认为必须有比使用数组的天真方式更有效的东西。但是,我可能无法提出保证良好“共享”的索引条目的良好全局排序。
我可以想到任何事情,从表示M作为树(其中M的索引来自深度优先搜索顺序或其他东西)到联合查找结构的哈希映射(不知道它将如何工作:)
对于任何这样的教科书数据结构的指针都非常受欢迎(数据库世界中有什么吗?)但我也很感激你提出一个“自制的”解决方案或只是随意的想法。
空间效率对我来说很重要,因为E可能包含数千甚至数百万个元素,(某些)索引集可能很大,至少某些索引集之间的相似性应该很大,并且可能存在多层映射。 / p>
非常感谢!
答案 0 :(得分:1)
击败指数将非常困难。您可以使用正确的数据类型来节省一些空间(例如,在gnu C中,短,如果E中的元素少于64k, int ,如果< 4G ...)。
此外,
由于您说E中的顺序并不重要,您可以对E进行排序,使其最大化连续元素以尽可能地匹配Ms。
例如,
E: { 1,2,3,4,5,6,7,8 }
0: {1,3,5,7}
1: {1,3,5,8}
2: {3,5,7,8}
重新安排E
E: { 1,3,5,7,8,2,4,6 }
并使用E索引而不是值,您可以根据E的子集定义Ms,给出索引
0: {0-3} // E[0]: 1, E[1]: 3, E[2]: 5, E[3]: 7 etc...
1: {0-2,4}
2: {1-3,4}
这样
0-3
表示0,1,2,3,困难的部分是使算法重新排列E,以便最大化子集大小 - 最小化Ms大小。
E重新安排算法建议
algo构建一个地图,它为一个元素'x'提供了邻居'y'的列表,以及 points ,'y'就在'x'之后的次数
Map map (x,y) -> z
for m in Ms
for e,f in m // e and f are consecutive elements
if ( ! map(e,f)) map(e,f) = 1
else map(e,f)++
rof
rof
重新安排E
ER = {} // E rearranged
Map mas = sort_map(map) // mas(x) -> list(y) where 'y' are sorted desc based on 'z'
e = get_min_elem(mas) // init with lowest element (regardless its 'z' scores)
while (mas has elements)
ER += e // add element e to ER
f = mas(e)[0] // get most likely neighbor of e (in f), ie first in the list
if (empty(mas(e))
e = get_min_elem(mas) // Get next lowest remaining value
else
delete mas(e)[0] // set next e neighbour in line
e = f
fi
elihw
算法(地图)应为O(n*m)
空格,E中有n个元素,所有Ms中有m个元素。
答案 1 :(得分:1)
您可以合并M中的所有数字并删除重复项,并将其命名为UniqueM。
所有M [X]集合都转换为位掩码。例如,int值可以存储32个数字(为了支持无限计数,你应该存储整数数组,如果数组大小为10,我们可以存储320个不同的元素)。 long类型可以存储64位。
E: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
M[0]: {6, 8, 1}
M[1]: {2, 8, 1}
M[2]: {6, 8, 5}
将转换为:
UniqueM: {6, 8, 1, 2, 5}
M[0]: 11100 {this is 7}
M[1]: 01110 {this is 14}
M[2]: 11001 {this is 19}
注意:强> 你也可以结合我和ring0方法,而不是重新排列E make new UniqueM并在其中使用区间。
答案 2 :(得分:1)
可以使用位数组。它们是元素a[i]
的数组,1
如果i
已设置,0
如果i
未设置。因此,即使它包含少量成员也不包含任何成员,每个集合都会占用size(E)
位。不是那么节省空间,但是如果用一些压缩算法压缩这个数组,它的大小会小得多(可能达到最终的熵限制)。因此,您可以尝试动态马尔可夫编码器或RLE或组合霍夫曼,并为您选择最有效的。然后,迭代过程可以包括动态解压缩,然后是1
位的线性扫描。对于looong 0
运行,您可以修改解压缩算法以检测此类情况(RLE是最简单的情况)。
如果您发现集合具有较小的差异,则可以存储集A
和A xor B
而不是A
和B
,以节省公共部分的空间。在这种情况下,要迭代B
,您必须将A
和A xor B
以及xor
解包。
答案 3 :(得分:0)
另一个有用的解决方案:
E: {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
M[0]: {1, 2, 3, 4, 5, 10, 14, 15}
M[1]: {1, 2, 3, 4, 5, 11, 14, 15}
M[2]: {1, 2, 3, 4, 5, 12, 13}
缓存常用项目:
Cache[1] = {1, 2, 3, 4, 5}
Cache[2] = {14, 15}
Cache[3] = {-2, 7, 8, 9} //Not used just example.
M[0]: {-1, 10, -2}
M[1]: {-1, 11, -2}
M[2]: {-1, 12, 13}
将指向缓存列表的链接标记为负数。