简化此网格,使每行和每列具有1个值

时间:2016-12-09 20:03:39

标签: r

此处的示例代码:

> temp2
  a b c d e f g h
i 1 1 0 0 0 1 0 1
j 0 1 0 0 0 1 0 1
k 0 1 1 0 0 1 1 1
l 0 0 0 0 1 0 0 1
m 0 0 1 1 0 0 1 1
n 0 0 1 1 0 0 1 1
o 0 0 0 1 0 0 1 1
p 0 0 0 0 1 0 0 1

> dput(temp2)
structure(list(a = c(1, 0, 0, 0, 0, 0, 0, 0), b = c(1, 1, 1, 
0, 0, 0, 0, 0), c = c(0, 0, 1, 0, 1, 1, 0, 0), d = c(0, 0, 0, 
0, 1, 1, 1, 0), e = c(0, 0, 0, 1, 0, 0, 0, 1), f = c(1, 1, 1, 
0, 0, 0, 0, 0), g = c(0, 0, 1, 0, 1, 1, 1, 0), h = c(1, 1, 1, 
1, 1, 1, 1, 1)), .Names = c("a", "b", "c", "d", "e", "f", "g", 
"h"), class = "data.frame", row.names = c("i", "j", "k", "l", 
"m", "n", "o", "p"))

我有1x和0s的8x8网格。我需要解决一些网格,其中每行和每列只有一个1和其余的0,但1必须在原始网格有1的地方。它几乎像一个数独问题但是不完全是。关于如何开始的任何想法?

我需要一些可以为普通网格执行此操作的功能,而不仅仅是这个特定功能。我们可以假设,在给定一些起始网格的情况下,始终存在解决方案网格。

谢谢!

编辑:有效的解决方案

> temp3
  a b c d e f g h
i 1 0 0 0 0 0 0 0
j 0 1 0 0 0 0 0 0
k 0 0 0 0 0 1 0 0
l 0 0 0 0 1 0 0 0
m 0 0 0 1 0 0 0 0
n 0 0 1 0 0 0 0 0
o 0 0 0 0 0 0 1 0
p 0 0 0 0 0 0 0 1

EDIT2:鉴于只有8个!任何网格的独特解决方案,我可能会尝试蛮力/匹配方法。

5 个答案:

答案 0 :(得分:9)

这可以解决为运输问题或整数编程问题。我们还展示了仅使用基R的单线解决方案,其产生随机矩阵,其中每行和每列列总和为1滤除并返回满足解附图矩阵的每个元素小于或等于的附加约束的矩阵。 temp2的相应元素。

1)运输问题在lpSolve中使用lp.transport我们可以在一个语句中解决它:

library(lpSolve)

res <- lp.transport(as.matrix(temp2), "max", 
  rep("=", 8), rep(1, 8), rep("=", 8), rep(1, 8), integers = 0:1)

res
## Success: the objective function is 8

soln <- array(res$solution, dim(temp2))

# verify

all(colSums(soln)==1) && all(rowSums(soln)==1) && all(temp2>=soln) && all(soln %in% 0:1)
## [1] TRUE

2)整数编程

如果X是解决方案,我们已经指定了行和列约束,但是没有指定X&lt; = temp2约束,因为它们将自动满足,因为没有解决方案将1放在temp2 0可以具有的最大目标8。

library(lpSolve)

n <- nrow(temp2)
obj <- unlist(temp2)
const_row <- t(sapply(1:n, function(i) c(row(temp2)) == i)) # each row sums to 1
const_col <- t(sapply(1:n, function(i) c(col(temp2)) == i)) # each col sums to 1
const.mat <- rbind(const_row, const_col)
res <- lp("max", obj, const.mat, "=", 1, all.bin = TRUE)
res
## Success: the objective function is 8

soln <- array(res$solution, dim(temp2))

# verify

all(colSums(soln)==1) && all(rowSums(soln)==1) && all(temp2>=soln) && all(soln %in% 0:1)
## [1] TRUE

(请注意,通过相同的参数我们可以将问题放宽到线性编程问题,只要我们添加0&lt; = soln [i,j]&lt; = 1约束,因为相同的参数允许我们省略soln [i,j]&lt; = temp2 [i,j]约束最大化将迫使soln元素为0或1。)

2a)此方法较长,但明确说明了X&lt; = temp2约束:

n <- nrow(temp2)
obj <- numeric(n*n)
const1 <- diag(n*n) # soln[i,j] <= temp2[i,j]
const2 <- t(sapply(1:n, function(i) c(row(temp2)) == i)) # each row sums to 1
const3 <- t(sapply(1:n, function(i) c(col(temp2)) == i)) # each col sums to 1
const.mat <- rbind(const1, const2, const3)
const.dir <- rep(c("<=", "="), c(n*n, 2*n))
const.rhs <- c(unlist(temp2), rep(1, 2*n))

res <- lp("max", obj, const.mat, const.dir, const.rhs, all.bin = TRUE)
res
## Success: the objective function is 0

