如何根据特定标准找到多个数据的最佳组合?

时间:2010-09-05 07:33:22

标签: algorithm

我正在尝试编写一个Python脚本,该脚本可以找到符合特定条件的游戏装甲项目组合。我有一个物品,每个物品槽都有钥匙(即头部,胸部,腰部等),以及每个钥匙中可以放入该槽位的所有物品的清单。每个项目共有10个插槽和多个项目,最多可达88个项目。

我的问题是:是否有某种算法已经用于做这样的事情?我想要做的一个例子是找到装甲件的组合,这给我stat1< 35具有最高的stat2 + 3 + 4。

我不相信粗暴强迫它是实用的,因为它需要很长时间(如果我错了,请纠正我)。任何帮助将不胜感激!

编辑 - 更多详情:

数据样本:http://pastebin.com/rTH3Q5Sj第一个元组是2个头部插槽项目,第二个元组是2个胸部插槽项目。

我可能想要对样本数据做的一件事是得到头盔和胸部的组合,其总削减/钝击/刺穿总数虽然少于12个。

2 个答案:

答案 0 :(得分:4)

听起来优雅的解决方案是linear programming。如果您提供更多详细信息,我可以提供帮助。

只有88个项目分散在10个插槽中,粗暴强迫它也不会很糟糕。两者的某些组合可能是最简单的。

更新

根据您提供的更新,我认为线性编程过度(并且难以应用)。我写了这个相当普遍的解决方案。研究并理解它。如果有人有任何改正或改进,我很乐意听到。

from itertools import ifilter, product

# Definition of ITEMS cut for brevity. See below.    

def find_best(slots, maximize, constraints):
    """example call:
         find_best(['helm', 'chest'], ['slashing', 'bludgeon'],
                   {'encumbrance': 12})
    """
    # save the slot names to construct a nice return value
    slot_names = slots

    # pull the actual lists of items for each slot out of the global dict
    slots = [ITEMS[slot] for slot in slots]

    # this function calculates the value of a solution
    value = lambda solution: sum(float(slot[attr]) for attr in maximize
                                                   for slot in solution)

    # replace our constraints with functions to check solutions
    constraints = [lambda solution:
                       sum(float(slot[attr]) for slot in solution) < constraint
                 for attr, limit in constraints.items()]

    # start with all possible combinations
    solutions = product(*slots)

    # chain together ifilters to weed out the ones that fail any of the
    # constraints. Note that for optimum performance, you should place the
    # constraints in descending order of probability to fail
    for constraint in constraints:
        solutions = ifilter(constraint, solutions)

    # We're going to do decorate, max, undecorate
    solutions = ((value(solution), solution) for solution in solutions)
    value, solution = max(solutions)

    # get the indexes and return
    return dict((name, slot.index(item)) for name, slot, item
                in zip(slot_names, slots, solution))

请注意,您应该将值存储为浮点数,将存储为字符串!它更容易(因为它通常在你需要时自动)转换为字符串而不是浮点数。然后你可以从我的代码中取出丑陋的演员阵容。我只是懒得为你做。请注意,您可以根据需要调用尽可能多的约束,但只能使用一组属性进行最大化。这对我来说很有意义。如果您研究代码并理解它,您应该能够根据自己的喜好对其进行修改。

以下是我修改数据结构的方法。

ITEMS = { 'helm': [{'Acid':' 2.71',
                        'Bludgeoning': '1.04',
                        'Cold': '2.71',
                        'Encumbrance': '8.00',
                        'Fire': '2.71',
                        'Holy': '2.71',
                        'Impact': '1.30',
                        'Lightning': '2.00',
                        'Name': 'Plate Helm',
                        'Piercing': '1.17',
                        'Slashing': '1.30',
                        'Unholy': '2.71'},

                       {'Acid': '2.18',
                        'Bludgeoning': '0.92',
                        'Cold': '2.18',
                        'Encumbrance': '7.00',
                        'Fire': '2.18',
                        'Holy': '2.18',
                        'Impact': '1.15',
                        'Lightning': '1.65',
                        'Name': 'Scale Helm',
                        'Piercing': '1.03',
                        'Slashing': '1.15',
                        'Unholy': '2.18'}],

              'chest':[{'Acid': '5.47',
                        'Bludgeoning': '2.05',
                        'Cold': '5.47',
                        'Encumbrance': '32.00',
                        'Fire': '5.47',
                        'Holy': '5.47',
                        'Impact': '2.57',
                        'Lightning': '4.06',
                        'Name': 'Plate Chest',
                        'Piercing': '2.31',
                        'Slashing': '2.57',
                        'Unholy': '5.47'},

                       {'Acid': '4.45',
                        'Bludgeoning': '1.84',
                        'Cold': '4.45',
                        'Encumbrance': '28.00',
                        'Fire': '4.45',
                        'Holy': '4.45',
                        'Impact': '2.30',
                        'Lightning': '3.31',
                        'Name': 'Scale Cuirass',
                        'Piercing': '2.07',
                        'Slashing': '2.30',
                        'Unholy': '4.45'}]}

请注意,最外层字典的值是列表而不是您所说的元组。有一个很大的区别!

答案 1 :(得分:1)

似乎是Knapsack problem的变体。如果您的体重(统计数据?)限制不是太大,动态编程应该足够好。

修改

这是Java动态编程解决方案的原型:

public static int getBestAcidSum(int[][] acid, int[][] fire, int maxFire) {
    int slots = acid.length;
    int[][] acidSum = new int[slots][maxFire + 1];

    for (int j = 0; j < acid[0].length; j++)
        acidSum[0][fire[0][j]] = Math.max(acidSum[0][fire[0][j]], acid[0][j]);

    for (int i = 1; i < slots; i++)
        for (int j = 0; j < acid[i].length; j++)
            for (int fireSum = fire[i][j]; fireSum <= maxFire; fireSum++)
                if (acidSum[i-1][fireSum - fire[i][j]] > 0)
                    acidSum[i][fireSum] = Math.max(acidSum[i][fireSum], acidSum[i-1][fireSum - fire[i][j]] + acid[i][j]);

    int ret = 0;
    for (int fireSum = 0; fireSum <= maxFire; fireSum++)
        ret = Math.max(ret, acidSum[slots - 1][fireSum]);
    return ret;
}

public static void main(String[] args) {
    System.out.println(getBestAcidSum(new int[][] {{271, 218},  {547, 445}}, new int[][] {{271, 218}, {547, 445}}, 800));
}

算法为O(N * C),其中N是项目总数,C是约束(示例中为“maxFire”)。应该立即使用您提到的数量。

代码非常简单,但它保持了基本的算法难度。它只返回满足约束的最佳和。应该很容易修改以返回实际组合。将多个统计数据汇总在一起也应该很容易合并。通过乘以100将值转换为整数。