如何将9x9阵列拆分为9个3x3组件

时间:2016-12-07 14:58:21

标签: arrays ruby

我有一个代表数独游戏的9x9多维数组。我需要将它分解为9个3x3的许多组件。怎么做?我完全不知道从哪里开始。

game = [
[1, 3, 2, 5, 7, 9, 4, 6, 8],
[4, 9, 8, 2, 6, 1, 3, 7, 5],
[7, 5, 6, 3, 8, 4, 2, 1, 9],
[6, 4, 3, 1, 5, 8, 7, 9, 2],
[5, 2, 1, 7, 9, 3, 8, 4, 6],
[9, 8, 7, 4, 2, 6, 5, 3, 1],
[2, 1, 4, 9, 3, 5, 6, 8, 7],
[3, 6, 5, 8, 1, 7, 9, 2, 4],
[8, 7, 9, 6, 4, 2, 1, 5, 3]
]

分成块,它变成

chunk_1 = [
[1, 3, 2],
[4, 9, 8],
[7, 5, 6]
]

chunk_2 = [
[5, 7, 9],
[2, 6, 1],
[3, 8, 4]
]

...and so on

3 个答案:

答案 0 :(得分:5)

方法Matrix#minor是为此量身定制的:

require 'matrix'

def sub3x3(game, i, j)
  Matrix[*game].minor(3*i, 3, 3*j, 3).to_a
end

chunk1 = sub3x3(game, 0, 0)
  #=> [[1, 3, 2], [4, 9, 8], [7, 5, 6]] 
chunk2 = sub3x3(game, 0, 1)
  #=> [[5, 7, 9], [2, 6, 1], [3, 8, 4]] 
chunk3 = sub3x3(game, 0, 2)
  #=> [[4, 6, 8], [3, 7, 5], [2, 1, 9]] 
chunk4 = sub3x3(game, 1, 0)
  #=> [[6, 4, 3], [5, 2, 1], [9, 8, 7]]
...
chunk9 = sub3x3(game, 2, 2)
  #=> [[6, 8, 7], [9, 2, 4], [1, 5, 3]] 

Ruby没有"行"的概念。和"列"数组。因此,为方便起见,我将参考3x3"子阵列" game的偏移ij i = 0,1,2j = 0,1,2),作为m = Matrix[*game]的3x3子矩阵,其左上角值为行偏移3*i3*j的列偏移m,转换为数组。

这是相对低效的,因为为每个" chunk"的计算创建了一个新矩阵。考虑到阵列的大小,这不是一个问题,而是提高效率,而不是重新考虑整体设计。创建九个局部变量(而不是一个九个数组的数组)是不可能的。

这里建议在所有打开的单元格填满后检查game(使用上面的方法sub3x3)的有效性。请注意,我使用了Wiki description of the game,其中唯一有效的条目是数字1-9,我假设当玩家在单元格中输入值时,代码会强制执行该要求。

def invalid_vector_index(game)
  game.index { |vector| vector.uniq.size < 9 }
end

def sub3x3_invalid?(game, i, j)
  sub3x3(game, i, j).flatten.uniq.size < 9
end

def valid?(game)
  i = invalid_vector_index(game)
  return [:ROW_ERR, i] if i
  j = invalid_vector_index(game.transpose)
  return [:COL_ERR, j] if j
  m = Matrix[*game]
  (0..2).each do |i|
    (0..2).each do |j|
      return [:SUB_ERR, i, j] if sub3x3_invalid?(game, i, j)
    end
  end
  true
end

valid?(game)
  #=> true

请注意,这会返回true,意味着game有效,或者数组既表示解决方案无效,又包含可用于通知玩家原因的信息。< / p>

现在尝试

game[5], game[6] = game[6], game[5]

所以

game
  #=> [[1, 3, 2,   5, 7, 9,   4, 6, 8],
  #    [4, 9, 8,   2, 6, 1,   3, 7, 5],
  #    [7, 5, 6,   3, 8, 4,   2, 1, 9],

  #    [6, 4, 3,   1, 5, 8,   7, 9, 2],
  #    [5, 2, 1,   7, 9, 3,   8, 4, 6],
  #    [2, 1, 4,   9, 3, 5,   6, 8, 7],

  #    [9, 8, 7,   4, 2, 6,   5, 3, 1],
  #    [3, 6, 5,   8, 1, 7,   9, 2, 4],
  #    [8, 7, 9,   6, 4, 2,   1, 5, 3]] 

