Подтвердить что ты не робот

MATLAB: найдите сокращенную версию матрицы, которая минимизирует сумму матричных элементов

У меня есть 151-на-151 матрица A. Это корреляционная матрица, поэтому на главной диагонали есть 1 и повторяющиеся значения выше и ниже главной диагонали. Каждая строка/столбец представляет человека.

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

В качестве отправной точки возьмите A = tril(A), который удалит избыточные недиагональные элементы из корреляционной матрицы.

Корреляционная матрица

Итак, если n = 4 и мы имеем гипотетическую матрицу 5 на 5, то очень ясно, что человека 5 следует выталкивать из матрицы, поскольку этот человек вносит очень много корреляций.

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

Я понимаю, что sum(A(:)) будет суммировать все в матрице. Однако я не совсем понимаю, как искать минимально возможный ответ.

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

EDIT: Я думал об итерации, но я не думаю, что это действительно минимизирует сумму элементов в приведенной матрице. Ниже у меня есть корреляционная матрица 4 на 4, выделенная жирным шрифтом, с суммами строк и столбцов на краях. Очевидно, что при n = 2 оптимальной матрицей является матрица идентичности 2 на 2, в которую входят Лица 1 и 4, но в соответствии с итеративной схемой я бы выгнал Person 1 на первой фазе итерации, и поэтому алгоритм делает решение, которое не является оптимальным. Я написал программу, которая всегда создавала оптимальные решения, и она хорошо работает, когда n или k малы, но, пытаясь создать оптимальную матрицу размером 75 на 75 из матрицы 151 на 151, я понял, что моя программа займет миллиарды лет для прекращения.

Я смутно вспоминал, что иногда эти n выбирают k проблем, которые могут быть решены с помощью динамических подходов к программированию, которые не позволяют перекомпоновку вещей, но я не могу решить, как это решить, и нигде не просвещает меня.

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

Если бы программа не могла оптимизировать матрицу в разумные сроки, я бы принял ответ, объясняющий, почему n выбирает k задач этого конкретного вида, не может быть разрешено в разумные сроки.

корреляционная матрица 4x4

4b9b3361

Ответ 1

Работая над предложением Мэтью Ганна, а также некоторыми советами на форумах Gurobi, я придумал следующую функцию. Кажется, это работает очень хорошо.

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

function [ values ] = the_optimal_method( CM , num_to_keep)
%the_iterative_method Takes correlation matrix CM and number to keep, returns list of people who should be kicked out 

N = size(CM,1);  

clear model;  
names = strseq('x',[1:N]);  
model.varnames = names;  
model.Q = sparse(CM); % Gurobi needs a sparse matrix as input  
model.A = sparse(ones(1,N));  
model.obj = zeros(1,N);  
model.rhs = num_to_keep;  
model.sense = '=';  
model.vtype = 'B';

gurobi_write(model, 'qp.mps');

results = gurobi(model);

values = results.x;

end

Ответ 2

Существует несколько подходов к поиску приближенного решения (например, квадратичное программирование по расслабленной проблеме или жадному поиску), но найти точное решение - это NP-hard проблема.

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

Математически эквивалентная формулировка:

Ваша проблема эквивалентна:

For some symmetric, positive semi-definite matrix S

minimize (over vector x)  x'*S*x

             subject to     0 <= x(i) <= 1        for all i
                            sum(x)==n
                            x(i) is either 1 or 0 for all i

Это проблема квадратичного программирования, где вектор x ограничивается только двоичными значениями. Квадратичное программирование, где область ограничено набором дискретных значений, называется смешанным целочисленным квадратичным программированием (MIQP). Бинарная версия иногда называется двоичным квадратичным программированием (BQP). Последнее ограничение, что x является двоичным, затрудняет задачу; он разрушает проблему выпуклости!

Быстрый и грязный подход к поиску приблизительного ответа:

Если вам не нужно точное решение, что-то, с чем можно поиграть, может быть расслабленной версией проблемы: отбросить двоичное ограничение. Если вы отбросите ограничение x(i) is either 1 or 0 for all i, то проблема станет тривиальной проблемой выпуклой оптимизации и может быть решена почти мгновенно (например, Matlab quadprog). Вы можете попытаться удалить записи, которые по расслабленной проблеме quadprog присваивают наименьшие значения в векторе x, но это действительно не решает исходную проблему!

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


Чтобы решить расслабленную проблему, вы можете попробовать что-то вроде:

% k is number of observations to drop
n = size(S, 1);
Aeq = ones(1,n)
beq = n-k;
[x_relax, f_relax] = quadprog(S, zeros(n, 1), [], [], Aeq, beq, zeros(n, 1), ones(n, 1));
f_relax = f_relax * 2;  % Quadprog solves .5 * x' * S * x... so mult by 2
temp = sort(x_relax);
cutoff = temp(k);
x_approx = ones(n, 1);
x_approx(x_relax <= cutoff) = 0;
f_approx = x_approx' * S * x_approx;

