制作20张独特的宾果卡

时间:2015-10-24 08:32:47

标签: sql r excel

我试图用数字创建20张独特的牌,但我有点挣扎..所以基本上我需要创建20个独特的矩阵3x3,第一列中的数字为1-10,第二列中的数字为11-20列和第三列中的21-30 ..任何想法?我更喜欢在r中完成它,特别是因为我不了解Visual Basic。在Excel中我知道如何生成卡片,但不知道如何确保它们是独一无二的。

对我来说,它似乎非常精确和直接。无论如何,我需要创建20个矩阵,如下所示:

      [,1] [,2] [,3]
[1,]    5   17   23
[2,]    8   18   22
[3,]    3   16   24    

每个矩阵应该是唯一的,每个列应该由三个唯一的数字组成(第1列 - 数字1-10,第2列11-20,第3列 - 21-30)。

生成随机数很容易,但如何确保生成的卡片是唯一的?请查看我投票的帖子作为答案 - 因为它为您提供了如何实现它的全面解释。

4 个答案:

答案 0 :(得分:1)

(注意:我误读了“行”而不是“列”,所以下面的代码和解释将处理第1行的随机数1-10,第2行的11-20等矩阵。列,但它只是转置完全相同)

此代码应保证唯一性和良好的随机性:

library(gtools)

# helper function
getKthPermWithRep <- function(k,n,r){
  k <- k - 1
  if(n^r< k){
    stop('k is greater than possibile permutations')
  }
  v <- rep.int(0,r)
  index <- length(v)
  while ( k != 0 )
  {
    remainder<- k %% n  
    k        <- k %/% n
    v[index] <- remainder
    index <- index - 1
  }
  return(v+1)
}

# get all possible permutations of 10 elements taken 3 at a time
# (singlerowperms = 720)
allperms <- permutations(10,3) 
singlerowperms <- nrow(allperms)

# get 20 random and unique bingo cards
cards <- lapply(sample.int(singlerowperms^3,20),FUN=function(k){
    perm2use <- getKthPermWithRep(k,singlerowperms,3)
    m <- allperms[perm2use,]
    m[2,] <- m[2,] + 10
    m[3,] <- m[3,] + 20
    return(m)
    # if you want transpose the result just do: 
    # return(t(m))
  })

解释

(免责声明tl; dr)

为了保证随机性和唯一性,一种安全的方法是生成所有可能的宾果卡,然后在其中随机选择而无需替换。

