Взвешенная упаковка для бин / оптимизация ранцев - PullRequest
0 голосов
/ 31 мая 2018

Я изо всех сил пытаюсь классифицировать проблему, над которой я работаю, а это означает, что я не смог выяснить, есть ли какие-либо установленные эвристические решения.Как вы думаете, что это за проблема, и как бы вы посоветовали мне ее решить?

У меня есть серия блоков A, B, C, D. Каждый из них может содержать определенное количествоПредметы.Общий размер ведер соответствует размеру населения.Каждый из элементов в популяции имеет оценку A, B, C, D.

Я хочу отсортировать элементы по группам так, чтобы общая оценка для соответствующих групп была максимизирована;то есть баллы A для всех предметов в корзине A, баллы B для всех предметов в корзине B и так далее.Из этого следует, что в идеале предмет может находиться в ведре B, даже если его оценка A выше, поскольку может быть много предметов с высокими баллами A и немногие с высокими баллами B.

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Для достаточно малых размеров метаэвристика (например, локальный поиск) может работать хорошо.

public class WeightedKnapsackProblem {

private int numberOfBins = 0;
private int numberOfItems = 0;

private int[][] scoreMatrix;
private int[] maxItemsPerBin;

public WeightedKnapsackProblem(int[][] score, int[] maxItemsPerBin){
    this.numberOfItems = score.length;
    this.numberOfBins = score[0].length;
    this.scoreMatrix = score;
    this.maxItemsPerBin = maxItemsPerBin;
}

public int score(int[] assignment){
    int s = 0;
    for(int i=0;i<numberOfItems;i++){
        int item = i;
        int bin = assignment[item];
        s += scoreMatrix[item][bin];
    }
    return s;
}

public int cost(int[] assignment){
    int c = 0;
    int[] tmp = new int[numberOfBins];
    for(int i=0;i<numberOfItems;i++){
        tmp[assignment[i]]++;
    }
    for(int i=0;i<numberOfBins;i++){
        if(tmp[i] > maxItemsPerBin[i])
            c++;
    }
    return c;
}

private java.util.Random RANDOM = new java.util.Random(System.currentTimeMillis());

private int[] mutate(int[] orig){
    int[] out = new int[orig.length];
    for(int i=0;i<orig.length;i++)
        out[i] = orig[i];
    out[RANDOM.nextInt(out.length)] = RANDOM.nextInt(numberOfBins);
    return out;
}

public int[] localSearch(){
    // initial assignment
    int[] a0 = new int[numberOfItems];
    for(int i=0;i<numberOfItems;i++)
        a0[i] = RANDOM.nextInt(numberOfBins);

    // max score for any item
    int max = scoreMatrix[0][0];
    for(int i=0;i<scoreMatrix.length;i++)
        for(int j=0;j<scoreMatrix[i].length;j++)
            max = java.lang.Math.max(max, scoreMatrix[i][j]);

    // local search
    int[] a1 = mutate(a0);
    int c0 = score(a0) - cost(a0) * max * max;
    int c1 = score(a1) - cost(a1) * max * max;
    for(int i=0;i<1000;i++){
        if(c1 > c0){
            a0 = a1;
            c0 = c1;
        }
        a1 = mutate(a0);
        c1 = score(a1) - cost(a1) * max;
    }

    // return
    return a0;
}

public int[] repeatedLocalSearch(int k){

    // max score for any item
    int max = scoreMatrix[0][0];
    for(int i=0;i<scoreMatrix.length;i++)
        for(int j=0;j<scoreMatrix[i].length;j++)
            max = java.lang.Math.max(max, scoreMatrix[i][j]);

    int[] a0 = localSearch();
    int c0 = score(a0) - cost(a0) * max * max;

    for(int i=0;i<k;i++){
        int[] a1 = localSearch();
        int c1 = score(a1) - cost(a1) * max * max;

        if(c1 > c0){
            c0 = c1;
            a0 = a1;
        }
    }

    return a0;
}
}

Эта программа, по сути, генерирует случайное присвоение элементов корзинам и итеративно пытается улучшить эту начальную ситуацию,

Поскольку с помощью этой техники вы можете легко попасть в локальные оптимумы, имеет смысл повторить это пару раз с различными (случайными) начальными конфигурациями.

В следующей программе используется WeightedKnapsackProblemкласс для генерации возможного решения:

    int[][] score = {   {9,5,2,3},
                        {8,9,2,1},
                        {3,2,1,4},
                        {1,2,1,2},
                        {7,8,9,2},
                        {0,1,2,3}
                    };
    int[] maxItemsPerBin = {2,1,2,1};

    WeightedKnapsackProblem wkp = new WeightedKnapsackProblem(score, maxItemsPerBin);
    int[] assignment =  wkp.repeatedLocalSearch(10);

    System.out.println(wkp.score(assignment));
    System.out.println(wkp.cost(assignment));
    System.out.println(Arrays.toString(assignment));

Это распечатывает:

34
0
[0, 1, 0, 3, 2, 2]

Другими словами, демонстрационная задача может быть решена за максимальный балл 34.
количество неуместных элементов (которое может превысить емкость) равно 0.

И назначение:

  • первый элемент в первом лотке
  • второй элемент во втором лотке
  • третий элемент в первой корзине
  • четвертый элемент в четвертой корзине
  • пятый и шестой элемент в третьей корзине
0 голосов
/ 31 мая 2018

Это выглядит как проблема минимального расхода с максимальным потоком .На самом деле это максимальная стоимость, но в нее можно внести поправки, просто отрицая веса.

Рассмотрим следующую сеть.Существует источник s и приемник t.

Каждый элемент i представлен вершиной u_i с ребром s -> u_i вместимостью 1 и стоимостью 0.

Каждое ведро также представлено вершиной: v_A v_B и так далее.Существует ребро v_A -> t с емкостью, равной размеру группы A и стоимостью 0, и аналогичными ребрами для других групп.

И наконец, есть ребра u_i -> v_G, которые имеют емкость 1 и стоимость, равную (минус оценка помещения элемента i в группу G).

Обратите внимание, что любой максимальный поток в этой сети соответствует разбиению элементов на группы, так что каждая группа имеет заданный размер.

Обратите внимание, что максимальный поток с минимальными затратами в этой сети является разделом, в котором максимальный общий балл раздела максимален.

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

...