Algorithm 如何高效地存储具有高度冗余值的矩阵
我有一个非常大的矩阵(100M行乘以100M列),它有很多重复的值,它们紧挨着彼此。例如:Algorithm 如何高效地存储具有高度冗余值的矩阵,algorithm,data-structures,sparse-matrix,matrix-multiplication,Algorithm,Data Structures,Sparse Matrix,Matrix Multiplication,我有一个非常大的矩阵(100M行乘以100M列),它有很多重复的值,它们紧挨着彼此。例如: 8 8 8 8 8 8 8 8 8 8 8 8 8 8 4 8 8 1 1 1 1 1 8 8 8 8 8 4 8 8 1 1 1 1 1 8 8 8 8 8 4 8 8 1 1 1 1 1 8 8 8 8 8 4 8 8 1 1 1 1 1 8 8 8 8 8 4 8 8 1 1 1 1 1 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 3 3 3 3 3 3 3 3 3
8 8 8 8 8 8 8 8 8 8 8 8 8
8 4 8 8 1 1 1 1 1 8 8 8 8
8 4 8 8 1 1 1 1 1 8 8 8 8
8 4 8 8 1 1 1 1 1 8 8 8 8
8 4 8 8 1 1 1 1 1 8 8 8 8
8 4 8 8 1 1 1 1 1 8 8 8 8
8 8 8 8 8 8 8 8 8 8 8 8 8
8 8 3 3 3 3 3 3 3 3 3 3 3
我想要一个数据结构/算法来尽可能紧凑地存储这样的矩阵。例如,上面的矩阵应该只占用O(1)空间(即使矩阵被任意拉伸),因为只有固定数量的矩形区域,其中每个区域只有一个值
重复在行和列之间都会发生,因此逐行压缩矩阵的简单方法不够好。(这将需要最少的O(num_行)空间来存储任何矩阵。)
矩阵的表示也需要逐行访问,以便我可以对列向量执行矩阵乘法。您可以将矩阵存储为包含单个值的叶子。可以把这看作是一个二维的“运行”值。如果您的数据确实是规则的,那么以结构化格式存储数据可能会使您受益匪浅;e、 g.示例矩阵可能存储为以下“填充矩形”说明列表:
(0,0)-(13,7) = 8
(4,1)-(8,5) = 1
(然后,要查找特定单元格的值,您需要在列表中向后迭代,直到找到包含该单元格的矩形)对于您显示的矩阵,我没有具体的答案。在有限元分析(FEA)中,矩阵包含冗余数据。在我的UnderGrad项目中实现FEA包时,我使用了skyline存储方法 一些链接:
最简单的方法是在一个维度上使用游程编码,而不用担心另一个维度 (如果数据集不是那么巨大,那么将其解释为图像并使用标准的无损图像压缩方法也会非常简单——但由于您必须致力于使算法在稀疏矩阵上工作,因此不会那么简单。) 另一个简单的方法是尝试矩形泛光填充——从右上角的像素开始,将其增大到最大的矩形(宽度优先);然后将所有这些像素标记为“完成”,并取右上角剩余的像素,重复操作直到完成。(您可能希望将这些矩形存储在某种BSP或四叉树中。)
一种高效的技术——虽然不是最优的,但可能已经足够好了——是使用一个二进制空间分区树,其中“空间”不是通过空间来衡量的,而是通过变化的数量来衡量的。您可以递归地进行剪切,以便在左侧和右侧(或顶部和底部——可能您希望保持正方形)有相同数量的更改,并且随着尺寸变小,您可以剪切尽可能多的更改。最终,你会把两个长方形分开,每个长方形都有相同的数字;然后停下来。(通过RLE在x和y中编码将快速告诉您更改点在哪里。)首先要尝试的始终是现有的库和解决方案。让自定义格式与最终需要的所有操作一起工作是一项艰巨的工作。稀疏矩阵是一个老问题,所以一定要阅读现有的东西 假设您没有找到合适的格式,我建议使用基于行的格式。不要对超紧凑表示法太过花哨,代码中的每一个小操作和bug都需要大量处理。相反,尝试分别压缩每一行。你知道你将不得不扫描每一行的矩阵向量乘法,让生活变得简单
我将从运行长度编码开始,首先看看它是如何工作的。一旦成功,尝试添加一些技巧,如对前一行的节的引用。所以一行可以被编码为:126个零,8个1,1000个直接从上面的行复制的条目,32个零。对于您给出的示例,这似乎非常有效。以上许多解决方案都很好
如果您正在处理文件,请考虑面向文件 压缩工具,如compress、bzip、zip、bzip2和friends。 它们工作得非常好,尤其是当数据包含冗余数据时 ASCII字符。使用外部压缩工具可以消除 代码中的问题和挑战,并将压缩 二进制和ASCII数据
在您的示例中,显示的是一个字符的数字。 数字0-9可以用较小的四位表示 编码模式。您可以在中使用附加位 一个字节作为计数。四位给了你额外的代码 逃到临时演员。。。但有一点值得注意 回到使用两个字符的旧千年虫 一年。来自ofset的字节编码将给出 255年,相同的两个字节将跨越所有已写入的历史,然后是一些。您可能想看看它的压缩算法。把你的矩阵想象成一个位图…你对100M x 100M大小矩阵的O(1)空间的描述令人困惑。当你有一个有限的矩阵,那么你的大小是一个常数(除非生成矩阵的程序没有改变它)。因此,存储所需的空间量也是一个常数,即使将其与标量相乘。当然,读取和写入矩阵的时间不会是O(1) 稀疏矩阵是我可以想到的,它可以减少存储这样一个矩阵所需的空间量。您可以将此稀疏矩阵写入文件并将其存储为tar.gz,这将进一步压缩数据
我有一个问题,100米的米代表什么?它是指兆字节/百万吗?如果是,此矩阵大小将为100 x 10^6 x 100 x 10^6字节=10^16/10^6 MB=10^10/10^6 TB=10^4 TB!!!你在用什么样的机器?你知道。。。。间隔树 区间树是一种高效存储区间并查询它们的方法。一个泛化就是广告,它可以是广告
0,0-n,n --> 8
4,4-7,7 --> 1
8,8-8,n --> 3
A
| w0 w1 w2 |
| x0 x1 x2 |
| y0 y1 y2 |
| z0 z1 z2 |
A’
| w0 w1 w2 x0 x1 x2 y0 y1 y2 z0 z1 z2 |
0 | 8 8 8 8 8 8 8 8 8 8 8 8 8 |
1 | 8 4 8 8 1 1 1 1 1 8 8 8 8 |
2 | 8 4 8 8 1 1 1 1 1 8 8 8 8 |
3 | 8 4 8 8 1 1 1 1 1 8 8 8 8 |
4 | 8 4 8 8 1 1 1 1 1 8 8 8 8 |
5 | 8 4 8 8 1 1 1 1 1 8 8 8 8 |
6 | 8 8 8 8 8 8 8 8 8 8 8 8 8 |
7 | 8 8 3 3 3 3 3 3 3 3 3 3 3 |
0 | 8{13} |
1 | 8{1} 4{1} 8{2} 1{5} 8{4} |
2 | 8{1} 4{1} 8{2} 1{5} 8{4} |
3 | 8{1} 4{1} 8{2} 1{5} 8{4} |
4 | 8{1} 4{1} 8{2} 1{5} 8{4} |
5 | 8{1} 4{1} 8{2} 1{5} 8{4} |
6 | 8{13} |
7 | 8{2} 3{11} |
A: 5 (1-5) | 8{1} 4{1} 8{2} 1{5} 8{4} |
B: 2 (0,6) | 8{13} |
C: 1 7 | 8{2} 3{11} |
0|1
/ \
A 0|1
/ \
B C
0 1 2 3 4 5 6 7
=================================
0 | 3 3 3 3 3 3 3 3 |
|-------+ +-------|
1 | 4 4 | 3 3 3 3 | 4 4 |
| +-----------+---+ |
2 | 4 4 | 5 5 5 | 1 | 4 4 |
| | | | |
3 | 4 4 | 5 5 5 | 1 | 4 4 |
|---+---| | | |
4 | 5 | 0 | 5 5 5 | 1 | 4 4 |
| | +---+-------+---+-------|
5 | 5 | 0 0 | 2 2 2 2 2 |
| | | |
6 | 5 | 0 0 | 2 2 2 2 2 |
| | +-------------------|
7 | 5 | 0 0 0 0 0 0 0 |
=================================
0 1 2 3 4 5 6 7
=================================
0 | 3 | 3 | | | 3 | 3 |
|---+---| 3 | 3 |---+---|
1 | 4 | 4 | | | 4 | 4 |
|-------+-------|-------+-------|
2 | | | 5 | 1 | |
| 4 | 5 |---+---| 4 |
3 | | | 5 | 1 | |
|---------------+---------------|
4 | 5 | 0 | 5 | 5 | 5 | 1 | 4 | 4 |
|---+---|---+---|---+---|---+---|
5 | 5 | 0 | 0 | 2 | 2 | 2 | 2 | 2 |
|-------+-------|-------+-------|
6 | 5 | 0 | 0 | 2 | 2 | 2 | 2 | 2 |
|---+---+---+---|---+---+---+---|
7 | 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
=================================
0 +- 0 +- 0 -> 3
| +- 1 -> 3
| +- 2 -> 4
| +- 3 -> 4
+- 1 -> 3
+- 2 -> 4
+- 3 -> 5
1 +- 0 -> 3
+- 1 +- 0 -> 3
| +- 1 -> 3
| +- 2 -> 4
| +- 3 -> 4
+- 2 +- 0 -> 5
| +- 1 -> 1
| +- 2 -> 5
| +- 3 -> 1
+- 3 -> 4
2 +- 0 +- 0 -> 5
| +- 1 -> 0
| +- 2 -> 5
| +- 3 -> 0
+- 1 +- 0 -> 5
| +- 1 -> 5
| +- 2 -> 0
| +- 3 -> 2
+- 2 +- 0 -> 5
| +- 1 -> 0
| +- 2 -> 5
| +- 3 -> 0
+- 3 +- 0 -> 0
+- 1 -> 2
+- 2 -> 0
+- 3 -> 0
3 +- 0 +- 0 -> 5
| +- 1 -> 1
| +- 2 -> 2
| +- 3 -> 2
+- 1 +- 0 -> 4
| +- 1 -> 4
| +- 2 -> 2
| +- 3 -> 2
+- 2 +- 0 -> 2
| +- 1 -> 2
| +- 2 -> 0
| +- 3 -> 0
+- 3 +- 0 -> 2
+- 1 -> 2
+- 2 -> 0
+- 3 -> 0
((1*4) + 3) + ((2*4) + 2) + (4 * 8) = 49 leaf nodes
49 * (2 + 1) = 147 (2 * 8 bit indexer, 1 byte data)
+ 14 inner nodes -> 2 * 14 bytes (2 * 8 bit indexers)
= 175 Bytes
0 1 2 3 4 5 6 7
=================================
0 | 3 3 3 3 3 3 3 3 |
|-------+---------------+-------|
1 | 4 4 | 3 3 3 3 | 4 4 |
| +-----------+---+ |
2 | 4 4 | 5 5 5 | 1 | 4 4 |
| | | | |
3 | 4 4 | 5 5 5 | 1 | 4 4 |
|---+---| | | |
4 | 5 | 0 | 5 5 5 | 1 | 4 4 |
| + - +---+-------+---+-------|
5 | 5 | 0 0 | 2 2 2 2 2 |
| | | |
6 | 5 | 0 0 | 2 2 2 2 2 |
| +-------+-------------------|
7 | 5 | 0 0 0 0 0 0 0 |
=================================
0: (4,1; 4,1), (5,1; 6,2), (7,1; 7,7) | 3
1: (2,5; 4,5) | 1
2: (5,3; 6,7) | 1
3: (0,0; 0,7), (1,2; 1,5) | 2
4: (1,0; 3,1), (1,6; 4,7) | 2
5: (2,2; 4,4), (4,0; 7,0) | 2
0 | 3 {8} | 1
1 | 4 {2} | 3 {4} | 4 {2} | 2
2,3 | 4 {2} | 5 {3} | 1 {1} | 4 {2} | 4
4 | 5 {1} | 0 {1} | 5 {3} | 1 {1} | 4 {2} | 5
5,6 | 5 {1} | 0 {2} | 2 {5} | 3
7 | 5 {1} | 0 {7} | 2
RLE Data: (1 + 3+ 4 + 5 + 3 + 2) * 2 = 36
Bit Stream: 20 bits packed into 3 bytes = 3
Huffman Tree: 10 nodes * 3 = 30
= 69 Bytes
3{8};4{2};3{4};4{4};5{3};1{1};4{4};5{3};1{1};4{2};5{1};0{1};
5{3};1{1};4{2};5{1};0{2};2{5};5{1};0{2};2{5};5{1};0{7}
= 2 * 23 = 46 Bytes
3{8};
4{2};3{4};
4{4};5{3};1{1};
4{4};5{3};
1{1};4{2};5{1};0{1};5{3};
1{1};4{2};5{1};0{2};2{5};
5{1};0{2};2{5};
5{1};0{7}
0 + 0 -> 3{8};4{2};3{4};
+ 1 -> 4{4};5{3};1{1};
1 + 0 -> 4{2};5{1} + 0 -> 0{1};5{3};1{1};
| + 1 -> 0{2}
|
+ 1 -> 2{5};5{1} + 0 -> 0{2};
+ 1 -> 0{7}
3{8};4{2};3{4} | 00
4{4};5{3};1{1} | 01
4{4};5{3};1{1} | 01
4{2};5{1};0{1};5{3};1{1} | 100
4{2};5{1};0{2} | 101
2{5};5{1};0{2} | 110
2{5};5{1};0{7} | 111
Bit stream: 000101100101110111
RLE Data: 16 * 2 = 32
Tree: : 5 * 2 = 10
Bit stream: 18 bits in 3 bytes = 3
= 45 bytes
A = [1 -1 0 0 0]
[0 1 -1 0 0]
[0 0 1 -1 0]
[0 0 0 1 -1]
[0 0 0 0 1]
B = [1 1 1 1 1]
[0 1 1 1 1]
[0 0 1 1 1]
[0 0 0 1 1]
[0 0 0 0 1]
Mv = B AMA B v