稀疏矩阵的最小表示

时间:2015-08-02 11:19:19

标签: algorithm matrix sparse-matrix

我有一个大的二进制稀疏矩阵(任何单元格都可以保存0或1作为值)。我不时想拍摄整个矩阵的快照。 快照必须尽可能小

矩阵表示2d地图和发生在区域中的事件,因此它更像是示例A的快照,而不是看起来像示例B的快照(它们都具有相同数量的" 1& #34;)虽然我需要支持算法中的两个例子。

Example A:
000000000000
000000011000
001100111100
001111111100
000000111100
000001111100
000000000000

Example B:
010010010010
001000001000
010010100100
000100101010
001000010010
010010001100
001000010000

由于数据可能不同于单一" 1"细胞到100%的细胞为" 1" (在非常非常后面的情况下)我认为我需要使用多个算法 - 并且在加载数据时使用与存储它相同的算法加载它。

例如,当只有一个单元格时,我将仅存储它的索引(以及"索引"算法的标识符),当99%的矩阵为" 1&#时34;我将它存储为位图("位图"算法的标识符)。

所以我得到一个像这样的通用算法:

  1. 对于每个表示法algorithem - 代表矩阵
  2. 选择最小的表示
  3. 以最小的代表存储数据
  4. 我的问题

    1. 我可以使用哪些算法 - 除了存储索引/位图?
    2. 处理此问题是否有良好的依据?
    3. 我怎样才能证明我的解决方案是最小的?
    4. 底线: 如何以最小的方式存储位图矩阵?

      修改 使用案例:我有一个稀疏矩阵,我需要通过一个非常低的带宽介质传输。因此,假设介质两侧的计算能力很强,发送矩阵应包含尽可能少的位。

2 个答案:

答案 0 :(得分:5)

数据压缩是一个很大的领域(你可以start here),而且你的问题没有明确的答案。如果我们知道如何以最佳速率压缩数据,那么每年都不会有新的压缩算法;)

话虽这么说,你建议的是一个好主意:从集合中选择最佳算法并在标题中识别它。实际上,大多数压缩格式都使用这种方案。

回答你的问题:

什么算法:

  • 使用现有格式:我会想到PNGGIF7zip
  • 没有压缩:位图,显然,但为了确保我们说的是同一件事,你需要为每个元素编码1位(而不是1个字节)。大多数语言都不容易使用位访问(大多数bool / boolean类型都存储在一个完整的字节中)。您必须使用按位运算,专用语言功能,例如bitfields / bitsets或库。例如C++中,std::vector<bool>实现了一个真正的位图(简单的bool[]没有)。
  • 熵编码:使用比不太可能的存储更少的存储来编码大多数可能的配置的想法。
    • 稀疏存储,你所谓的索引,但与你暗示的相反,当有很多,但也有很多零(只是否定结果!)时它很有用。如果只有一个0或仅一个1是对称的,则应以相同的方式处理。备用存储的最坏情况是拥有与零一样多的存储空间。
    • 块编码,这意味着如果您知道1111可能的模式,则可以将min = E( -log2(P(message)) ) 存储在少于4位中。但这意味着将使用超过4位存储另一个(不太可能)的4位模式,因此在选择模式时要小心。有很多方法可以做到这一点:固定大小或可变大小的单词,编码可以是静态的,也可以是数据相关的。一些示例包括:RLEHuffmanLZ变体(您喜欢的压缩程序中使用的基于字典的算法)
    • Arithmetic coding基于相同的想法,即根据其概率调整每个符号/块的存储空间,但它一次编码整个数据,而不是将其分成块,这通常会导致更好编码方案。
    • 由于您的数据是二维的,因此通过2D块而不是1D块处理它是有意义的,因此例如您可以编码8x8块。例如JPEG就是这样。
  • 数据准备:在应用熵编码算法(实际减少数据大小的算法)之前,应用双向函数(双向函数)通常很有意思,这并不会减少数据大小)来编码您对数据模型的了解。在您的情况下,您说您的数据代表事件,而这些事件通常是特别聚集的。有没有办法将其表示为事件列表+它们传播的方式?或者您可以使用某些图像压缩方案,例如Fourier transform或某种wavelet transform。或者你可以简单地使用contour matrix(一个值在两个单元格之间变化时,当值恒定时为零),这可能会增加矩阵的稀疏性(它在你的例子A )

极简证明:

由于压缩实际上取决于数据的概率分布,因此通常没有最佳压缩算法。实际上,你知道你的矩阵总是一样的,你甚至不需要对它进行编码。你知道它是两个可能的矩阵之一,只有一个就足以编码它是哪一个。

在一般情况下,Shanon的entropy估计编码消息所需的理论最小位数:

E

其中P是对所有可能消息的期望,P是概率函数。但是在实践中,你不知道000000000000 000000011000 001100111100 001111111100 000000111100 000001111100 000000000000 ,所以你能做到的最好比以前的最佳算法做得更好;)

