Оптимизации с дискретными параметрами в МатЛАБ
у меня есть 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 легко отбрасывает "нулевые" комбинации и использует только "бесконечные".
таким образом, грубая сила может быть управляемый.