背包:如何将项目类型添加到现有解决方案

时间:2013-11-13 15:47:07

标签: ruby algorithm knapsack-problem

我一直在使用这种动态编程变体来解决背包问题:

KnapsackItem = Struct.new(:name, :cost, :value)
KnapsackProblem = Struct.new(:items, :max_cost)


def dynamic_programming_knapsack(problem)
  num_items = problem.items.size
  items = problem.items
  max_cost = problem.max_cost

  cost_matrix = zeros(num_items, max_cost+1)

  num_items.times do |i|
    (max_cost + 1).times do |j|
      if(items[i].cost > j)
        cost_matrix[i][j] = cost_matrix[i-1][j]
      else
        cost_matrix[i][j] = [cost_matrix[i-1][j], items[i].value + cost_matrix[i-1][j-items[i].cost]].max
      end
    end
  end

  cost_matrix
end

def get_used_items(problem, cost_matrix)
  i = cost_matrix.size - 1
  currentCost = cost_matrix[0].size - 1
  marked = Array.new(cost_matrix.size, 0) 

  while(i >= 0 && currentCost >= 0)
    if(i == 0 && cost_matrix[i][currentCost] > 0 ) || (cost_matrix[i][currentCost] != cost_matrix[i-1][currentCost])
      marked[i] = 1
      currentCost -= problem.items[i].cost
    end
    i -= 1
  end
  marked
end

这对于上面的结构非常有用,您只需提供名称,成本和价值。可以像下面这样创建项目:

 items = [
      KnapsackItem.new('david lee', 8000, 30) , 
      KnapsackItem.new('kevin love', 12000, 50), 
      KnapsackItem.new('kemba walker', 7300, 10),
      KnapsackItem.new('jrue holiday', 12300, 30),
      KnapsackItem.new('stephen curry', 10300, 80),
      KnapsackItem.new('lebron james', 5300, 90),
      KnapsackItem.new('kevin durant', 2300, 30),
      KnapsackItem.new('russell westbrook', 9300, 30),
      KnapsackItem.new('kevin martin', 8300, 15),
      KnapsackItem.new('steve nash', 4300, 15),
      KnapsackItem.new('kyle lowry', 6300, 20),
      KnapsackItem.new('monta ellis', 8300, 30),
      KnapsackItem.new('dirk nowitzki', 7300, 25),
      KnapsackItem.new('david lee', 9500, 35),
      KnapsackItem.new('klay thompson', 6800, 28)
    ]

  problem = KnapsackProblem.new(items, 65000)

现在,我遇到的问题是我需要为每个玩家添加一个位置,我必须让背包算法知道它仍然需要在所有玩家中最大化价值,除非有新的限制并且该限制是每个玩家都有一个位置,每个位置只能被选择一定次数。一些职位可以选择两次,其他职位一次。物品理想地成为这个:

KnapsackItem = Struct.new(:name, :cost, :position, :value)

职位会有如下限制:

PositionLimits = Struct.new(:position, :max)

限制将被实例化,可能如下所示:

limits = [Struct.new('PG', 2), Struct.new('C', 1), Struct.new('SF', 2), Struct.new('PF', 2), Struct.new('Util', 2)]

让这更加棘手的是每个玩家都处于Util位置。如果我们想要禁用Util位置,我们只需将2设置为0.

我们的原始项目数组如下所示:

items = [
          KnapsackItem.new('david lee', 'PF', 8000, 30) , 
          KnapsackItem.new('kevin love', 'C', 12000, 50), 
          KnapsackItem.new('kemba walker', 'PG', 7300, 10),
          ... etc ...
        ]

如何为背包算法添加位置限制,以便仍保留所提供的播放器池的最大值?

4 个答案:

答案 0 :(得分:4)

ruby​​中有一些高效的库可以满足你的任务,很明显你正在寻找一些constrain based optimization,ruby中有一些库是开源的所以,免费使用,只需包含它们在你的项目中。您需要做的就是从约束中生成Linear programming模型目标函数,并且库的优化器将生成满足所有约束的解,或者如果没有任何结果可以从给定的约束中得出结论,则说没有解决方案。

ruby​​中提供的一些此类库是

OPL遵循类似于IBM CPLEX的LP语法,IBM CPLEX是广泛使用的优化软件,所以你可以很好地参考如何使用它来建模LP,而且这是建立在RGLPK之上。

答案 1 :(得分:2)

据我了解,您指定的附加约束如下:

  

应该有一组元素,其中最多只有k(k = 1或   2)可以在解决方案中选择元素。应该有多个   这样的集合。

我想到了两种方法,这两种方法都不够有效。

