简体   繁体   English

从Ruby中的大型矩阵获取2x2矩阵的列表

[英]Obtaining a list of 2x2 matrices from a large matrix in Ruby

I have been working on a coding challenge for some time just for fun, the problem is this: 我一直在努力进行编码挑战,只是为了好玩,问题是这样的:
Given a rectangular matrix containing only digits, calculate the number of different 2 × 2 squares in it. 给定一个仅包含数字的矩形矩阵,请计算其中不同的2×2正方形的数量。

The 2x2 matrices may be overlapping. 2x2矩阵可能重叠。 Inputs can be up to 100 x 100 matrices and will be rectangular but not necessarily square. 输入最多可以是100 x 100矩阵,并且可以是矩形,但不一定是正方形。 I was able to solve this problem using nested loops, the problem is that it is too slow for inputs that are large matrices and it exceeds the time limit (4000ms) of the coding challenge. 我能够使用嵌套循环解决此问题,问题是对于大型矩阵的输入而言,它太慢了,并且超过了编码挑战的时间限制(4000毫秒)。 This is how I initially solved it. 这就是我最初解决它的方式。

def differentSquares(matrix)
    i = 0
    squares = []
    while i < matrix.length - 1 
        j = 0
        while j < matrix[i].length - 1 
            current = [matrix[i][j], matrix[i][j+1], matrix[i+1][j], matrix[i+1][j+1]]
            squares << current if !squares.include?(current)
            j += 1 
        end 
        i += 1 
    end 
    squares.length 
end

I have considered somehow using a hash because they are much faster to iterate through than an array, but I can't figure out how to do that. 我考虑过以某种方式使用哈希,因为它们的遍历比数组快得多,但是我不知道该怎么做。 Can anyone help me find an implementation that is faster than a nested loop? 谁能帮我找到比嵌套循环更快的实现?

Examples of input and expected output: 输入和预期输出的示例:

input:   
[[2,5,3,4,3,1,3,2],   
 [4,5,4,1,2,4,1,3],   
 [1,1,2,1,4,1,1,5],   
 [1,3,4,2,3,4,2,4],   
 [1,5,5,2,1,3,1,1],   
 [1,2,3,3,5,1,2,4],   
 [3,1,4,4,4,1,5,5],   
 [5,1,3,3,1,5,3,5],   
 [5,4,4,3,5,4,4,4]] 

expected output: 54  

another input:   
[[1,2,1],   
 [2,2,2],   
 [2,2,2],   
 [1,2,3],   
 [2,2,1]]

expected output: 6   

Here is a collection of the proposed solutions including a benchmark: 这是建议的解决方案的集合,包括基准:

require 'benchmark/ips'
require 'set'
require 'matrix'

def generate(x, y, max)
  matrix = []
  x.times do
    row = []
    y.times do
      row << rand(max)
    end
    matrix << row
  end
  matrix
end

def original(matrix)
    i = 0
    squares = []
    while i < matrix.length - 1
        j = 0
        while j < matrix[i].length - 1
            current = [matrix[i][j], matrix[i][j+1], matrix[i+1][j], matrix[i+1][j+1]]
            squares << current if !squares.include?(current)
            j += 1
        end
        i += 1
    end
    squares.length
end

def with_set(matrix)
  i = 0
  squares = Set.new
  while i < matrix.length - 1
    j = 0
    while j < matrix[i].length - 1
      squares << [matrix[i][j], matrix[i][j+1], matrix[i+1][j], matrix[i+1][j+1]]
      j += 1
    end
    i += 1
  end
  squares.length
end

def with_set_and_length(matrix)
  i = 0
  squares = Set.new
  a = matrix.length - 1
  b = matrix.first.length - 1
  while i < a
    j = 0
    while j < b
      squares << [matrix[i][j], matrix[i][j+1], matrix[i+1][j], matrix[i+1][j+1]]
      j += 1
    end
    i += 1
  end
  squares.length
end

def foo(matrix)
  matrix.each_cons(2) do |row|
    row.each_cons(2) do |col|

    end
  end
