【问题标题】:Sorting a binary 2D matrix?对二进制二维矩阵进行排序?
【发布时间】:2009-11-19 01:57:18
【问题描述】:

我在这里寻找一些建议,因为我不太清楚从哪里开始研究这个。

我有一个二维矩阵,每个单元格为0或1,如:

  1 2 3 4
A 0 1 1 0
B 1 1 1 0
C 0 1 0 0
D 1 1 0 0

我想对其进行排序,使其尽可能“上三角形”,如下所示:

  4 3 1 2
B 0 1 1 1
A 0 1 0 1
D 0 0 1 1
C 0 0 0 1

行和列必须保持不变,即元素不能单独移动,只能“整体”交换。

我了解可能存在矩阵具有多个可能的排序结果(即形状相同,但“原始”行/列的标识不同)的病态情况。

那么,谁能建议我在哪里可以找到一些起点?现有的库/算法会很棒,但我会满足于知道我要解决的问题的名称!

我怀疑这是一个线性代数问题,也许有某种适用的图像处理技术。

除了任何其他想法,我最初的猜测只是在行上编写一个简单的插入排序,然后在列上进行迭代,直到它稳定(并希望检测病理情况不会太难。)

更多细节:关于我正在尝试做的一些更多信息可能有助于澄清。每一行代表一个竞争对手,每一列代表一个挑战。每个 1 或 0 代表参赛者在特定挑战中的“成功”。

通过对矩阵进行排序,使所有的1都在右上角,我希望然后提供每个挑战的内在难度排名和竞争对手的排名(这将考虑到他们成功挑战的难度在,而不仅仅是成功的次数。)

关于接受答案的说明:我已接受模拟退火作为“答案”,但需要注意的是这个问题没有正确答案。这似乎是一个不错的方法,尽管我实际上还没有想出一个适合我的问题的评分函数。

【问题讨论】:

  • 问题:(1) 请注意,对于全 1 的矩阵,您无能为力:您可以吗? (2)一旦对角线下方没有零,您是否关心1在对角线上方的位置? (3) 最小化对角线下方 1 的数量是否足够好?如何简单地减少对角线下方(至少)为 1 的行数?
  • Answer 1) 是的,全零或全一永远不会出现,如果它们发生了,那么根据定义,它们都将被认为是等价的,因此将它们分类为其他排列不会是个问题。
  • Answer 2+3) 是的,我希望 1 尽可能靠近每列的顶部,即右上角的 1 尽可能多。请注意,对角线下方可能有 1,上方可能有 0,这不是严格意义上的三角矩阵。
  • 您需要对什么大小的矩阵进行排序?您可以使用简单地尝试行和列的每种组合的蛮力方法逃脱吗?这意味着 n!平方组合,所以 5 行仍然是相当可行的,但 6 行/列将是 50 万个组合。
  • 矩阵会非常大,至少 100s x 100s,希望它们可以随着新行/列的添加而逐步解决。

标签: algorithm sorting matrix


【解决方案1】:

基于simulated annealing 的算法可以轻松处理这类事情太多麻烦。如果你有一个很可能有一个固定解决方案的小矩阵,那就不好了,但是如果你的矩阵变得更大并且问题变得更加困难,那就太好了。

(但是,它也辜负了您可以增量完成插入的愿望。)

预赛

  1. 设计一个对矩阵“评分”的性能函数 - 更接近三角形的矩阵应该比那些不太三角形的矩阵获得更好的分数。

  2. 设计一组在矩阵上允许的操作。您的描述有点模棱两可,但如果您可以交换行,那么一个操作将是 SwapRows(a, b)。另一个可能是SwapCols(a, b)

退火循环

我不会在这里给出完整的阐述,但这个想法很简单。您使用您的操作对矩阵执行随机变换。您测量操作后矩阵的“更好”程度(使用操作前后的性能函数)。然后,您决定是否提交该转换。你重复这个过程很多

决定是否提交转换是有趣的部分:您需要决定是否执行该操作。在退火过程结束时,您只接受提高矩阵分数的转换。但早些时候,在更混乱的时期,您允许进行不会提高分数的转换。一开始,算法是“热的”,什么都可以。最终,算法冷却下来,只允许进行良好的转换。如果对算法进行线性冷却,那么是否接受变换的选择是:

