找到最小化约束的最佳解决方案?

时间:2011-10-28 09:59:54

标签: python algorithm dynamic-programming

让我们把这个问题称为Slinger-Bird问题(实际上Slinger类似于服务器和鸟类的请求,但我正在思考它,所以我改变它们希望得到不同的观点!) 。

  • 有S石投掷者(slingers)和B鸟。
  • 甩尾者不在彼此的范围内。
  • 投掷一次可以杀死所有鸟类在一个抛物线的视线内,并将消耗一枪一次单位

我试图找出最佳解决方案,最大限度地减少了鸟类特定到达模式下杀死鸟类所需的时间和射击次数。让我举一个绝对数字的例子:3个Slingers和4个鸟。

        Time        1            2            3           4             5
Slinger
S1                B1, B2     B1, B2, B3       B4
S2                               B1         B1, B2      B3,B4     
S3                  B1         B3, B4                 B1,B2,B3,B4

我的数据如下:

>> print t
[
  {
    1: {S1: [B1, B2], S2: [], S3: [B1]}, 
    2: {S1: [B1, B2, B3], S2: [B1], S3: [B3, B4]},
    3: {S1: [B4], S2: [B1,B2], S3: []},
    4: {S1: [], S2: [B3, B4], S3: [B1, B2, B3, B4]}
  }
]

有许多我能想到的解决方案(Sx在t = k意味着抛物线Sx在时间k拍摄):

  1. S1 = t = 1,S1为t = 2,S1为t = 3 < - 成本:3次+ 3次单位= 6
  2. S1在t = 2,S1在t = 3 < - 成本:2枪+3时间单位= 5
  3. S1 = t = 1,S3为t = 2 < - 成本:2次+ 2次单位= 4
  4. S3 at t = 4&lt; - 成本:1次+ 4次单位= 5
  5. 对我而言,解决方案 3 似乎是最佳解决方案。当然,我是手工完成的(所以有可能我可能错过了一些东西)但是我想不出这种可扩展的方式。此外,我担心有一些角落案件,因为一个射击者的决定可能会改变他人的决定,但因为我有全球视野,可能无关紧要。

    在python中解决这个问题的快速而好的方法是什么?我很难想出一个好的数据结构来做这个,而不用算法去做。我正在考虑使用动态编程,因为这似乎涉及状态空间探索,但对如何继续进行有点困惑。有什么建议吗?

3 个答案:

答案 0 :(得分:4)

这不是一个最佳的分配问题,因为甩尾者会杀死所有的鸟类。

你有一个二维目标函数,所以在镜头和时间之间可以有许多权衡。确定特定时间限制的最小拍摄数量正是设定的覆盖问题(正如mhum所建议的那样)。集合覆盖问题是NP难以近似的,但在实践中,使用线性规划公式的对偶进行分支和绑定对于找到最优值非常有效。

答案 1 :(得分:1)

我建议对slingers和birds使用位图,即

S1 = B1 = 1, S2 = B2 = 2, S3 = B3 = 4, B4 = 8

然后输入数据可以写为

bird_data = [[3, 0, 1], [7, 1, 12], [8, 3, 0], [0, 12, 15]]

现在可以像这样编写成本函数:

def cost(shots):
    hit = units = 0
    for show, shot in zip(bird_data, shots):
        units += 1
        for n, birds in enumerate(show):
            if shot & 1:
                units += 1
                hit |= birds
                if hit == 15: # all are hit
                    return units
            shot >>= 1
    return 99 # penalty when not all are hit

现在通过计算成本函数的最小值很容易找到最佳镜头:

from itertools import product

shot_sequences = product(*([range(7)]*len(bird_data)))

print min((cost(shots), shots) for shots in shot_sequences)

打印

(4, (0, 5, 0, 0))

这意味着当S1和S3(5 = 1 + 4)在t = 2时发射时,最佳值为4个单位。当然,您的解决方案也是可能的,其中S1在t = 1时触发,S3在t = 2时触发,两者都具有相同的成本。

然而,由于该算法使用暴力,在所有可能的镜头序列上运行,所以当数据集非常小时,它就是快速可行的,就像你的例子一样。

答案 2 :(得分:1)

我假设您在启动算法时知道示例中给出的所有数字,并且在完成t1之后不会得到t2等。

我还假设两个甩手可以同时射击,但这并不重要。

在第一个选项中,您可以为每个单元格指定一个值,即amountOfBirdsInCell-time。

这为您提供了两个值为1的单元格,即S1t1,S1t2,其余为较低。

只有最后一个单元格的时间计入您的分数,因此选择最早的单元格将消除下一轮的时间,使其成为最有价值的时间。这是第一个选择。

现在,从所有细胞中移除第一个选择中杀死的鸟类。

对剩余的单元格重复值确定过程。在您的示例中,单元格S3t2将给出最高结果,为0。

重复这个过程,您可以在最早的时间获得最有价值的细胞。

你的例子中没有涉及的一个重要方面:如果你的第一个最有价值的选择是在t2,那么下一个最有价值的选择可能是t1或t2,所以你应该考虑这些。但是,由于t2已经确认,因此您不应该将他们的时间考虑在内。

我从来没有写过python,我只是因为算法标签,所以这里有一些类似java / c的伪代码:

highestCellTime = 0;
while(birdsRemain)
{
    bestCell;

    for(every cell that has not been picked yet)
    {
        currentCellValue = amountOfBirds;
        if(currentCellTime > highestCellTime)
        {
            currentCellValue = currentCellValue - currentCellTime;
        }

        if(currentCellValue < bestCellValue)
        {
            bestCell = thisCell;
        }
        else if(currentCellValue == bestCellValue && currentCellTime < bestCellTime)
        {
            bestCell = thisCell;
        }
    }
    addCellToPicks(bestCell);
    removeBirdsFromOtherCells(bestCellBirds);
}

除非我忘记了某些内容,否则您现在可以在选择集合中拥有最佳的单元格组合。

我希望这段代码对python程序员有意义。如果有人可以翻译,请做!请删除这段文本以及之前提到的java / c-pseudocode。

OP编辑:第一个版本并没有以最佳细胞结束。我猜它一定是我代码中的一个错误但是我在这里发帖。

import math

cellsNotPicked = range(0,12)
cellToBird = {
    0: [1, 2],
    1: [],
    2: [1],
    3: [1,2,3],
    4: [1],
    5: [3,4],
    6: [4],
    7: [1,2],
    8: [],
    9: [],
    10: [3,4],
    11: [1,2,3,4]
}

picks = []

def getCellValue(cell):
    return len(cellToBird[cell])

def getCellTime(cell):
    return int(math.floor(cell / 3)) + 1

birdsRemain = 4

while(birdsRemain > 0):

    bestCell = 0;

    for thisCell in cellsNotPicked:

        currentCellValue = getCellValue(thisCell);

        currentCellTime = getCellTime(thisCell)
        highestCellTime = getCellTime(bestCell)

        if(currentCellTime > highestCellTime):
            currentCellValue = currentCellValue - currentCellTime;

        if(currentCellValue < getCellValue(bestCell)):
            bestCell = thisCell
        elif (currentCellValue == getCellValue(bestCell)) and (currentCellTime < getCellTime(bestCell)):
            bestCell = thisCell

    picks.append(bestCell)
    cellsNotPicked.remove(bestCell)

    birdsToRemove = cellToBird[bestCell]

    for key in cellToBird:
        for bird in birdsToRemove:
            try:
                cellToBird[key].remove(bird)
                birdsRemain -= 1
            except:
                pass

print picks