arrays - 6 个位置内 3 个元素的排列

标签 arrays r combinatorics permute

我希望在六个位置内排列(或组合)c("a","b","c"),条件是始终具有具有替代元素的序列,例如abcbab.

排列可以很容易地得到:

abc<-c("a","b","c")
permutations(n=3,r=6,v=abc,repeats.allowed=T)

我认为使用 gtools 不可能做到这一点,并且我一直在尝试为此设计一个函数 - 尽管我认为它可能已经存在。

最佳答案

由于您正在寻找排列,因此 expand.grid 可以与 permutations 一样工作。但由于你不需要相似的邻居,我们可以大大缩短它的维数。我认为这在随机方面是合法的!

前面:

r <- replicate(6, seq_len(length(abc)-1), simplify=FALSE)
r[[1]] <- c(r[[1]], length(abc))
m <- t(apply(do.call(expand.grid, r), 1, cumsum) %% length(abc) + 1)
m[] <- abc[m]
dim(m)
# [1] 96  6
head(as.data.frame(cbind(m, apply(m, 1, paste, collapse = ""))))
#   Var1 Var2 Var3 Var4 Var5 Var6     V7
# 1    b    c    a    b    c    a bcabca
# 2    c    a    b    c    a    b cabcab
# 3    a    b    c    a    b    c abcabc
# 4    b    a    b    c    a    b babcab
# 5    c    b    c    a    b    c cbcabc
# 6    a    c    a    b    c    a acabca
<小时/>

演练:

  • 由于您想要它的所有回收排列,我们可以使用 gtools::permutations,或者我们可以使用 expand.grid ...我将使用后者,我不知道它是否快得多,但它确实是我需要的捷径(稍后会详细介绍)
  • 在处理这样的约束时,我喜欢扩展值向量的索引
  • 但是,由于我们不希望邻居相同,因此我认为我们不应将每行值作为直接索引,而是对它们进行求和;通过使用这个,我们可以控制累积和重新达到相同值的能力...通过从可能的列表中删除 0length(abc)值,我们消除了(a)永远不会保持不变,以及(b)永远不会实际增加一个向量长度(重复相同的值)的可能性;作为演练:

    head(expand.grid(1:3, 1:2, 1:2, 1:2, 1:2, 1:2), n = 6)
    #   Var1 Var2 Var3 Var4 Var5 Var6
    # 1    1    1    1    1    1    1
    # 2    2    1    1    1    1    1
    # 3    3    1    1    1    1    1
    # 4    1    2    1    1    1    1
    # 5    2    2    1    1    1    1
    # 6    3    2    1    1    1    1
    

    由于第一个值可以是所有三个值,因此它是 1:3,但每个附加值都应与其相差 1 或 2。

    head(t(apply(expand.grid(1:3, 1:2, 1:2, 1:2, 1:2, 1:2), 1, cumsum)), n = 6)
    #      Var1 Var2 Var3 Var4 Var5 Var6
    # [1,]    1    2    3    4    5    6
    # [2,]    2    3    4    5    6    7
    # [3,]    3    4    5    6    7    8
    # [4,]    1    3    4    5    6    7
    # [5,]    2    4    5    6    7    8
    # [6,]    3    5    6    7    8    9
    

    好吧,这似乎没什么用(因为它超出了向量的长度),所以我们可以调用模运算符和移位(因为模返回从 0 开始,我们想要从 1 开始):

    head(t(apply(expand.grid(1:3, 1:2, 1:2, 1:2, 1:2, 1:2), 1, cumsum) %% 3 + 1), n = 6)
    #      Var1 Var2 Var3 Var4 Var5 Var6
    # [1,]    2    3    1    2    3    1
    # [2,]    3    1    2    3    1    2
    # [3,]    1    2    3    1    2    3
    # [4,]    2    1    2    3    1    2
    # [5,]    3    2    3    1    2    3
    # [6,]    1    3    1    2    3    1
    
  • 要验证此方法是否有效,我们可以对每一行进行 diff 并查找 0:

    m <- t(apply(expand.grid(1:3, 1:2, 1:2, 1:2, 1:2, 1:2), 1, cumsum) %% 3 + 1)
    any(apply(m, 1, diff) == 0)
    # [1] FALSE
    
  • 为了自动将其转换为任意向量,我们借助replicate来生成可能向量的列表:

    r <- replicate(6, seq_len(length(abc)-1), simplify=FALSE)
    r[[1]] <- c(r[[1]], length(abc))
    str(r)
    # List of 6
    #  $ : int [1:3] 1 2 3
    #  $ : int [1:2] 1 2
    #  $ : int [1:2] 1 2
    #  $ : int [1:2] 1 2
    #  $ : int [1:2] 1 2
    #  $ : int [1:2] 1 2
    

    然后 do.call 展开它。

  • 如果你有索引矩阵,

    head(m)
    #      Var1 Var2 Var3 Var4 Var5 Var6
    # [1,]    2    3    1    2    3    1
    # [2,]    3    1    2    3    1    2
    # [3,]    1    2    3    1    2    3
    # [4,]    2    1    2    3    1    2
    # [5,]    3    2    3    1    2    3
    # [6,]    1    3    1    2    3    1
    

    然后用向量的值替换每个索引:

    m[] <- abc[m]
    head(m)
    #      Var1 Var2 Var3 Var4 Var5 Var6
    # [1,] "b"  "c"  "a"  "b"  "c"  "a" 
    # [2,] "c"  "a"  "b"  "c"  "a"  "b" 
    # [3,] "a"  "b"  "c"  "a"  "b"  "c" 
    # [4,] "b"  "a"  "b"  "c"  "a"  "b" 
    # [5,] "c"  "b"  "c"  "a"  "b"  "c" 
    # [6,] "a"  "c"  "a"  "b"  "c"  "a" 
    
  • 然后我们cbind统一的字符串(通过applypaste)

