【问题标题】:Boggle cheat... erm... solutioning with graphs in RBoggle 作弊……呃……用 R 中的图表解决问题
【发布时间】:2015-02-19 15:07:23
【问题描述】:

我看过其他一些与这款游戏相关的帖子,但没有一篇以我选择的算法类型为中心,至少目前还没有太多细节。这也是我更多地了解图表的借口(例如使用igraph 包)。不用说,我不鼓励人们在任何情况下作弊。这确实是我为自己设定的一个学习挑战——通常是通过我最终学到最多的东西。

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

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

下一步(可能并不理想,但无法直接从 igraph 包中弄清楚如何实现这一点)。无论如何,它是使用gtools 生成所有排列:

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

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

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

所以现在我想找到一种方法以更合理的方式生成这些排列(也许它们可以称为“路径”或“轨迹”),也许使用 igraph 之类的工具,所以我不不要因为玩得太开心而炸我的主板。使用图表对我来说是新事物,因此它可能就在我面前,但我看不到任何东西,例如“生成通过图表上 N 个相邻节点的所有轨迹”或文档中的类似内容。也许它存在,但它被称为“某人的算法”,很遗憾我以前从未听说过。

完成所有准备工作后,我对结果感到非常满意。它相当快且完全准确。我只是坚持使用 7 个字母的单词(5 个悲惨点,呵呵)。如果大家有兴趣,我可能会在某个时候把它放在 GitHub 上。我认为对图表足够了解的人应该能够为我指明正确的方向,这就是为什么我认为将任何长度编码在这里没有任何用途。

提前致谢!

(为了完整起见,一旦计算出“有效排列”,我就会根据字典条目运行生成的单词并将匹配的单词放在一边。我正在使用 RSQLite 并处理长度增加的单词块;以这种方式将事物分开使代码很容易理解,也使数据库搜索非常快。)

【问题讨论】:

  • 我认为你可以用递归解决这个问题:cs.bu.edu/teaching/alg/maze
  • 你能发布你用来创建你的 igraph 的代码吗?我认为一个简单的解决方案是使用igraph::neighbors 递归地将数字附加到向量,直到达到一定长度。然后结果向量的每个子集将是较小的r 的路径。
  • Welp,对于任何想要尝试解决方案的人,这里是创建 boggle 网络的代码:gist.github.com/Zelazny7/fd396b5cb0fed713048c
  • 为了创建igraph,我以graph.lattice为基础,然后用get.adjacency提取邻接矩阵,并手动将对角线添加到后者,并使用graph.adjacency构建最终igraph 来自修改后的邻接矩阵。我确信有一些更好的方法可以做到这一点,但它仍然有效!

标签: r graph permutation igraph adjacency-matrix


【解决方案1】:

这是一个递归解决方案,可以找到长度为 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-length 单词的排列数:

> 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

【讨论】:

  • 像魅力一样工作!谢谢!!
  • 有兴趣在 github 上查看您的项目的人中也包括我!
  • hmmm 函数似乎没有检测到一些排列;正在处理它,一两天后查看 github 页面以查看更新。
  • 我的答案的子集部分似乎放弃了一些排列......我不会使用解决方案的那部分。我更新了我的帖子,详细统计了每条 L 长度的路径。
  • 12,029,540 这个数字也在网上流传。 Quora 上的某个人还计算了所有组合,包括 1-2 个字母路径,我也可以匹配该数字。我认为解决方案有效,只需忽略子集快捷方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 2015-01-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多