Рюкзак: как добавить тип элемента в существующее решение

Я работаю с этим вариантом динамического программирования для решения проблемы с рюкзаком:

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 ...
        ]

Как можно добавить ограничения по положению к алгоритму ранца, чтобы сохранить максимальное значение для предоставленного пула игроков?

Ответ 1

В рубине есть несколько эффективных библиотек, которые могут удовлетворить вашу задачу. Ясно, что вы ищете некоторую оптимизацию ограничений на основе ограничений, есть некоторые библиотеки в рубине, которые являются открытым исходным кодом, поэтому, свободно использовать, просто включите их в проект. Все, что вам нужно сделать, это генерировать "Линейное программирование" . Функция объектной модели из ваших ограничений и оптимизатора библиотеки будет генерировать решение, которое удовлетворяет всем вашим ограничениям, или говорит, что нет решение существует, если из заданных ограничений ничего не может быть сделано.

Некоторые из таких библиотек, доступных в ruby,

OPL следует синтаксису LP, подобному IBM CPLEX, который широко используется в программном обеспечении оптимизации. Таким образом, вы можете получить хорошие ссылки на то, как моделировать LP, используя это. Кроме того, это построено поверх RGLPK.

Ответ 2

Как я понимаю, дополнительное ограничение, которое вы указываете, следующее:

Должен быть набор элементов, из которых не более k (k = 1 или 2) элементы могут быть выбраны в решении. Должно быть многократное такие множества.

Есть два подхода, которые мне приходят, ни один из них недостаточно эффективен.

Подход 1:

  • Разделите элементы на группы позиций. Поэтому, если имеется 5 позиций, каждый элемент должен быть назначен одной из 5 групп.

  • Итерацию (или повторение) через все комбинации путем выбора 1 (или 2) элемента из каждой группы и проверки общего значения и стоимости. Есть способы, которыми вы можете понять некоторые комбинации. Например, в группе, если есть два элемента, в которых один дает большее значение при меньшей стоимости, тогда другой может быть отклонен из всех решений.

Подход 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.

Ответ 3

Эта проблема похожа на проблему маршрутизации транспортного средства ограничения. Вы можете попробовать эвристику, как алгоритм экономии от Clarke & Wright. Вы также можете попробовать алгоритм грубой силы с меньшим количеством игроков.

Ответ 4

Учитывая, что у игроков есть пять позиций, ваша проблема с рюкзаком будет: -

   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);
    }




}

Примечание. Если игроки с определенными позициями добавлены больше, чем их емкость, то добавляются как утилиты, так как игроки из любой позиции могут быть добавлены к использованию.