方法1:

  1. 将元素划分为多个位置组。因此,如果有5个位置,则每个元素应分配给5个组中的一个。

  2. 通过从每个组中选择1(或2)元素并检查总价值和成本来迭代(或重复)所有组合。有一些方法可以让你了解一些组合。例如,在一个组中,如果有两个元素,其中一个元素以较低的成本提供更多的值,则另一个元素可以从所有解决方案中被拒绝。

  3. 方法2:

    Mixed Integer Linear Programming Approach.
    

    按如下方式制定问题:

    Maximize summation (ViXi) {i = 1 to N} 
    where Vi is value and 
    Xi is a 1/0 variable denoting presence/absence of an element from the solution.
    
    Subject to constraints:
    summation (ciXi) <= C_MAX {total cost}
    And for each group:
    summation (Xj) <= 1 (or 2 depending on position)
    All Xi = 0 or 1.
    

    然后你必须找到解决上述MILP的解算器。

答案 2 :(得分:1)

此问题类似于约束车辆路径问题。您可以尝试使用Clarke&amp; Wright的保存算法等启发式算法。您也可以尝试使用较少玩家的强力算法。

答案 3 :(得分:0)

考虑到玩家有五个位置你的背包问题是: -

   Knpsk(W,N,PG,C,SF,PF,Util) = max(Knpsk(W-Cost[N],N-1,...)+Value[N],Knpsk(W,N-1,PG,C,SF,PF,Util),Knpsk(W-Cost[N],N-1,PG,C,SF,PF,Util-1)+Value[N])

    if(Pos[N]=="PG") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG-1,....)

    if(Pos[N]=="C") then Knpsk(W-Cost[N],N-1,....) = Knpsk(W-Cost[N],N-1,PG,C-1....)

    so on...

  PG,C,SF,PF,Util are current position capacities 
  W is current knapsack capacity
  N number of items available

动态编程可以像以前一样使用7-D表,并且在你的情况下,位置的值很小,它会使算法减慢16倍,这对于n-p完全问题很有用

以下是JAVA中的动态编程解决方案:

public class KnapsackSolver {

    HashMap CostMatrix;
    // Maximum capacities for positions
    int posCapacity[] = {2,1,2,2,2};
    // Total positions 
    String[] positions = {"PG","C","SF","PF","util"}; 
    ArrayList playerSet = new ArrayList<player>();  
    public ArrayList solutionSet;
    public int bestCost;


    class player {

        int value;
        int cost;
        int pos;
        String name;

        public player(int value,int cost,int pos,String name) {
            this.value = value;
            this.cost = cost;
            this.pos = pos;
            this.name = name;
        }

        public String toString() {
            return("'"+name+"'"+", "+value+", "+cost+", "+positions[pos]);
        }

    }

    // Used to add player to list of available players
    void  additem(String name,int cost,int value,String pos) {
        int i;
        for(i=0;i<positions.length;i++) {
            if(pos.equals(positions[i]))
                break;
        }
        playerSet.add(new player(value,cost,i,name));
    }

    // Converts subproblem data to string for hashing
    public String encode(int Capacity,int Totalitems,int[] positions) {
        String Data = Capacity+","+Totalitems;
        for(int i=0;i<positions.length;i++) {
            Data = Data + "," + positions[i];
        }
        return(Data);
    }

    // Check if subproblem is in hash tables
    int isDone(int capacity,int players,int[] positions) {

        String k = encode(capacity,players,positions);

        if(CostMatrix.containsKey(k)) {
            //System.out.println("Key found: "+k+" "+(Integer)CostMatrix.get(k));
            return((Integer)CostMatrix.get(k));
        }

        return(-1);
    }

    // Adds subproblem added hash table
    void addEncode(int capacity,int players,int[] positions,int value) {

        String k = encode(capacity,players,positions);
        CostMatrix.put(k, value);
    }

    boolean checkvalid(int capacity,int players) {

        return(!(capacity<1||players<0));
    }

