使用矩阵创建随机毯子图案

时间:2018-08-07 09:45:07

标签: r matrix combinatorics

我的伴侣要求我按照以下规则帮助她设计毯子的图案:

  1. 这是一个由169个小正方形(13 x 13)组成的正方形毯子
  2. 有13种颜色可供使用(a-m)
  3. 每格需要
  4. 3种不同颜色(颜色的顺序无关紧要;例如“ a,b,c” ==“ b,c,a”)
  5. 对于每个小方块,有2种颜色不能全部组合在一起;设置1(a,b,c,d)和设置2(g,h,j,m)。例如。一个小正方形不能是a,b,c,但是可以是a,b,e
  6. 设计毛毯时,每个正方形都不能与包含任何相同颜色的正方形正交(对角线很好)
  7. 编辑:理想情况下,每个小方块只能使用一次。

我已经成功地完成了上面的步骤1-4(请参见下面的代码),但是我完全对如何以编程方式执行步骤5感到困惑。我知道我可以通过蛮力或反复试验(或者只是手工完成) ),但我希望有一种更聪明的方法来解决此问题。

任何人都可以提供帮助吗?

如果有可能解决,那么接下来有几个问题:

  1. 如何扩展n种颜色和[i,j]矩阵的解决方案?
  2. 如何获得给定参数的随机解(因为我假设对于所提供的颜色和矩阵大小没有单一的解)?

到目前为止,这是我的工作:

# Generate the entire list of unique colour combinations (regardless of order).
col_list_combination <- as.data.frame(t(combn(letters[1:13], 3)))

# There are 2 sets where no combination is allowed:
excl_sets <- t(combn(letters[1:4], 3)) %>%
  rbind(t(combn(letters[c(7, 8, 10, 13)], 3))) %>%
  as.data.frame()

# Setting the column names for my own sanity.
colnames(col_list_combination) <- colnames(excl_sets) <- c("Primary", "Secondary", "Tertiary")

# Removing those combinations that are not allowed
col_list_combination %<>% 
  anti_join(excl_sets, by = c("Primary", "Secondary", "Tertiary"))

# An attempt to figure out which combinations are allowed for a single square.
abe_neighbours <- col_list_combination %>%
  filter(Primary != "a" & Secondary != "a" & Tertiary != "a") %>%
  filter(Primary != "b" & Secondary != "b" & Tertiary != "b") %>%
  filter(Primary != "e" & Secondary != "e" & Tertiary != "e")

编辑:所需的输出将是13 x 13矩阵,其中矩阵的每个单元格是一组三种颜色的三元代码,其位置受上述规则限制。例如,类似:

tibble(c("abe", "ghm"), c("cki", "adf"))

1 个答案:

答案 0 :(得分:1)

经过几天的思考和工作,我相信我已经提出了一个解决方案:

# Generate the entire list of unique colour combinations (regardless of order). (Rule 3)
col_list_combination <- as.data.frame(t(combn(letters[1:13], 3)))

# There are 2 sets where no combination is allowed (Rule 4):
excl_sets <- t(combn(letters[1:4], 3)) %>%
  rbind(t(combn(letters[c(7, 8, 10, 13)], 3))) %>%
  as.data.frame()

# Setting the column names for my own sanity.
colnames(col_list_combination) <- colnames(excl_sets) <- c("Primary", "Secondary", "Tertiary")

# Removing those combinations that are not allowed (Rule 4)
col_list_combination %<>% 
  anti_join(excl_sets, by = c("Primary", "Secondary", "Tertiary")) %>%
  mutate(con_cat = paste(Primary, Secondary, Tertiary)) %>%
  select(con_cat)

到目前为止,我只是创建了可能的颜色三元组列表。现在,我将尝试创建毯子。

# First create the blank matrix of the size you wish (Rule 1 for this example).
output_matrix <- matrix(NA,13,13)

# Next set a random starting seed in the top left-most cell (this allows for random variations on output).
output_matrix[1,1] <- sample(col_list_combination %>% pull(con_cat), 1)

#create an empty vector for the "used" squares so that they do not get re-used (Rule 6).
used_combinations <- c()

#iterate over all columns and rows.
for(i in 1:nrow(output_matrix)) {
  for(j in 1:ncol(output_matrix)) {

    #for each cell, figure out the colours of the cell above and to the left (since we are working left to right and top to bottom)
    split_cells <- strsplit(c(output_matrix[i, ifelse(j == 1, j, j-1)], 
                              output_matrix[ifelse(i == 1, i, i-1), j]), " ") %>% 
      unlist()
    split_cells <- split_cells[!is.na(split_cells)] #need to remove NAs since some cells do not have a cell above or to the left.

    #the number of non-allowed neighbour colours will either be 3 or 6 (I am ignoring duplicates for simplicity)
    if(length(split_cells) == 3) {
      cell_neighbours <- col_list_combination %>%
        filter(!con_cat %in% used_combinations) %>% #removes any that have been used already
        filter(!grepl(split_cells[1], con_cat) & 
                 !grepl(split_cells[2], con_cat) & 
                 !grepl(split_cells[3], con_cat)) %>%
        pull()
    } else if(length(split_cells) == 6) {
      cell_neighbours <- col_list_combination %>%
        filter(!con_cat %in% used_combinations) %>% removes any that have been used already.
        filter(!grepl(split_cells[1], con_cat) & 
                 !grepl(split_cells[2], con_cat) & 
                 !grepl(split_cells[3], con_cat) &
                 !grepl(split_cells[4], con_cat) &
                 !grepl(split_cells[5], con_cat) &
                 !grepl(split_cells[6], con_cat)) %>%
        pull()
    }
    cell_neighbours

    #select the new combination.
    new_combination <- sample(cell_neighbours, 1)

    #add the new combination to the appropriate cell.
    output_matrix[i,j] <- new_combination

    #update the used comnbination vector.
    used_combinations <- c(used_combinations, new_combination)
  }
}
output_matrix

我相信这可以按预期工作,并且可以扩展到任何矩阵大小,色号,并允许使用多个可变输出矩阵。

我很确定代码可以更高效地编写...