一般而言,您尝试压缩数据的次数越多,程序就越昂贵,无论是在运行时资源(CPU周期和内存)还是开发工作方面。

示例

只是将它放在例子1 的实践中,给你灵感 - 即使从1个例子设计是一个非常糟糕的主意,因为它几乎没有给你代表性的概率!

初始数据(我将始终省略大小标题,因为它将保持不变) - 位图,84位(25个):

index

position方案中,输出一个列表position。如果您进行概括,则可以使用pattern + pattern列表。例如,0可以是连续的数量,因此您不需要为一个块输出多个位置。让我们进一步说,我们编码2D模式,定义如下:

  • 3位大小(0到7)
  • 1位形状:

    • 1111表示一行 - 例如1是一行大小为4的行;
    • 11 11 表示正方形 - 例如:

      position

      是2的正方形。

然后让我们说取相对而不是绝对位置,这意味着每个-- block #1 00001 position +1 absolute position from top-left, since this is the first block 001 pattern-size 1 0 pattern-shape is row for size 1, square and row are the same (which means my coding isn't optimal) -- block #2 00100 position +4 relative to last position 1, this means position 5 010 pattern-size 2 1 pattern-shape is square -- decoded output below 0100011000... 0000011000... 0000000000... ............. 编码自前一个位置以来你前进了多少。以您为例,我选择5位位置。以下是解码的工作原理:

10100 (position [0]+20 from top-left)
010 0 (size: 2, shape: row)
00110 (position [20]+6)
010 1 (size: 2, shape: square)
00100 (position +4)
100 1 (size: 4, shape: square)
01000 (position +8)
100 0 (size: 4, shape: row)
11011 (position +27)
001 0 (size 1, shape: row)

因此,使用此方案,您可以使用45位对数据进行编码:

shape

注意:通常,您希望存储标题以了解块的数量(在本例中为5),但您可以从文件/网络有效负载大小中推断出它。同样在这个例子中,我们只需要3个模式大小(1,2,4),因此我们可以将大小存储在两个位上并将其增加到功率2,使有效载荷为40位,但这使得该方案过于专业化。此外,至少有一个无意义的position(这里有两个:000/0和000/1)是必要的,因为在{{1}中你没有足够的位数编码一个大的&#34;洞&#34;模式之间。例如,这个20 x 3矩阵:

10000000000000000000
00000000000000000000
00000000000000000001

在0和59位置有一个。由于我不能以59位跳转编码59,我们必须跳两次,我们将其编码为:

00000 (+0)  001 0 (a single 1)
11111 (+31) 000 0 (nothing)
11100 (+28) 001 0 (a single 1)

答案 1 :(得分:1)

您已经提到了一些明显的方法来存储这些 - 1单元格的位图和索引。通过编码“索引”的标识符,可以很容易地扩展索引思想。方法和1个单元格的数量,后跟那么多对坐标。您甚至可以尝试按行(或列)分组压缩,如

rowNumber colNumber1 colNumber2 ... -1

使用-1或其他一些标志值来表示行的结尾。这可以为只有少量条目的大尺寸矩阵节省大量空间。您还需要存储矩阵大小。

对于例A,你得到(使用0索引,空白只是为了便于阅读)

7 12 //dimensions
1 7 8 -1 
2 2 3 6 7 8 9 -1
3 2 3 4 5 6 7 8 9 -1
4 6 7 8 9 -1
5 5 6 7 8 9 -1

对于您的情况,存储它的方法的另一个示例可能如下 - 您必须进行实验,看看它在压缩&#39;信息。算法的想法来自观察在你的A例子中,几乎所有行都只有一个大的连接部分1s,被0包围。

假设您的矩阵可以有任何维度,第一步就是编码。因此对于n * m矩阵,只需存储这两个整数。

在此之后,对于每一行,以下内容如何:

  • 存储左侧连接的0的数量。
  • 存储此后连接的1的数量。
  • 重复直到行结束。

要解码,您只需遵循以下流程:

    每行
  • (在给定的行数中)
    • count为目前为止读取的矩阵条目数。初始化为0
    • next成为布尔值,表示下一个数字编码的值。初始化为false
    • 阅读下一个号码
      • 将等于next的数量的值插入相应的行。
      • 翻转next
      • 按该号码增加count
    • 循环直到count == m(如果count > m出错了)
  • 循环直到所有行都被读取

我希望我能很好地解释这个想法。正如我所说,我不知道它的表现如何,但它源于对你的例子的观察。要真正压缩它,您可以确保每个数字条目只需要尽可能多的位来计算(并且不高于)行宽(m在我的伪代码中。)

示例A会出现类似这样的内容(空白只是为了便于阅读):

7 12 //dimensions
12
7 2 3
2 2 2 4 2
2 8 2
6 4 2
5 5 2
12

如果您对这些数字进行了优化,那么每个数字只需要4位。

我不会尝试做例B,但它的表现会更差