Оптимизации с дискретными параметрами в МатЛАБ

у меня есть 12 наборов векторов (около 10-20 векторов каждый), и я хочу выбрать один вектор каждого набора, чтобы функция f, которая принимает сумму этих векторов в качестве аргумента, была максимизирована. Кроме того, у меня есть ограничения для некоторых компонентов этой суммы.

пример:

a_1 = [3 2 0 5], a_2 = [3 0 0 2], a_3 = [6 0 1 1], ... , a_20 = [2 12 4 3]
b_1 = [4 0 4 -2], b_2 = [0 0 1 0], b_3 = [2 0 0 4], ... , b_16 = [0 9 2 3]
...
l_1 = [4 0 2 0], l_2 = [0 1 -2 0], l_3 = [4 4 0 1], ... , l_19 = [3 0 9 0]

s = [s_1 s_2 s_3 s_4] = a_x + b_y + ... + l_z

ограничения:

s_1 > 40
s_2 < 100
s_4 > -20

цель: выберите x, y, ... , z для максимизации f (s):

f(s) -> max

где f-нелинейная функция, которая принимает вектор s и возвращает a скалярный.

Bruteforcing занимает слишком много времени, потому что есть около 5,9 триллионов комбинаций, и поскольку мне нужен максимум (или даже лучше 10 лучших комбинаций), я не могу использовать ни один из жадных алгоритмов, которые пришли мне на ум.

векторы довольно разрежены, около 70-90% - нули. Если это как-то помогает ...?

MATLAB Optimization toolbox также не помог, поскольку он не очень поддерживает дискретную оптимизацию.

3 ответов


в основном это проблема выбора замка, где штыри замка имеют 20 различных положений, и есть 12 штырей. Также:

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

...интересно!

основываясь на подходе Расмана и комментарии Phpdna, и в предположение, что вы используете int8 тип данных, при заданных ограничениях есть

>> d = double(intmax('int8'));
>> (d-40) * (d+100) * (d+20) * 2*d
ans =
    737388162

возможные векторы s (плюс-минус несколько, не думал о +1 и т. д.). ~740 миллионов оценок ваших относительно простых f(s) не должно занять более 2 секунд, и обнаружив все s, которые максимизируют f(s), у вас остается проблема поиска линейных комбинаций в вашем векторном наборе, которые складываются в одно из этих решений s.

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

int16:   ans = 2.311325368800510e+018
int32:   ans = 4.253529737045237e+037
int64:   ans = 1.447401115466452e+076

Итак, я буду обсуждать более прямой и более общий подход здесь.

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

к сожалению, нет ничего подобного в панели инструментов оптимизации (или обмена файлами, насколько я мог найти). fmincon нет, поскольку он использует градиент и Гессиан информации (которая обычно будет все-ноль для целых чисел), и fminsearch не-идти, так как вам понадобится действительно хорошая начальная оценка, и скорость сходимости (примерно)O(N), что означает, что для этой 20-мерной проблемы вам придется подождать задолго до сближения, без гарантия нахождения глобального решения.

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

Итак, если вы не чувствуете, как реализация собственного нелинейного двоичного целочисленного программирования алгоритм, или не в настроении для приключений с INTLAB, на самом деле осталось только одно: эвристические методы. В этой ссылке существует аналогичная ситуация, с наброском решения: используйте генетический алгоритм (ga) из панели инструментов глобальная оптимизация.

я бы реализовал проблему примерно так:

