在矩阵中查找全零的有效方法?

时间:2011-08-24 23:23:47

标签: algorithm matrix

我正在考虑有效的算法来找到一行矩阵中的零个数,但只能想到O(n 2 )解(即迭代每一行和每列)。是否有更有效的方法来计算零?

例如,给定矩阵

3,  4,  5,  6
7,  8,  0,  9
10, 11, 12, 3
4,  0,  9,  10 

我会报告有两个零。

6 个答案:

答案 0 :(得分:13)

没有存储任何外部信息,不,你不能做任何比Θ更好的事情(N 2 )。基本原理很简单 - 如果你不查看矩阵中的所有N 2 位置,那么你不能保证你找到了所有的零并且可能最终给出了错误的答案背部。例如,如果我知道您查看的位置少于N 2 ,那么我可以在矩阵上运行您的算法并查看您报告的零数。然后,我可以查看您未访问的位置,用零替换它们,然后再次运行算法。由于你的算法没有查看那些位置,因此无法知道它们中有零,因此算法的两次运行中至少有一次会给出错误的答案。

更一般地说,在设计处理数据的算法时,查看是否可以比某些运行时更好的方法是使用这种“对抗性分析”。问自己一个问题:如果我跑得比O(f(n))更快,对手是否可以通过改变答案的方式操纵数据,但我无法检测到?这种分析与一些更聪明的数学一起证明,基于比较的排序算法在平均情况下不能比Ω(n log n)做得更好。

如果矩阵具有一些其他属性(例如,如果它已经排序),那么你可能比在O(N 2 )中运行做得更好。例如,假设您知道矩阵的所有行都已排序。然后,您可以轻松地对每一行进行二进制搜索,以确定它包含多少个零,这需要O(N log N)时间并且更快。

根据您的设置参数,如果您假定允许并行扫描,则可以使算法运行得更快。例如,如果您的机器上有K个处理器,专用于扫描矩阵的任务,那么您可以将矩阵拆分为K个大致均匀大小的组,让每个处理器计算组中的零个数,然后将这些计算的结果相加。这最终会给你一个Θ(N 2 / K)的运行时间,因为运行时分为多个核心。

答案 1 :(得分:2)

始终为O(n ^ 2) - 或者更确切地说是O(n x m)。你不能跳过它。

但是如果您知道矩阵是sparse(只有少数元素具有非零值),则只能存储非零和矩阵大小的值。然后考虑使用散列来存储整个矩阵 - 通常创建散列,将行号映射到嵌套散列。

示例:

m = 
[ 
  0 0 0 0
  0 2 0 0
  0 0 1 0
  0 0 1 0
]

将表示为:

row_numbers = 4
column_numbers = 4
hash = { 1 => { 1 => 2}, 2 => {2 => 1, 3 => 2}}

然后:

number_of_zeros = row_numbers * column_numbers - number_of_cells_in_hash(hash)

答案 2 :(得分:1)

对于任何未排序的矩阵,它应该是O(n)。因为通常我们用'n'表示总元素。

如果Matrix包含X行和Y列,则X由Y = n。

组成

E.g在4 X 4 un sorted矩阵中,它总数为16.所以当我们用2个循环线性迭代时,4 X 4 = 16次。它将是O(n),因为数组中的总元素是16。

许多人投票赞成O(n ^ 2),因为他们认为n X n是矩阵。

如果我的理解错误,请纠正我。

答案 3 :(得分:0)

假设当你说“在一个矩阵的行中”时,你的意思是你有行索引i,并且你想要计算i行中的零个数,你可以比O(N ^ 2)做得更好。

假设N是行数,M是列数,那么请存储 矩阵作为单个数组[3,4,5,6,7,8,0,9,10,11,12,34,0,9,10],然后访问行i,您可以在索引N*i访问该数组。

由于数组具有恒定的时间访问权限,因此该部分不依赖于矩阵的大小。然后,您可以通过访问从N*i + jj的{​​{1}}元素0来遍历整行,如果您知道您想要哪一行,则为O(N)访问,你正在使用数组。

答案 4 :(得分:0)

由于我将解释的原因,这不是一个完美的答案,但它提供的替代解决方案可能比您描述的解决方案更快:

  1. 由于您不需要知道矩阵中零点的位置,您可以将其展平为一维数组

  2. 之后,对元素执行快速排序,这可能会提供O(n log n)的性能,具体取决于您输入的矩阵的随机性。

  3. 最后,计算数组开头的零元素,直到达到非零数字。

  4. 在某些情况下,这将比检查每个元素更快,尽管在最坏的情况下,快速排序将采用O(n2),除了最后的零计数可能比迭代每行更糟糕和专栏。

答案 5 :(得分:0)

假设给定矩阵为M执行M+(-M)操作确实使用默认+使用而不是my_add(int a, int b),以便

int my_add(int a, int b){
  return (a == b == 0) ? 1 : (a+b);      
}

这会给你一个像

这样的矩阵
0  0  0  0
0  0  1  0
0  0  0  0
0  1  0  0

现在您创建一个s := 0并继续将所有元素添加到s。 s += a[i][j]

你甚至可以在一个周期内完成这两项任务。 s += my_add(a[i][j], (-1)*a[i][j])

但仍然是O(m*n)

注意

要计算1的数量,您通常会检查矩阵中的所有项目。如果不对所有元素进行操作,我认为你不能分辨出1的数字。并循环所有元素(m*n)。它可以比(m*n)更快,当且仅当你可以保留一些未选中的元素并说明1的数量

修改

但是,如果您在矩阵上移动2x2内核并跳转,则会获得(m*n)/k次迭代,例如如果你操作相邻元素a[i][j], a[i+1][j], a[i][j+1], a[i+1][j+1]直到i < m&amp; i< n