soln <- array(res$solution, dim(temp2))

# verify

all(colSums(soln)==1) && all(rowSums(soln)==1) && all(temp2>=soln) && all(soln %in% 0:1)
## [1] TRUE

2b)请注意,如果X是解矩阵,则在X&lt; = temp2中,只有与temp2中的零对应的X的位置实际上受到约束,因此我们可以消除对应于1 in的任何约束(2a)解决方案中的temp2。通过此更改,所有约束都会成为等式约束。

n <- nrow(temp2)
obj <- numeric(n*n)
const1 <- diag(n*n)[unlist(temp2) == 0, ]
const2 <- t(sapply(1:n, function(i) c(row(temp2)) == i)) # each row sums to 1
const3 <- t(sapply(1:n, function(i) c(col(temp2)) == i)) # each col sums to 1
const.mat <- rbind(const1, const2, const3)
const.dir <- "="
const.rhs <- c(numeric(nrow(const1)), rep(1, 2*n))

res <- lp("max", obj, const.mat, const.dir, const.rhs, all.bin = TRUE)
res
## Success: the objective function is 0

soln <- array(res$solution, dim(temp2))

# verify

all(colSums(soln)==1) && all(rowSums(soln)==1) && all(temp2>=soln) && all(soln %in% 0:1)
## [1] TRUE

事实上,我们可以更进一步,删除与temp2的零元素相对应的变量。