valid?(game)
  #=> [:SUB_ERR, 1, 0]

行和列显然仍然有效,但此返回值表示至少有一个3x3子数组无效且数组

[[6, 4, 3],
 [5, 2, 1],
 [2, 1, 4]]

是第一个发现无效的。

答案 1 :(得分:4)

这是一项有趣的练习!

答案

game.each_slice(3).map{|stripe| stripe.transpose.each_slice(3).map{|chunk| chunk.transpose}}.flatten(1)

这很麻烦,不需要定义每个chunk_1, chunk_2, ...

如果您需要chunk_2,可以使用extract_chunks(game)[1]

它输出[chunk_1, chunk_2, chunk_3, ..., chunk_9],所以它是一个数组数组:

1 3 2
4 9 8
7 5 6

5 7 9
2 6 1
3 8 4

4 6 8
3 7 5
2 1 9

6 4 3
5 2 1
...

您可以定义一种方法来检查此网格是否有效(它是):

def extract_chunks(game)
  game.each_slice(3).map{|stripe| stripe.transpose.each_slice(3).map{|chunk| chunk.transpose}}.flatten(1)
end

class Array # NOTE: Use refinements if you don't want to patch Array
  def has_nine_unique_elements?
    self.flatten(1).uniq.size == 9
  end
end

def valid?(game)
  game.has_nine_unique_elements? &&
  game.all?{|row| row.has_nine_unique_elements? } &&
  game.all?{|column| column.has_nine_unique_elements? } &&
  extract_chunks(game).all?{|chunk| chunk.has_nine_unique_elements? }
end

puts valid?(game) #=> true

理论

  • 大网格可以是sliced 3条纹,每条包含3行9个单元格。
  • 第一个条带将包含chunk_1,chunk_2和chunk_3。
  • 我们需要将条带垂直切成3个块。为此:
  • 我们为条纹#2和#3执行same
  • 为了避免返回一行单元格行块数组,我们使用flatten(1)删除一个级别并返回一行单元格行数组。 :)

答案 2 :(得分:0)

您可以创建一个从给定索引生成单个3X3块的方法。由于数独板的长度为9,因此将为您生成9个3X3块。见下文。

#steps
#you'll loop through each index of the board
#to get the x value
#you divide the index by 3 and multiply by 3
#to get the y value
#you divide the index by 3, take remainder and multiply by 3
#for each x value, you can get 3 y values
#this will give you a single 3X3 box from one index so

def three_by3(index, sudoku)
  #to get x value
  x=(index/3)*3
  #to get y value
  y=(index%3)*3
  (x...x+3).each_with_object([]) do |x,arr|
    (y...y+3).each do |y|
      arr<<sudoku[x][y]
    end
  end
end
sudoku = [ [1,2,3,4,5,6,7,8,9],
          [2,3,4,5,6,7,8,9,1],
          [3,4,5,6,7,8,9,1,2],
          [1,2,3,4,5,6,7,8,9],
          [2,3,4,5,6,7,8,9,1],
          [3,4,5,6,7,8,9,1,2],
          [1,2,3,4,5,6,7,8,9],
          [2,3,4,5,6,7,8,9,1],
          [3,4,5,6,7,8,9,1,2]]

p (0...sudoku.length).map {|i| three_by3(i,sudoku)}
#output:
#[[1, 2, 3, 2, 3, 4, 3, 4, 5], 
# [4, 5, 6, 5, 6, 7, 6, 7, 8], 
# [7, 8, 9, 8, 9, 1, 9, 1, 2], 
# [1, 2, 3, 2, 3, 4, 3, 4, 5], 
# [4, 5, 6, 5, 6, 7, 6, 7, 8], 
# [7, 8, 9, 8, 9, 1, 9, 1, 2], 
# [1, 2, 3, 2, 3, 4, 3, 4, 5], 
# [4, 5, 6, 5, 6, 7, 6, 7, 8], 
# [7, 8, 9, 8, 9, 1, 9, 1, 2]]