Мне интересно, насколько хорош x_approx? Это не решает вашу проблему, но это может быть не ужасно! Заметим, что f_relax является нижней границей решения исходной задачи.


Программное обеспечение для решения вашей конкретной проблемы

Вы должны проверить эту ссылку и перейти к разделу "Смешанное целочисленное квадратичное программирование" (MIQP). Мне кажется, что Гуроби может решить проблемы вашего типа. Другой список решателей здесь.

Ответ 3

Это приблизительное решение с использованием генетического алгоритма.

Я начал со своего тестового примера:

data_points = 10; % How many data points will be generated for each person, in order to create the correlation matrix.
num_people = 25; % Number of people initially.
to_keep = 13; % Number of people to be kept in the correlation matrix.
to_drop = num_people - to_keep; % Number of people to drop from the correlation matrix.
num_comparisons = 100; % Number of times to compare the iterative and optimization techniques.
for j = 1:data_points
    rand_dat(j,:) = 1 + 2.*randn(num_people,1); % Generate random data.
end
A = corr(rand_dat);

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

function individuals = user1205901individuals(nvars, FitnessFcn, gaoptions, num_people)

individuals = zeros(num_people,gaoptions.PopulationSize);
for cnt=1:gaoptions.PopulationSize
    individuals(:,cnt)=randperm(num_people);
end

individuals = individuals(1:nvars,:)';

- индивидуальная генерация.

function fitness = user1205901fitness(ind, A)

fitness = sum(sum(A(ind,ind)));

- функция оценки пригодности

function offspring = user1205901mutations(parents, options, nvars, FitnessFcn, state, thisScore, thisPopulation, num_people)

offspring=zeros(length(parents),nvars);
for cnt=1:length(parents)
    original = thisPopulation(parents(cnt),:);
    extraneus = setdiff(1:num_people, original);
    original(fix(rand()*nvars)+1) = extraneus(fix(rand()*(num_people-nvars))+1);
    offspring(cnt,:)=original;
end

- это функция для мутации отдельного

function children = user1205901crossover(parents, options, nvars, FitnessFcn, unused, thisPopulation)

children=zeros(length(parents)/2,nvars);
cnt = 1;
for cnt1=1:2:length(parents)
    cnt2=cnt1+1;
        male = thisPopulation(parents(cnt1),:);
        female = thisPopulation(parents(cnt2),:);
        child = union(male, female);
        child = child(randperm(length(child)));
        child = child(1:nvars);
        children(cnt,:)=child;
        cnt = cnt + 1;

end

- это функция генерации новой индивидуальной связи двух родителей.

На этом этапе вы можете определить свою проблему:

[email protected](idx)user1205901fitness(idx,A)
gaproblem2.nvars = to_keep
gaproblem2.options = gaoptions()
gaproblem2.options.PopulationSize=40
gaproblem2.options.EliteCount=10
gaproblem2.options.CrossoverFraction=0.1
gaproblem2.options.StallGenLimit=inf
gaproblem2.options.CreationFcn= @(nvars,FitnessFcn,gaoptions)user1205901individuals(nvars,FitnessFcn,gaoptions,num_people)
gaproblem2.options.CrossoverFcn= @(parents,options,nvars,FitnessFcn,unused,thisPopulation)user1205901crossover(parents,options,nvars,FitnessFcn,unused,thisPopulation)
[email protected](parents, options, nvars, FitnessFcn, state, thisScore, thisPopulation) user1205901mutations(parents, options, nvars, FitnessFcn, state, thisScore, thisPopulation, num_people)
gaproblem2.options.Vectorized='off'

открыть инструмент генетического алгоритма

gatool

в меню File выберите Import Problem... и выберите gaproblem2 в открывшемся окне.

Теперь запустите инструмент и дождитесь остановки итераций.

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

Результирующий вектор - это список индексов, который вы должны хранить в исходной матрице, поэтому A(garesults.x,garesults.x) - это матрица с нужными лицами.

Ответ 4

Если я понял, что вы задаете проблему, у вас есть матрица N x N M (которая оказывается корреляционной матрицей) и вы хотите найти для integer n, где 2 <= n N, n x n матрица m, которая минимизирует сумму по всем элементам m, который я обозначаю f (m)?

В Matlab довольно легко и быстро получить подматрицу матрицы (см., например, Удаление строк и столбцов из матрицы в Matlab) и функция f относительно недорога для оценки n= 151. Так почему вы не можете реализовать алгоритм, который динамически решает эту задачу в программе, как показано ниже, где я набросал псевдокод:

function reduceM(M, n){

    m = M

    for (ii = N to n+1) {

        for (jj = 1 to ii) {

            val(jj) = f(m) where mhas column and row jj removed, f(X) being summation over all elements of X

        }

        JJ(ii) = jj s.t. val(jj) is smallest

        m = m updated by removing column and row JJ(ii)   
    }
}

В итоге вы получаете m размера n, который является решением вашей проблемы, и вектор JJ, который содержит индексы, удаленные на каждой итерации (вы должны легко преобразовать их обратно в индексы, применимые к полной матрица M)