function [sol, fval, exitflag] = bintprog_nonlinear()

    %// insert your data here
    %// Any sparsity you may have here will only make this more 
    %// *memory* efficient, not *computationally*
    data = [... 
        ...  %// this will be an array with size 4-by-20-by-12
        ...  %// (or some permutation of that you find more intuitive)
        ];

    %// offsets into the 3D array to facilitate indexing a bit
    offsets = bsxfun(@plus, ...
        repmat(1:size(data,1), size(data,3),1), ...
        (0:size(data,3)-1)' * size(data,1)*size(data,2));   %//'

    %// your objective function
    function val = obj(X)

        %// limit "X" to integers in [1 20]
        X = min(max(round(X),1),size(data,3));

        %// "X" will be a collection of 12 integers between 0 and 20, which are 
        %// indices into the data matrix

        %// form "s" from "X"        
        s = sum(bsxfun(@plus, offsets, X*size(data,1) - size(data,1)));


        %// XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX        
        %// Compute the NEGATIVE VALUE of your function here
        %// XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxX


    end

    %// your "non-linear" constraint function 
    function [C, Ceq] = nonlcon(X)

        %// limit "X" to integers in [1 20]
        X = min(max(round(X),1),size(data,3));

        %// form "s" from "X"        
        s = sum(bsxfun(@plus, offsets, X(:)*size(data,1) - size(data,1)));

        %// we have no equality constraints
        Ceq = [];

        %// Compute inequality constraints
        %// NOTE: solver is trying to solve C <= 0, so: 
        C = [...
            40 - s(1)
            s(2) - 100
            -20 - s(4)
        ];

    end

    %// useful GA options
    options = gaoptimset(...
        'UseParallel', 'always'...
        ...
    );

    %// The rest really depends on the specifics of the problem.
    %// Useful to look at will be at least 'TolCon', 'Vectorized', and of course, 
    %// 'PopulationType', 'Generations', etc.

    %// THE OPTIMZIATION 
    [sol, fval, exitflag] = ga(...
        @obj, size(data,3), ...  %// objective function, taking a vector of 20 values
        [],[], [],[], ...        %// no linear (in)equality constraints
        1,size(data,2), ...      %// lower and upper limits
        @nonlcon, options);      %// your "nonlinear" constraints


end

обратите внимание, что, хотя ваши ограничения по существу линейный, кстати, который вы должны вычислить значение для вашего s требует использования пользовательской функции ограничения (nonlcon).

особенно обратите внимание, что в настоящее время это (вероятно) неоптимальный способ использования ga -- Я не знаю специфики вашей целевой функции,поэтому возможно гораздо больше. Например, в настоящее время я использую простой round() для преобразования входных данных X целым числам, но с помощью 'PopulationType', 'custom' (с настраиваемым 'CreationFcn', 'MutationFcn' etc.) может дать лучшие результаты. Кроме того,'Vectorized' вероятно, ускорит многое, но я не знаю, легко ли ваша функция векторизована.

и да, я использую вложенные функции (я просто люблю такие вещи!); это предотвращает эти огромные, обычно идентичные списки входных аргументов, если вы используете подфункции или автономные функции, и они действительно могут повысить производительность, потому что мало копирования данных. Но я понимаю, что их правила обзора делают их несколько похожими на goto конструкции, и поэтому они это -ahum- "не всеобщая чашка чая"...возможно, вы захотите преобразовать их в подфункции, чтобы предотвратить долгие и бесполезные обсуждения с вашими коллегами:)

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


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

скажем, вы найдете s s.т. f (s) является максимальным с учетом ограничений s, вам все равно нужно выяснить, как построить s с двенадцатью 4-элементными векторами (переопределенная система, если она когда-либо была), где каждый вектор имеет 20 возможных значений. Разреженность может помочь, хотя я не уверен, как можно иметь вектор с четырьмя элементы 70-90% ноль, и разреженность была бы полезна только в том случае, если бы еще не была описана методология в том, как организован вектор

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


Я знаю, этот ответ до вас действительно late.

к сожалению, проблема, как есть, показывает не так много шаблонов для использования, кроме грубой силы-ветви и границы,Master & Slave и т. д.- Попытка ведущего ведомого подхода-т. е. решение сначала непрерывной нелинейной задачи функции в качестве ведущего и решение дискретного выбора в качестве ведомого может помочь, но с таким количеством комбинаций и без дополнительной информации по векторам не слишком много места для работы.

но, основываясь на данных непрерывных почти повсеместно функциях, основанных на комбинациях сумм и операторов умножения и их обратных, разреженность является ясной точкой для использования здесь. Если 70-90% векторов равны нулю, почти большая часть пространства решений будет близка к нулю или близка к infinite. Следовательно, псевдо-решение 80-20 легко отбрасывает "нулевые" комбинации и использует только "бесконечные".

таким образом, грубая сила может быть управляемый.