Boggle作弊......呃......用R中的图表解决

时间:2015-02-19 15:07:23

标签: r graph permutation igraph adjacency-matrix

我已经看过其他一些与此游戏相关的帖子,但其中没有一个以我选择的算法类型为中心,至少在很多细节方面还没有。这也是我学习更多关于图形的借口(例如使用igraph包)。不用说,我不鼓励人们在任何情况下作弊。这实际上是我为自己设定的一个学习挑战 - 它经常通过我最终学到的东西。

除了French dictionary的明显集合之外,我的计划涉及一些准备工作。

第一个重要的步骤是构建一个看起来像这样的igraph,说明了Boggle字母之间允许的连接。 (对于那些不熟悉Boggle的人,你只能用直接相邻的字母创建单词,包括对角线。单词越长,奖励就越大。)

igraph built from graph.lattice, adding diagonals manually

下一步(可能不太理想,但无法找出如何直接从igraph包中实现这一目标)。无论如何,它是使用gtools

生成所有排列

permutations(n=16, r=3) permutations(n=16, r=4)

然后使用igraph::neigbourhood功能来"验证"每一个排列,看看他们是否在Boggle游戏中是合法的。我们从下面的数字中看到,"样本" (如果您愿意,单词越久),排列的排列就越多。因此,获得非常少的附加信息需要很多处理能力。显然不是最佳的。当r超过7时,所有的地狱都会崩溃(我的8 Gb Ram还不够!)

4 letter permutations - total : 43680 
                        legit : 1764 (4.0%)
6 letter permutations - total : 5765760 
                        legit : 22672 (0.4%) 
and so forth

所以现在我想找到一种方法以更加敏感的方式产生这些排列(也许它们可以被称为"路径"或者#34;轨迹"),也许使用igraph这样的工具,这样我就不会因为太多的乐趣而炒我的主板。使用图表对我来说是新的,所以它可能站在我的脸上,但我看不到任何诸如"生成通过图表上的N个相邻节点的所有轨迹"或者文档中的类似内容。也许它存在,但它被称为"有些人的算法",我以前从未听说过的人。

一旦完成准备工作,我对结果非常满意。它相当快速且完全准确。我只是坚持用7个字母的单词(5个悲惨的点赫赫赫)。如果ppl感兴趣,我可能会在某些时候把它放在GitHub上。我认为那些了解图表的人应该能够指出我正确的方向,这就是为什么我不认为任何编码的长度都可以用于任何目的。

提前致谢!

(为了完整起见,一旦"有效排列"被计算出来,我会对字典条目运行生成的单词,并将匹配的单词放在一边。我使用RSQLite并使用增加长度的单词块;以这种方式保持分离使得代码非常容易遵循,并且使db搜索速度非常快。)

1 个答案:

答案 0 :(得分:2)

这是一个递归解决方案,可找到长度为L的所有路径。

使用此Gist创建的图表:

getPaths <- function(v, g, L = 4) {
  paths <- list()
  recurse <- function(g, v, path = NULL) {
    path <- c(v, path)

    if (length(path) >= L) {
      return(NULL)
    } else {    
      for (i in neighbors(g, v)) {
        if (!(i %in% path)) {
          paths[[length(paths) + 1]] <<- c(i, path)
          recurse(g, i, path)
        }
      }
    }
  }
  recurse(g, v)
  return(paths)
}

allPaths <- lapply(V(g), getPaths, g)

# look at the first few paths from vertex 1:
> head(allPaths[[1]])
[[1]]
[1] 2 1

[[2]]
[1] 3 2 1

[[3]]
[1] 4 3 2 1

[[4]]
[1] 6 3 2 1

[[5]]
[1] 7 3 2 1

[[6]]
[1] 8 3 2 1

修改

这是一个更有效的解决方案,只保留L长度路径。

getPaths <- function(v, g, L = 4) {
  paths <- list()

  recurse <- function(g, v, path = NULL) {
    path <- c(v, path)

    if (length(path) >= L) {
      paths[[length(paths) + 1]] <<- rev(path)      
    } else {    
      for (i in neighbors(g, v)) {
        if (!(i %in% path)) recurse(g, i, path)
      }
    }
  }
  recurse(g, v)
  return(paths)
}

allPaths <- lapply(V(g), getPaths, g, 4)

L4way <- do.call(rbind, lapply(allPaths, function(x) do.call(rbind, x)))

> head(L4way)
     [,1] [,2] [,3] [,4]
[1,]    1    2    3    4
[2,]    1    2    3    6
[3,]    1    2    3    7
[4,]    1    2    3    8
[5,]    1    2    5    6
[6,]    1    2    5    9

编辑#2:

library(doSNOW)
library(foreach)

# this is a very parallel problem and can be parallel-ized easily
cl <- makeCluster(4)
registerDoSNOW(cl)

allPaths <- foreach(i = 3:16) %:%
  foreach(v = V(g), .packages = c('igraph')) %dopar% getPaths(v, g, i)

stopCluster(cl)

path.list <- list()
for (i in seq_along(3:16)) {
  path.list[[i]] <- do.call(rbind, lapply(allPaths[[i]],
      function(x) do.call(rbind, x)))
}

L长度词的排列数:

> data.frame(length=3:16, nPerms=sapply(path.list, nrow))
   length  nPerms
1       3     408
2       4    1764
3       5    6712
4       6   22672
5       7   68272
6       8  183472
7       9  436984
8      10  905776
9      11 1594648
10     12 2310264
11     13 2644520
12     14 2250192
13     15 1260672
14     16  343184

总排列

> sum(sapply(path.list, nrow))
[1] 12029540