public bool ShouldAccept(double cost, double temperature, Random random) {
    return Math.Exp(-cost / temperature) > random.NextDouble();
}

您应该阅读Numerical Recipes 中包含的优秀信息以获取有关此算法的更多信息。

长话短说,您应该学习其中的一些通用算法。这样做可以让您解决大量难以通过分析解决的问题。

评分算法

这可能是最棘手的部分。您将需要设计一个计分器来引导退火过程朝着您的目标前进。记分器应该是一个连续函数,当矩阵接近理想解时会产生更大的数字。

你如何衡量“理想解决方案”——三角形?这是一个天真的得分手:对于每一分,您都知道应该是1 还是0。如果矩阵是正确的,则为分数添加 +1,如果它是错误的,则为 -1。这是一些代码,所以我可以明确(未经测试!请查看!)

int Score(Matrix m) {
    var score = 0;
    for (var r = 0; r < m.NumRows; r++) {
        for (var c = 0; c < m.NumCols; c++) {
            var val = m.At(r, c);
            var shouldBe = (c >= r) ? 1 : 0;
            if (val == shouldBe) {
                score++;
            }
            else {
                score--;
            }
        }
    }
    return score;
} 

使用此评分算法,1 和 0 的随机字段将给出 0 分。“相反”三角形将给出最负分,正确的解决方案将给出最正分。区分两个分数会给您带来成本。

如果这个记分器不适合你,那么你需要“调整”它,直到它产生你想要的矩阵。

此算法的前提是调整此记分器比设计用于排序矩阵的最优算法要简单得多。

【讨论】:

  • 是的,但是这些“通用算法”通常也很难找到真正的最佳解决方案——它们通常需要很长时间才能收敛,或者陷入局部最小值。你能证明针对这个特定问题通过模拟退火获得的结果吗?
  • 这应该可以工作,尽管是以非确定性的方式。 (与迄今为止的其他回复不同......)+1。也许您可以建议在混合中引入“捏”的分析/启发式技巧,例如通过识别只有零的行或列,并将它们分别放在底部/左侧并使这些不可移动 w/r 到允许转换。
  • 关键点(至少我被卡住的地方)是得分函数。鉴于此,模拟退火肯定可以工作,但你怎么知道矩阵的“三角形-y”?是的,两个允许的操作是 SwapRow(a,b) 和 SwapCol(a,b)。
  • @ShreevatsaR,冷静我的朋友。我发现每当我使用模拟退火时,它都避免了“非常糟糕”的局部最小值,但当然它并不完美。任何具有实际实现的搜索算法都会陷入一些陷阱。这些算法最适合大型数据集,我在第一段中说过。我证明了什么吗?不!我只是提出了一个强大的算法。
  • @mjv 很好的建议 - 但我不想在如此漂亮的幼稚算法中添加启发式算法。 :-)
【解决方案2】:

我想出了下面的算法,它似乎可以正常工作。

第 1 阶段:将1s 最多的行向上移动,1s 最多的列向右移动。

  1. 首先是行。通过计算它们的1s 对行进行排序。我们不在乎 如果 2 行具有相同数量的 1s。
  2. 现在是列。排序cols 数着他们的1s。我们不在乎 如果 2 个 cols 的数量相同 1s.

第 2 阶段:重复 第 1 阶段,但使用额外的标准,以便我们满足三角矩阵变形。
行标准:如果 2 行具有相同数量的1s,我们将上移以较少0s 开头的行。

cols 的标准:如果 2 个 cols 具有相同数量的 1s,我们将 移动底部具有较少 0s 的 col。


例子:

第一阶段

  1 2 3 4                     1 2 3 4                   4 1 3 2
A 0 1 1 0                   B 1 1 1 0                 B 0 1 1 1
B 1 1 1 0  - sort rows->    A 0 1 1 0  - sort cols->  A 0 0 1 1
C 0 1 0 0                   D 1 1 0 0                 D 0 1 0 1
D 1 1 0 0                   C 0 1 0 0                 C 0 0 0 1