end

def with_each_cons(m)
  m.each_cons(2).flat_map { |a, b| a.each_cons(2).zip(b.each_cons(2)) }.uniq.count
end

def with_each_cons_rearanged(m)
  m.map { |a| a.each_cons(2).to_a }.each_cons(2).flat_map { |a, b| a.zip(b) }.uniq.count
end

def with_matrix(m)
  (m.row_count-1).times.flat_map do |i|
    (m.column_count-1).times.map { |j| m.minor(i,2,j,2) }
  end.uniq.size
end


def with_matrix_and_set(m)
  set = Set.new
  (m.row_count-1).times do |i|
    (m.column_count-1).times do |j|
      set << m.minor(i, 2, j, 2)
    end
  end
  set.size
end

array_matrix = generate(100, 100, 20)
real_matrix = m = Matrix[*array_matrix]

Benchmark.ips do |x|
  x.compare!
  x.report('original') do
    original(array_matrix)
  end
  x.report('with_set') do
    with_set(array_matrix)
  end
  x.report('with_set_and_length') do
    with_set_and_length(array_matrix)
  end
  x.report('with_each_cons') do
    with_each_cons(array_matrix)
  end
  x.report('with_each_cons_rearanged') do
    with_each_cons_rearanged(array_matrix)
  end
  x.report('with_matrix') do
    with_matrix(real_matrix)
  end
  x.report('with_matrix_and_set') do
    with_matrix_and_set(real_matrix)
  end
end

Results: 结果:

Comparison:
 with_set_and_length:       52.7 i/s
            with_set:       52.0 i/s - 1.01x slower
with_each_cons_rearanged:       31.4 i/s - 1.68x slower
 with_matrix_and_set:       21.0 i/s - 2.52x slower
      with_each_cons:       18.7 i/s - 2.82x slower
         with_matrix:       17.5 i/s - 3.01x slower
            original:        0.1 i/s - 605.89x slower

This is both, short and fast: 这既简短又快速:

def different_squares(m)
  m.each_cons(2).flat_map { |a, b| a.each_cons(2).zip(b.each_cons(2)) }.uniq.count
end

It takes advantage of each_cons and zip . 它利用了each_conszip优势。

Rearranging the methods makes it faster: 重新排列方法可以使其更快:

def different_squares(m)
  m.map { |a| a.each_cons(2).to_a }.each_cons(2).flat_map { |a, b| a.zip(b) }.uniq.count
end

Solved with Pascal Betz suggestion of using a Set: 解决了Pascal Betz使用Set的建议:

require 'set'

def differentSquares(matrix)
    i = 0
    squares = Set.new 
    while i < matrix.length - 1 
        j = 0
        while j < matrix[i].length - 1 
            current = [matrix[i][j], matrix[i][j+1], matrix[i+1][j], matrix[i+1][j+1]]
            squares.add(current)
            j += 1 
        end 
        i += 1 
    end 
    squares.length 
end

Although as Pascal mentioned, the algorithm is still not very good and I would love some suggestions of a better algorithm. 尽管正如Pascal所提到的,该算法仍然不是很好,我希望有一些更好算法的建议。

require 'matrix'

def nbr_uniq_2x2_submatrices(arr)
  m = Matrix[*arr]
  (m.row_count-1).times.flat_map do |i|
    (m.column_count-1).times.map { |j| m.minor(i,2,j,2) }
  end.uniq.size
end

arr = [[2,5,3,4,3,1,3,2],
       [4,5,4,1,2,4,1,3],
       [1,1,2,1,4,1,1,5],
       [1,3,4,2,3,4,2,4],
       [1,5,5,2,1,3,1,1],
       [1,2,3,3,5,1,2,4],
       [3,1,4,4,4,1,5,5],
       [5,1,3,3,1,5,3,5],
       [5,4,4,3,5,4,4,4]]

nbr_uniq_2x2_submatrices(arr)
  #=> 54

Class and instance methods for the class Matrix are documented here . Matrix类的类和实例方法在此处介绍

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM