【问题标题】:Create 20 unique bingo cards创建 20 张独特的宾果卡
【发布时间】:2015-10-24 08:32:47
【问题描述】:

我正在尝试创建 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-10,第二列 11-20,第三列 - 21-30)。

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

【问题讨论】:

  • 我投票重新开放,恕我直言并不太宽泛,这里的问题对我来说似乎很准确。

标签: sql r excel


【解决方案1】:

(注:我误读了“行”而不是“列”,所以下面的代码和解释将处理第一行随机数 1-10、第二行随机数 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. 得到它们的笛卡尔积

使用gtools包的函数permutations可以很容易地获得步骤(1)(参见代码中的对象allPerms)。请注意,我们只需要第一行的排列(即从 1-10 中取出的 3 个元素),因为可以通过分别添加 10 和 20 从第一行轻松获得其他行的排列。

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

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

例如

# 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 函数),然后对于每个索引,我们使用函数 getKthPermWithRep1:720 中获取 3 个数字的对应排列。 最后,这些数字三元组可以通过将它们用作子集allPerms 的索引转换为实际的卡片行,并得到我们的最终矩阵(当然,在第二行和第三行添加+10+20 之后)。


奖金

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)。因此,当您在 1:n 中通过重复 r 元素来询问第 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函数。

【讨论】:

  • 看起来它可能会工作......虽然它比我更复杂,但我到达那里,只需要更多的研究。非常感谢您的详尽解释!
  • @BlueSkies:如答案所述,在 gtools 包中
  • @BlueSkies:顺便说一句,当您认为某个答案有帮助时,您应该考虑支持它甚至接受它(不仅在这里,而且在您的其他问题中)。这是stackoverflow的工作方式;)
  • 你的意思是例如在第一行你可以得到:“1,3,3”?不,你不能,因为我们使用包含 3 个元素的所有可能唯一行的 allperms 矩阵(在 1:10 中)。
  • 完美,我看到了重复项,因为我尝试将行与列交换并且保留一行不变。这一切对我来说都很有意义,并且是一个简洁明了的答案。非常感谢!
【解决方案2】:

可以使用以下代码生成一个具有所需值范围的 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]])
}

【讨论】:

  • 概率非常低,因为每次调用 sample() 都会生成元素的随机排列。
  • 在矩阵的相同九个位置选择相同的九个数字是非常不可能的。尽管如此,可以添加 if 语句来检查任何两个矩阵是否相等,如果相等,则可以再次调用新的 for 循环。
  • 这对我来说听起来是一个相当棘手的问题,因为大概如果你有两张相同数字的卡片,但顺序不同,那也是不可接受的。
  • 好吧,如果顺序无关紧要,即它是唯一组合而不是排列的问题,那么检查唯一性将非常困难。
【解决方案3】:

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

这是每张宾果卡的唯一签名,如果更改任何列中的数字顺序而不更改实际数字,该签名将保持不变。公式是

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

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

这个想法是为每列生成一个 10 位数字,然后将每个数字乘以一个常数并相加得到签名。

因此,我在这里使用来自here 的标准公式生成了两张随机宾果卡,再加上两张故意将它们设置为彼此的排列。

然后我使用公式检查是否有任何签名重复

=MAX(COUNTIF(D5:D20,D5:D20))

给出的答案不应超过 1。

万一出现重复,您只需按 F9 并生成一些新卡片。

所有公式都是数组公式,必须用CtrlShiftEnter

输入

【讨论】:

  • 有点耗时和手动解析,但想象一下如果你必须生成 100 张卡片?
  • 好吧,每列有 120 种可能的组合(从 10 种中选择 3 种),所以三列 120^3 = 1728000 所以即使有 100 张牌,你也会很不幸得到重复:检查只是为了安心。
  • 根据lazycackle.com/…,100 张卡片的重复机会不到 1/300,但肯定有 1000 张卡片,您可能需要不同的方法(尽管公平地说,这不是您的问题) .
【解决方案4】:

这是一种不优雅的方法。生成所有可能的组合,然后在不替换的情况下进行采样。这些是排列组合:宾果游戏中的顺序很重要

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)

【讨论】:

    猜你喜欢
    • 2011-11-24
    • 2018-07-27
    • 2012-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 2015-05-07
    相关资源
    最近更新 更多