第二阶段

  4 1 3 2                     4 1 3 2
B 0 1 1 1                   B 0 1 1 1
A 0 0 1 1  - sort rows->    D 0 1 0 1  - sort cols-> "completed"
D 0 1 0 1                   A 0 0 1 1
C 0 0 0 1                   C 0 0 0 1

编辑:事实证明,我的算法并不总是给出正确的三角矩阵。
例如:

第一阶段

   1 2 3 4                    1 2 3 4                
A  1 0 0 0                  B 0 1 1 1                
B  0 1 1 1 - sort rows->    C 0 0 1 1  - sort cols-> "completed"
C  0 0 1 1                  A 1 0 0 0                
D  0 0 0 1                  D 0 0 0 1                

第二阶段

   1 2 3 4                    1 2 3 4                   2 1 3 4
B  0 1 1 1                  B 0 1 1 1                 B 1 0 1 1
C  0 0 1 1 - sort rows->    C 0 0 1 1  - sort cols->  C 0 0 1 1
A  1 0 0 0                  A 1 0 0 0                 A 0 1 0 0
D  0 0 0 1                  D 0 0 0 1                 D 0 0 0 1
                           (no change)

(*)也许第三阶段会增加良好的结果。在那个阶段,我们将以较少0s 开头的行放在顶部。

【讨论】:

  • 这是一个不起作用的输入:考虑[1 0 0 0], [0 1 1 1], [0 0 1 1], [0 0 0 1](已经是上三角形)。在它上面使用你的算法到达[1 0 1 1], [0 0 1 1], [0 0 0 1], [0 1 0 0],事实并非如此。 (如果没有给出初始形式,而是从后一个矩阵开始,那么算法不会改变任何东西:它找不到上三角形式。)
  • 或者更简单的 3x3 示例:[1 0 0], [0 1 1], [0 0 1].
  • @ShreevatsaR,你是对的,谢谢。它并不总是产生三角矩阵。但是,它没有给出你说的矩阵。也许您没有正确应用这些步骤。检查我的编辑。至于[1 0 0], [0 1 1], [0 0 1],它会给[1 0 1], [0 1 0], [0 0 1]
【解决方案3】:

查找 Anna Lubiw 1987 年关于“矩阵的双重词汇排序”的论文。

下面有引用。排序与您要查找的不同,但非常接近。如果不出意外,你应该可以从那里得到一个不错的主意。

http://dl.acm.org/citation.cfm?id=33385

【讨论】:

  • 请同时引用您问题中的信息。如果 ACM 链接发生变化(不时发生,我也是会员),您的答案将丢失所有上下文。
【解决方案4】:

这是一个起点:

将每一行从二进制位转换为数字

按降序排列数字。

然后将每一行转换回二进制。

【讨论】:

  • 是的,这适用于行,但列也需要排序。
【解决方案5】:

基本算法:

  1. 确定行总和并存储 价值观。确定列总和 并存储值。
  2. 按升序对行总和进行排序。对列进行排序 总和按升序排列。

希望您的矩阵尽可能接近右上角的三角形区域。

【讨论】:

  • 这种工作,但不会“完成”对我给出的示例进行排序:行总和是 A:2、B:3、C:1、D:2,列总和是1:2、2:4、3:2、4:0,所以 A、D 行和 1,3 列应该按什么顺序排列是不明确的。
  • 如果我理解问题的其余部分...也许在您完成前两个步骤之后,您可以查看新矩阵并测试具有相同相等总和的行和列查看哪些更密集地在右侧/顶部填充 1(因此较低的行索引和较高的列索引)。
【解决方案6】:

将行视为二进制数,最左边的列作为最高位,并按从上到下的降序排序

将列视为二进制数,将最底部的行作为最高有效位,并按升序从左到右对它们进行排序。

重复直到到达一个固定点。算法终止的证明留给读者练习。

【讨论】:

  • 请注意,这与首先按列排序会给出不同的结果。如果重要的话,这两种变体都只会收敛到局部最优值。
猜你喜欢
  • 2013-07-03
  • 2015-01-31
  • 2015-09-17
  • 2020-08-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-17
相关资源
最近更新 更多