<小时/>

性能:

library(microbenchmark)
library(dplyr)
library(tidyr)
library(stringr)

microbenchmark(
  tidy1 = {
    gtools::permutations(n = 3, r = 6, v = abc, repeats.allowed = TRUE) %>% 
      data.frame() %>% 
      unite(united, sep = "", remove = FALSE) %>%
      filter(!str_detect(united, "([a-c])\\1"))
  },
  tidy2 = {
      filter(unite(data.frame(gtools::permutations(n = 3, r = 6, v = abc, repeats.allowed = TRUE)),
                   united, sep = "", remove = FALSE),
             !str_detect(united, "([a-c])\\1"))
  },
  base = {
    r <- replicate(6, seq_len(length(abc)-1), simplify=FALSE)
    r[[1]] <- c(r[[1]], length(abc))
    m <- t(apply(do.call(expand.grid, r), 1, cumsum) %% length(abc) + 1)
    m[] <- abc[m]
  },
  times=10000
)
# Unit: microseconds
#   expr      min        lq     mean   median       uq       max neval
#  tidy1 1875.400 2028.8510 2446.751 2165.651 2456.051 12790.901 10000
#  tidy2 1745.402 1875.5015 2284.700 2000.051 2278.101 50163.901 10000
#   base  796.701  871.4015 1020.993  919.801 1021.801  7373.901 10000

我尝试了中缀(非%>%)tidy2版本只是为了好玩,虽然我相信理论上它会更快,但我没有意识到它会节省超过7%的费用运行时间。 (50163 可能是 R 垃圾收集,而不是“真实的”。)我们为可读性/可维护性付出的代价。

关于arrays - 6 个位置内 3 个元素的排列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53566191/

相关文章:

javascript - 包含其他数组的计数项的数组

c - 如何使用快速排序对一对整数的结构进行排序?

r - 是否有 R 命令触发从 Rstudio Server 下载文件?

python - 拆分总和 K 方式组合数学 python

algorithm - 从给定字符串打印按字典顺序排序的子集

java - 将二维数组转换为一维数组

android - 在 Android 中转义多个 “%” 字符

r - 使用 lubridate 根据日期创建因子

通过多列排列对数据框中的行重新排序

c++ - 使用 STL 置换 std::vector 元素的最短解决方案