    // Solve the Knapsack recursively with Hash look up
    int solve(int capacity,int players,int[] posCapacity) {

        // Check if sub problem is valid

        if(checkvalid(capacity,players)) {

            //System.out.println("Processing: "+encode(capacity,players,posCapacity));

            player current = (player)playerSet.get(players);

            int sum1 = 0,sum2 = 0,sum3 = 0;

            int temp = isDone(capacity,players-1,posCapacity);

            // Donot add player

            if(temp>-1) {

                sum1 = temp;
            }
            else sum1 = solve(capacity,players-1,posCapacity);

            //check if current player can be added to knapsack

            if(capacity>=current.cost) {

                posCapacity[posCapacity.length-1]--;

                temp = isDone(capacity-current.cost,players-1,posCapacity);

                posCapacity[posCapacity.length-1]++;

                // Add player to util

                if(posCapacity[posCapacity.length-1]>0) {

                    if(temp>-1) {

                        sum2 = temp+current.value;
                    }
                    else {

                        posCapacity[posCapacity.length-1]--;
                        sum2 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
                        posCapacity[posCapacity.length-1]++;

                    }

                }

                // Add player at its position

                int i = current.pos;

                    if(posCapacity[i]>0) {

                        posCapacity[i]--;
                        temp  = isDone(capacity-current.cost,players-1,posCapacity);
                        posCapacity[i]++;
                        if(temp>-1) {

                            sum3 = temp+current.value;
                        }
                        else {

                            posCapacity[i]--;
                            sum3 = solve(capacity-current.cost,players-1,posCapacity)+current.value;
                            posCapacity[i]++;

                        }
                    }
                }   

            //System.out.println(sum1+ " "+ sum2+ " " + sum3 );


            // Evaluate the maximum of all subproblem   
            int res = Math.max(Math.max(sum1,sum2), sum3);

            //add current solution to Hash table
            addEncode(capacity, players, posCapacity,res);
            //System.out.println("Encoding: "+encode(capacity,players,posCapacity)+" Cost: "+res);

            return(res);


        }
        return(0);
    }

    void getSolution(int capacity,int players,int[] posCapacity) {


        if(players>=0) {
           player curr = (player)playerSet.get(players);
           int bestcost = isDone(capacity,players,posCapacity);
           int sum1 = 0,sum2 = 0,sum3 = 0;
           //System.out.println(encode(capacity,players-1,posCapacity)+" "+bestcost);
           sum1 = isDone(capacity,players-1,posCapacity);
           posCapacity[posCapacity.length-1]--;
           sum2 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
           posCapacity[posCapacity.length-1]++;
           posCapacity[curr.pos]--;        
           sum3 = isDone(capacity-curr.cost,players-1,posCapacity) + curr.value;
           posCapacity[curr.pos]++;

           if(bestcost==0)
               return;

           // Check if player is not added
           if(sum1==bestcost) {
               getSolution(capacity,players-1,posCapacity);
           }
           // Check if player is added to util
           else if(sum2==bestcost) {
               solutionSet.add(curr);
               //System.out.println(positions[posCapacity.length-1]+" added");
               posCapacity[posCapacity.length-1]--;
               getSolution(capacity-curr.cost,players-1,posCapacity);
               posCapacity[posCapacity.length-1]++;

           }
           else {
               solutionSet.add(curr);
               //System.out.println(positions[curr.pos]+" added");
               posCapacity[curr.pos]--;
               getSolution(capacity-curr.cost,players-1,posCapacity);
               posCapacity[curr.pos]++;     


           }   
        }


   }



    void getOptSet(int capacity) {

        CostMatrix = new HashMap<String,Integer>();
        bestCost = solve(capacity,playerSet.size()-1,posCapacity);
        solutionSet = new ArrayList<player>();
        getSolution(capacity, playerSet.size()-1, posCapacity);

    }



    public static void main(String[] args) {

        KnapsackSolver ks = new KnapsackSolver();
        ks.additem("david lee", 8000, 30, "PG"); 
        ks.additem("kevin love", 12000, 50, "C"); 
        ks.additem("kemba walker", 7300, 10, "SF");
        ks.additem("jrue holiday", 12300, 30, "PF");
        ks.additem("stephen curry", 10300, 80, "PG");
        ks.additem("lebron james", 5300, 90, "PG");
        ks.additem("kevin durant", 2300, 30, "C");
        ks.additem("russell westbrook", 9300, 30, "SF");
        ks.additem("kevin martin", 8300, 15, "PF");
        ks.additem("steve nash", 4300, 15, "C");
        ks.additem("kyle lowry", 6300, 20, "PG");
        ks.additem("monta ellis", 8300, 30, "C");
        ks.additem("dirk nowitzki", 7300, 25, "SF");
        ks.additem("david lee", 9500, 35, "PF");
        ks.additem("klay thompson", 6800, 28,"PG");
        //System.out.println("Items added...");
       // System.out.println(ks.playerSet);
        int maxCost = 30000;
        ks.getOptSet(maxCost);
        System.out.println("Best Value: "+ks.bestCost);
        System.out.println("Solution Set: "+ks.solutionSet);
    }




}

注意:如果具有某些位置的玩家被添加超过其容量,那么添加为util的玩家因为任何位置的玩家都可以添加到util。