要生成所有可能的卡片,我们应该:

  1. 为每行3个元素生成所有可能性
  2. 获取他们的笛卡尔产品
  3. 使用包permutations的函数gtools可以轻松获得步骤(1)(请参阅代码中的对象allPerms)。注意,我们只需要第一行的排列(即从1-10中取出的3个元素),因为通过分别加10和20可以容易地从第一行获得其他行的排列。

    步骤(2)也很容易进入R,但我们首先考虑将产生多少种可能性。步骤(1)每行返回720个案例,最后我们将有720*720*720 = 720^3 = 373248000个可能的宾果卡! 生成所有这些都是不实际的,因为占用的内存将是巨大的,因此我们需要找到一种方法来在这种大范围的可能性中获得20个随机元素,而不实际将它们保存在内存中。

    解决方案来自函数getKthPermWithRep,在给定索引k的情况下,它返回第k个排列,重复r元素取自1:n(请注意,在这种情况下,重复排列对应于笛卡尔积。)

    e.g。

    # all permutations with repetition of 2 elements in 1:3 are
    permutations(n = 3, r = 2,repeats.allowed = TRUE)
    #      [,1] [,2]
    # [1,]    1    1
    # [2,]    1    2
    # [3,]    1    3
    # [4,]    2    1
    # [5,]    2    2
    # [6,]    2    3
    # [7,]    3    1
    # [8,]    3    2
    # [9,]    3    3
    
    # using the getKthPermWithRep you can get directly the k-th permutation you want :
    
    getKthPermWithRep(k=4,n=3,r=2)
    # [1] 2 1
    getKthPermWithRep(k=8,n=3,r=2)
    # [1] 3 2
    

    因此,现在我们只选择1:720^3范围内的20个随机索引(使用sample.int函数),然后对于每个索引,我们得到从1:720取得的3个数字的相应排列功能getKthPermWithRep
    最后,这些数字的三元组可以转换为实际的卡行,方法是将它们用作子集allPerms的索引并获取最终矩阵(当然,之后将+10+20添加到第2和第3行)。

    加成

    getKthPermWithRep

    的说明

    如果你看一下上面的例子(在1:3中重复2个元素的排列),然后将1减去所有结果,你得到这个:

    > permutations(n = 3, r = 2,repeats.allowed = T) - 1
          [,1] [,2]
     [1,]    0    0
     [2,]    0    1
     [3,]    0    2
     [4,]    1    0
     [5,]    1    1
     [6,]    1    2
     [7,]    2    0
     [8,]    2    1
     [9,]    2    2
    

    如果您将每行的每个数字视为数字,您可以注意到那些行(00,01,02 ...)都是0到8之间的数字,以3为基数表示(是的,3为N)。因此,当您在r中重复1:n元素时询问第k个排列时,您还要求将k-1翻译为基数n并返回增加的数字1

    因此,给定算法将任何数字从基数10改为基数n:

    changeBase <- function(num,base){
      v <- NULL
      while ( num != 0 )
      {
        remainder = num %% base    # assume K > 1
        num       = num %/% base   # integer division
        v <- c(remainder,v)
      }
      if(is.null(v)){
        return(0)
      }
      return(v)
    }
    

    您可以轻松获得getKthPermWithRep功能。

答案 1 :(得分:0)

可以使用以下代码生成一个具有所需值范围的3x3矩阵:

mat <- matrix(c(sample(1:10,3), sample(11:20,3), sample(21:30, 3)), nrow=3)

此外,您可以使用for循环生成20个唯一矩阵的列表,如下所示:

for (i in 1:20) {
mat[[i]] <- list(matrix(c(sample(1:10,3), sample(11:20,3), sample(21:30,3)), nrow=3))
print(mat[[i]])
}

答案 2 :(得分:0)

好吧,我可能会在这里摔倒,但我建议使用校验和(使用Excel)。

这是每个宾果卡的唯一签名,如果在不更改实际数字的情况下更改任何列中的数字顺序,它将保持不变。公式是

=SUM(10^MOD(A2:A4,10)+2*10^MOD(B2:B4,10)+4*10^MOD(C2:C4,10))

第一张牌的宾果号码在A2:C4。

我们的想法是为每列生成一个10位数的数字,然后将每个数字乘以一个常数并添加它们以获得签名。

所以我在这里使用here加上两个标准公式生成了两张随机宾果卡,故意将它们作为彼此的排列。

enter image description here

然后我使用公式

检查是否有任何签名是重复的
=MAX(COUNTIF(D5:D20,D5:D20))

不应该给出超过1的答案。

如果有重复的话,你只需按F9并生成一些新卡。

所有公式都是数组公式,必须使用 Ctrl Shift 输入

输入

答案 3 :(得分:0)

这是一种不太优雅的方式。生成所有可能的组合,然后无需更换即可进行采样这些是排列,组合:订单在宾果游戏中很重要

library(dplyr)
library(tidyr)
library(magrittr)

generate_samples = function(n) {
  first = data_frame(first = (n-9):n)

  first %>%
  merge(first %>% rename(second = first)) %>% 
  merge(first %>% rename(third = first)) %>%
  sample_n(20)
}

suffix = function(df, suffix)
  df %>%
  setNames(names(.) %>%
             paste0(suffix))

generate_samples(10) %>% suffix(10) %>%
  bind_cols(generate_samples(20) %>% suffix(20)) %>%
  bind_cols(generate_samples(30) %>% suffix(30)) %>%
  rowwise %>%
  do(matrix = t(.) %>% matrix(3)) %>%
  use_series(matrix)