【问题标题】:how to reindex a sparse associative array如何重新索引稀疏关联数组
【发布时间】:2012-10-12 09:35:33
【问题描述】:

首先,这个问题与特定语言无关——我使用 Haxe 来针对多个平台——所以伪代码就绰绰有余了。

这是我的问题:我有一个sparse Matrix 的这种形式的描述:

edges = 
[
1,1,2,1,3,1,4,1,
2,2,3,2,
3,3,4,3,5,3,
4,4,5,4,6,4,
5,5,6,5,7,5,25,5,27,5,28,5,29,5,30,5
];

这描述了边关联:

  • 点 1 链接到点 1、2、3 和 4
  • 点 2 与点 2 和 3 相关联
  • 第 3 点链接到第 3、4 和 5 点
  • 第 4 点链接到第 4、5 和 6 点
  • 点 5 与点 5、6、7、25、27、28、29 和 30 相关联

现在我需要在 3D 中渲染它,为此,我需要将数据“压缩”到没有“间隙”的索引缓冲区中。 用上面的例子说,我需要得到:

newEdges = 
[ 
1,2, 1, 3, 1, 4,
2,3,
3,4, 3,5,
4,5, 4,6,
5,6, 5,7, 5,8, 5,9, 5,10, 5,11, 5,12
]

因此,连接自身的边(边 1-1、2-2、3-3 等)必须移除(简单)。

由于点顺序并不重要(边缘 1-2 = 边缘 2-1),我们还将删除重复的边缘(有点简单)。

现在棘手的部分是消除“差距”:因为 7 是最高连续值,而 25 是紧随其后的值,所以 25 必须变为 8,27 必须变为 9,28 必须变为 10,依此类推。

现在我使用 BitmapData,在其中将所有值绘制为 XY 坐标。 然后我递归地将这个位图的非空垂直条纹(1像素宽的矩形)复制到一个临时位图中。 然后我对水平条纹做同样的事情,最后扫描我的位图并将像素的 X 和 Y 值存储为边缘的 ID。

而且它有效!(至少它看起来是:)) 但是开销很糟糕,并且取决于输入矩阵,我可能无法生成位图(例如,flash 被限制为最大 4092 像素,JS 不太支持 copyPixels)。

所以问题是,如果没有位图和特定语言的方法,您将如何进行这种“间隙消除”?

希望这足够明确, 感谢您的关注。

尼古拉斯

【问题讨论】:

    标签: algorithm pseudocode sparse-matrix reindex


    【解决方案1】:

    E[m+1][m+1]为与edges对应的二维邻接矩阵,其中点索引的范围为[0..m]。

    f[n] 是在edges 中访问的n 点的排序数组。 通过创建f[n] 数组,我们在 [0..m] 范围内的非连续点索引和 [0..n-1] 范围内的连续点索引之间创建了一个映射。

    如下创建一个新的邻接矩阵G

    for i = 0..(n-1)
        for j = 0..(n-1)    // or, j = (i+1)..(n-1) for upper triangular portion
            G[i][j] = E[f[i]][f[j]]
        end
    end
    

    这将只需要 O(n^2) 而不是 O(m^2) 时间。

    编辑:删除了if 语句。如果 E 和 G 都初始化为全 0,则没有必要。

    【讨论】:

    • 嘿,谢谢!在某种程度上它是如此明显:) 这是我在线性数组上获得结果的方法: var value:Int = 0; var newValue:Int = 0; for( i in 0...edges.length ) { value = edges[ i ]; if( newValue
    【解决方案2】:

    由于您的矩阵是稀疏的,我建议您使用排序列表数据结构从边缘列表构建稀疏结构。对于每一行,您需要创建一个动态排序列表(升序),向其中添加边。例如,对于边(1,2),您将列2 插入到排序列表sorted_lists{1} 中。对于每行的少量条目(几百个),最好在排序列表中使用线性搜索,然后将较大的元素移动到列表的末尾。对于每行更多的条目,您可以使用二分法来找到正确的位置。我经常将这种方法用于有限元方法中出现的稀疏矩阵。以我的经验,这是最快的方法,并且它可以简单地并行化! (在线程之间分割行范围)

    这是一个实现排序列表的示例 MATLAB 代码:

    function list = sorted_list_insert(list, col)
    
    % special case for empty list
    if isempty(list)
        list = col;
        return;
    end
    
    % search for col position in the row
    % can be done with bisection,
    % but linear search is much faster for small number of entries per row
    it = 1;
    while it<length(list) && list(it)<col
        it = it+1;
    end
    
    % duplicate entry - do not add
    if list(it)==col
        return;
    end
    
    % insert col in proper position, move other elements in the list
    list = [list(1:it) col list(it+1:end)];
    end
    

    将一行中的所有条目添加到此排序列表的复杂度为O(number of entries per row ^ 2)

    接下来您要做的是检查边缘列表并添加列以正确的行排序列表 (sorted_lists{row})。在下面,edges 被假定为一个二维数组,其中edges(1,i) 是列,edges(2,i) 是行:

    % find maximum row id
    max_row = number of rows in the matrix
    
    % initialize sorted list structures for all rows - max_row empty lists
    sorted_lists = cell(max_row, 1);
    
    % create sorted rows
    nedges = total number of edges
    for it=1:nedges
        row = edges(2,it);
        col = edges(1,it);
        sorted_lists{row} = sorted_list_insert(sorted_lists{row}, col);
    end
    

    上述步骤的复杂度为O(number of rows * number of entries per row ^ 2)

    最后一件事是消除差距。对于排序列表,只需在排序列表中找到col 的位置即可轻松完成。您还必须添加偏移量。从您的数据看来,您处理的是矩阵的上三角部分(您说边缘中节点的顺序无关紧要)。所以偏移量只是行号(在 MATLAB 中为-1,因为它具有基于 1 的编号)

    % the positions of col in every row (plus offset)
    % are the new col id with removed gaps
    for it=1:nedges
        offset = edges(2,it)-1;
        edges(1,it) = offset + find(sorted_lists{edges(2,it)}==edges(1,it));
    end
    

    这是edges 使用上述代码处理后的样子:

    edges =
    
    Columns 1 through 13
    
     1     2     3     4     2     3     3     4     5     4     5     6     5
     1     1     1     1     2     2     3     3     3     4     4     4     5
    
    Columns 14 through 20
    
     6     7     8     9    10    11    12
     5     5     5     5     5     5     5
    

    该过程适用于已排序和未排序的边。它只假设col &gt;= row。您可以轻松实现。您还可以轻松添加对角线 (i,i) 边缘的移除。

    【讨论】:

    • 嘿,非常感谢您的深入解释 :) 我对非排序集和以前的答案有疑问。现在我按照你的方法(创建临时数据持有者而不是表),一切都很好。非常感谢
    • @nicoptere 很高兴你喜欢它。为此,插入排序实际上非常快。而且你不需要保留零条目,所以它适用于非常大的稀疏数组。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-12-29
    • 2020-07-09
    • 1970-01-01
    • 1970-01-01
    • 2011-09-18
    • 1970-01-01
    • 2015-02-20
    相关资源
    最近更新 更多