【问题标题】:Replace every cell in a matrix with the average of adjcent cells用相邻单元格的平均值替换矩阵中的每个单元格
【发布时间】:2017-07-05 05:14:13
【问题描述】:

要求:必须就地完成。

例如:

给定矩阵

1, 2, 3
4, 5, 6
7, 8, 9

应该用它的 3*3 个相邻单元和它自己的总和的平均值替换:

(1+2+4+5)/4,       (2+1+3+4+5+6)/6 ,            (3+2+6+5)/4
(1+2+5+4+7+8)/6,   (1+2+3+4+5+6+7+8+9)/9,       (2+3+5+6+8+9)/6
(4+5+7+8)/4,       (4+5+6+7+8+9)/6,             (5+6+8+9)/4

这是:

All floating number convert to int

3,      3.5(3), 4             3, 3, 4
4.5(4),  5, 5.5(5)  =>        4, 5, 5
6,      6.5(6), 7             6, 6, 7

我尝试只迭代矩阵并更新每个单元格,但我发现这会影响未来的计算:

假设我将原来的 1 更新为 3,但是当我尝试更新原来的 2 时,原来的 1 现在变成了 3。

复制原始矩阵来计算平均值是一种解决方法,但这是一个坏主意,我们能否在不使用那么多空间的情况下实现这一目标?

【问题讨论】:

  • 在更新单元格之前创建矩阵的副本是通常的方法。如果矩阵非常非常大,您可以只保留“滚动”备份,例如上一行和当前行,甚至更少。
  • 假设您有一个 1000x1000 矩阵,并且您正在逐行更新单元格。在第 n 行时,您不需要第 n-2 行的副本(因为它不再相关),也不需要第 n+1 行(因为它仍然具有原始值。所以只保留前一个的备份和当前行就足够了。是否值得努力是另一个问题。
  • @tobias_k 你能详细解释一下“滚动”备份的细节吗?我认为这可能是关键。

标签: algorithm matrix


【解决方案1】:

在大多数情况下,您应该只创建原始矩阵的副本并使用它来计算平均值。除非创建矩阵的副本会使用比可用内存更多的内存,否则开销应该可以忽略不计。

如果您有一个 非常 大的矩阵,您可以使用“滚动”备份(没有更好的术语)。假设您逐行更新单元格,并且您当前位于第 n 行中。您不需要备份行 n-2,因为这些单元格不再相关,行 n+1 也不相关,因为它们仍然是原始的价值观。所以你可以只保留前一行和当前行的备份。每当您前进到下一行时,丢弃前一行的备份,将当前行的备份移至上一行,并创建新的当前行的备份。

一些伪代码(不考虑任何边缘情况):

previous = []  # or whatever works for the first row
for i in len(matrix):
    current = copy(matrix[i])
    for k in len(matrix[i]):
         matrix[i][k] = previous[k-1] + ... + current[k] + ... matrix[i+1][k+1] / 9
    previous = current

(您还可以保留 next 行的备份,这样您就可以只为所有值使用备份行,而不必区分。)

【讨论】:

    【解决方案2】:

    您必须对结果数据进行某种缓存,这样您才能保持对原始数据的引用。我认为没有办法解决它。

    如果数据集很大,您可以通过使用较小的数据缓冲区(例如通过钥匙孔查看)并在更新时“滚动”输入矩阵来进行优化。在您的情况下,您可以使用小至 3x3 的缓冲区。 这是速度和空间之间的妥协。缓冲区越小,性能就越差。

    为了可视化问题,从数据集的左上角 (0,0) 开始:
    (为简单起见,结果值向下舍入)

    第一步:更新前 4 个单元格(填充缓冲区)

    // Data Set        // Data Viewport // Result Set
    01,02,03,04,05     01,02,03         04,04,??
    06,07,08,09,10     06,07,08         06,07,??
    11,12,13,14,15     11,12,13         ??,??,??
    16,17,18,19,20
    21,22,23,24,25
    

    那么对于每次迭代..
    (用 [xx] 表示的新值)

    ++ 从结果集中更新数据集中的第一列

    // Data Set        // Data Viewport   // Result Set
    [04],02,03,04,05   01,02,03         04,04,??
    [06],07,08,09,10   06,07,08         06,07,??
     11 ,12,13,14,15   11,12,13         ??,??,??
     16 ,17,18,19,20
     21 ,22,23,24,25
    

    ++ 将数据视口和结果集右移 1 列

    // Data Set        // Data Viewport // Result Set
    [04],02,03,04,05   02,03,04         04,[03],??
    [06],07,08,09,10   07,08,09         07,[08],??
     11 ,12,13,14,15   12,13,14         ??, ?? ,??
     16 ,17,18,19,20
     21 ,22,23,24,25
    

    ++ 更新结果集的中间列

    // Data Set        // Data Viewport // Result Set
    [04],02,03,04,05   02,03,04         04,[05],??
    [06],07,08,09,10   07,08,09         07,[08],??
     11 ,12,13,14,15   12,13,14         ??, ?? ,??
     16 ,17,18,19,20
     21 ,22,23,24,25
    

    在接下来的迭代中,数据状态将是:

    // Data Set        // Data Viewport // Result Set
    04,[04],03,04,05   03,04,05         05,[06],??
    06,[07],08,09,10   08,09,10         08,[09],??
    11, 12 ,13,14,15   13,14,15         ??, ?? ,??
    16, 17 ,18,19,20
    21, 22 ,23,24,25
    

    ..等等

    不要忘记处理其他极端情况。

    *数据视口表示仅用于可视化。在代码中,实际的视口将是结果缓冲区。

    【讨论】:

    • 您能进一步解释一下吗?我真的不明白 3x3 单元的缓存如何就足够了......
    • 我在第二步中犯了一个错误。数据视口中的值应与数据集匹配。更新。在示例中,我只更新(主)迭代结果集中间列中的 2 个单元格。您可以将缓冲区设置为任意大小,只要它至少为 3x3。这是一种导航无限数据空间的技术(您不能缓存任何行或列,因为它们是无限的。这样做,您仅受索引大小的限制。在渲染中,这称为剪辑空间算法,如我知道。这有帮助吗?
    • 我不确定我是否正确 - 我们使用 3*3 缓冲区,并且更新所有元素不会涉及其他元素的平均值。我认为这是理想的方式,但很难在代码中实现。
    • 是的,没错。如果您在我的示例中使用 3(rows) x 5(columns) 缓冲区,不包括边缘情况,您将能够更新整个第一行和第二行缓冲区。
    • 我完全相信你,但我还是不明白。也许您可以添加一些伪代码或算法链接,以更好地解释单元格更新的顺序。
    猜你喜欢
    • 2019-01-10
    • 2015-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多