3)r2dtable 这里我们使用问题中的rd2table to generate 10,000 8x8 tables whose rows and columns sum to 1 and then filter them to pick out only those satisfying the X < temp2 constrainsts. With temp2`,显示的随机种子找到了3个解。如果使用不同的输入它找不到解决方案,那么尝试生成更多数量的随机提议。这种方法不使用任何包。

set.seed(123) # for reproducibility
Filter(function(x) all(x <= temp2), r2dtable(10000, rep(1, 8), rep(1, 8)))

,并提供:

[[1]]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    0    0    0    0    0    0    0
[2,]    0    0    0    0    0    1    0    0
[3,]    0    1    0    0    0    0    0    0
[4,]    0    0    0    0    0    0    0    1
[5,]    0    0    0    0    0    0    1    0
[6,]    0    0    1    0    0    0    0    0
[7,]    0    0    0    1    0    0    0    0
[8,]    0    0    0    0    1    0    0    0

[[2]]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    0    0    0    0    0    0    0
[2,]    0    0    0    0    0    1    0    0
[3,]    0    1    0    0    0    0    0    0
[4,]    0    0    0    0    1    0    0    0
[5,]    0    0    0    1    0    0    0    0
[6,]    0    0    1    0    0    0    0    0
[7,]    0    0    0    0    0    0    1    0
[8,]    0    0    0    0    0    0    0    1

[[3]]
     [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8]
[1,]    1    0    0    0    0    0    0    0
[2,]    0    1    0    0    0    0    0    0
[3,]    0    0    0    0    0    1    0    0
[4,]    0    0    0    0    1    0    0    0
[5,]    0    0    1    0    0    0    0    0
[6,]    0    0    0    0    0    0    1    0
[7,]    0    0    0    1    0    0    0    0
[8,]    0    0    0    0    0    0    0    1

答案 1 :(得分:6)

蛮力方式:

m = as.matrix(temp2)
w = data.frame(which(m == 1, arr.ind = TRUE))
combos = as.matrix(do.call(expand.grid, with(w, split(col, row))))
combos[ apply(combos, 1, function(x) !anyDuplicated(x)), ]


      1 2 3 4 5 6 7 8
 [1,] 1 6 2 8 7 3 4 5
 [2,] 1 2 6 8 7 3 4 5
 [3,] 1 6 2 8 3 7 4 5
 [4,] 1 2 6 8 3 7 4 5
 [5,] 1 6 2 8 4 3 7 5
 [6,] 1 2 6 8 4 3 7 5
 [7,] 1 6 2 8 3 4 7 5
 [8,] 1 2 6 8 3 4 7 5
 [9,] 1 6 2 5 7 3 4 8
[10,] 1 2 6 5 7 3 4 8
[11,] 1 6 2 5 3 7 4 8
[12,] 1 2 6 5 3 7 4 8
[13,] 1 6 2 5 4 3 7 8
[14,] 1 2 6 5 4 3 7 8
[15,] 1 6 2 5 3 4 7 8
[16,] 1 2 6 5 3 4 7 8
OP声称只需要处理一个8x8网格,所以我觉得这个表现得还不错。结果的每一行都是一个解决方案。第一行说(1,1),(2,6),(3,2)......是解决方案。

使用data.table的变体:

library(data.table)
m = as.matrix(temp2)
comboDT = setDT(melt(m))[ value == 1, do.call(CJ, split(Var2, Var1)) ][, 
  rid := .I ][, melt(.SD, id="rid", variable.name="row", value.name="col")]

setkey(comboDT, rid)
comboDT[ .( comboDT[, !anyDuplicated(col), by=rid][(V1), rid]) ]

答案 2 :(得分:1)

这是有效的。让网格成为我的网格(从上面看temp2)。那么这将返回一个有效的网格

# create random sufficient grid
  counter = 0
  while(2 > 1) {
    counter = counter + 1
    if(counter == 10000) {
      break
    }
    rand_grid = matrix(0, nrow = 8, ncol = 8)
    indices_avail = seq(1,8,by=1)
    for(i in 1:8) {
      k = sample(indices_avail, 1)
      rand_grid[i, k] = 1
      indices_avail = indices_avail[indices_avail != k]
    }
    if(sum(grid[which(rand_grid == 1)]) == 8) {
      break
    }
    print(counter)
  }

答案 3 :(得分:1)

此方法将返回所有有效组合。首先找到所有矩阵行组合。然后详尽地搜索。如果矩阵大小增加,则必须改进此方法。一个简单的改进就是并行进行诊断测试。

st<-as.matrix(temp2) # make sure we are working with matrices
## This method will return all possible matrices of combinations
## in essence if you have diag(matr) = width matrix than you have
## a valid choice


## Helper function to build all combinations, there may be better way to
## do this but it gets the job done
allCombinationsAux<-function(z,nreg,x){
    if(sum(nreg)>1){
        innerLoop<-do.call(rbind,lapply(x[nreg&(z!=x)], test1,nreg&(z!=x),x))
        ret<-cbind(z,innerLoop )
    }
    else{
        ret<-x[nreg]
    }
    ret
}

## Build all of the combinations of possible matrices
combs<-do.call(rbind,lapply(x,function(y) allCombinationsAux(y,y!=x,x)))

## iterate through all the possible combinations of matrices, to find out
## which ones have 1s throughout the  diag
inds<-which(apply(combs,1,function(x) sum(diag(st[x,]))==8))

lapply(inds,function(x) st[combs[x,],])

答案 4 :(得分:0)

虽然这里已经有很好的答案,对于蛮力方法和实际使用数学,只是为了踢,这里是一个猜测和检查不匹配列滞后的版本。对于有问题的例子,它实际上证明是非常快的,作为奖励,你可以在任何特定的运行中找到新的答案!真有趣!代码:

set.seed(47)    # remove this to have more fun

mat.in <- as.matrix(temp2)  # we'll work in matrices
mat.out <- diag(8)  # a starting guess
dimnames(mat.out) <- dimnames(mat.in)  # make our answer pretty

iteration <- 1  # for kicks, a loop counter

while (any((mat.out != mat.in)[as.logical(mat.out)])) {
    mat.ref <- mat.out
    mat.out <- mat.out[, sample(8)]  # make this deterministic if you like
    inner <- 1  # don't repeat yourself (too much)
    while (any(mat.out != mat.ref) & inner <= 8) {
        mat.ref <- mat.out

        # find non-matching indices and lag those columns
        to.lag <- which((mat.out != mat.in)[as.logical(mat.out)])
        i <- 1:8
        i[to.lag] <- c(to.lag[length(to.lag)], to.lag[-length(to.lag)])
        mat.out <- mat.out[, i]

        cat(inner, " ")  # let's see what it does
        inner <- inner + 1
    }
    print(iteration)  # whoo, scrolling numbers
    iteration <- iteration + 1
}
## 1  2  3  [1] 1
## 1  2  3  4  5  6  7  8  [1] 2
## 1  2  [1] 3
## 1  2  3  [1] 4

,对于此特定种子返回

mat.out
##   a c e g d b f h
## i 1 0 0 0 0 0 0 0
## j 0 0 0 0 0 1 0 0
## k 0 1 0 0 0 0 0 0
## l 0 0 0 0 1 0 0 0
## m 0 0 1 0 0 0 0 0
## n 0 0 0 0 0 0 1 0
## o 0 0 0 1 0 0 0 0
## p 0 0 0 0 0 0 0 1

它当然可以进一步优化,但它已经很快(没有打印,这会减慢它):

Unit: microseconds
        expr     min       lq     mean  median      uq      max neval
 let's guess 137.796 383.6445 838.2327 693.819 1163.08 2510.436   100

在几分之一秒内完成100次运行。它比实际猜测(切断内循环)快得多:

Unit: microseconds
           expr     min       lq       mean    median        uq       max neval cld
    guess smart 148.997  349.916   848.6314   588.162  1085.841   3117.78   100  a 
 actually guess 322.458 7341.961 31197.1237 20012.969 47677.501 160250.02   100   b

但请注意,运气在这里发挥作用,如果解决方案较少,则需要更长的时间。如果没有解决方案,它将永远运行。当然,它可以通过确保它不会重复使用由sample(8)提供的相同的起始排列来优化以避免这样的命运(无论如何这是一个好主意,我认为这只是因为它只是贯穿其中而多余无论如何都要运行一些排列。劈开。