1

我希望在 R中实现类似于Knuth 的算法 X的东西。

问题:我有anxk 矩阵A,n>=k,实值条目代表成本。一般来说,n 和 k 都会非常小(n<10,k<5)。我想找到行到列的映射,以最小化矩阵的总成本,受限于不能使用单行两次的约束。

我认为这有点像算法 X,因为一个合理的方法似乎是:

  1. 在 A 中选择一列并找到其中的最小值。
  2. 删除该行和该列。现在你只剩下Asub了。
  3. 转到第 1 步并使用 Asub 和新列选择重复,直到 ncol(Asub)=1。

但是我不知道如何在 R 中创建一个递归数据结构来存储单元级成本的结果树。到目前为止,这是我所拥有的,它只使它下降了一个分支,因此找不到最佳解决方案。

# This version of the algorithm always selects the first column. We need to make it 
# traverse all branches.
algorithmX <- function(A) {
  for (c in 1:ncol(A)) {
    r <- which.min(A[,c])
    memory <- data.frame(LP_Number = colnames(A)[c], 
                         Visit_Number = rownames(A)[r], 
                         cost = as.numeric(A[r,c]))
    if (length(colnames(A))>1) {
      Ared <- A[-r, -c, drop=FALSE]
      return( rbind(memory, algorithmX(Ared)) )
    }
    else {
      return(memory)
    }
  }
}

foo <- c(8.95,3.81,1.42,1.86,4.32,7.16,12.86,7.59,5.47,2.12,
         0.52,3.19,13.97,8.79,6.52,3.37,0.91,2.03)
colnames(foo) <- paste0("col",c(1:3))
rownames(foo) <- paste0("row",c(1:6))
algorithmX(foo)

我确定我在如何处理 R 函数中的递归方面缺少一些基本知识。如果这个算法实际上不是最合适的,我也很高兴听到解决这个问题的其他方法。

4

2 回答 2

1

您错过了将 foo 设置为矩阵,因此您无法设置colnames(foo)or rownames(foo)。假设这只是一个错字,还有一个问题是你从不访问除 之外的任何东西c = 1,因为内部测试的两个分支都返回了一些东西。您可能希望在循环中收集结果,选择最好的,然后返回。

例如,

algorithmX <- function(A) {
  bestcost <- Inf
  save <- NULL
  for (c in 1:ncol(A)) {
    r <- which.min(A[,c])
    memory <- data.frame(LP_Number = colnames(A)[c], 
                         Visit_Number = rownames(A)[r], 
                         cost = as.numeric(A[r,c]))
    if (length(colnames(A))>1) {
      Ared <- A[-r, -c, drop=FALSE]
      memory <- rbind(memory, algorithmX(Ared)) 
    }
    if (sum(memory$cost) < bestcost) {
      bestcost <- sum(memory$cost)
      save <- memory
    }
  }
  return(save)
}
于 2019-04-19T15:36:17.960 回答
1

感谢上面的 user2554330 提供了一些关于如何构造递归函数以便保留值的指针。我按如下方式修改了他们的代码,现在它似乎可以工作了,捕获了我之前发现的所有极端情况,这需要我首先编写这个函数!

algorithmX <- function(A) {
  best.match <- data.frame(LP_Number=numeric(), Visit_Number=numeric(), cost=numeric(), total.cost=numeric())
  for (c in 1:ncol(A)) {
    r <- which.min(A[,c])
    memory <- data.frame(LP_Number = colnames(A)[c], 
                         Visit_Number = rownames(A)[r], 
                         cost = as.numeric(A[r,c]),
                         total.cost = as.numeric(NA))
    if (length(colnames(A))>1) {
      Ared <- A[-r, -c, drop=FALSE]
      memory <- rbind(memory, algorithmX(Ared))
    }
    total.cost <- summarize(memory, sum(cost)) %>% unlist() %>% as.numeric()
    memory$total.cost <- total.cost
    if (length(best.match$total.cost)==0 | memory$total.cost[1] < best.match$total.cost[1]) {
      best.match <- memory
    }
  }
  return(best.match)
}
于 2019-04-19T20:02:03.137 回答