MATLAB:查找矩阵的缩写版本,最小化矩阵元素的总和

时间:2015-11-16 15:26:54

标签: matlab optimization quadratic-programming

我有 151 -by- 151 矩阵A。它是一个相关矩阵,因此主对角线上有1 s,主对角线上方和下方有重复值。每行/每列代表一个人。

对于给定的整数n,我将寻求通过踢人来减小矩阵的大小,这样我留下了n-by-n相关矩阵,可以最小化元素的总和。除了获得缩写矩阵之外,我还需要知道应该从原始矩阵中引导的人的行号(或者他们的列号 - 它们将是相同的数字)。

作为一个起点,我采用A = tril(A),这将从相关矩阵中删除冗余的非对角线元素。

Correlation matrix

所以,如果n = 4并且我们上面有假设的 5 -by- 5 矩阵,那么很明显第5个人应该被踢出矩阵,因为那个人贡献了很多非常高的相关性。

同样清楚的是,第1人不应该被踢出,因为那个人会产生很多负相关,从而降低了矩阵元素的总和。

我理解sum(A(:))会对矩阵中的所有内容求和。但是,我很清楚如何搜索最小可能的答案。

我注意到一个类似的问题Finding sub-matrix with minimum elementwise sum,它有一个强力解决方案作为公认的答案。虽然这个答案很好,但对于 151 -by- 151 矩阵来说这是不切实际的。

编辑:我曾想过迭代,但我认为这并不能真正减少简化矩阵中元素的总和。下面我有一个粗体的 4 -by- 4 相关矩阵,边上有行和列的总和。很明显,对于n = 2,最优矩阵是涉及人1和4的 2 -by - 2 单位矩阵,但根据迭代方案,我会有在迭代的第一阶段踢出了人1,因此算法提出了一个不是最优的解决方案。我写了一个总是生成最优解的程序,当n或k很小时,它可以很好地工作,但是当试图从一个最佳的 75 -by- 75 矩阵时 151 -by- 151 矩阵我意识到我的程序需要数十亿年才能终止。

我含糊地回忆说,有时这些 n选择k 问题可以通过动态编程方法来解决,避免重新计算的东西,但我无法弄清楚如何解决这个问题,也没有google搜索启发我

如果没有其他选择,我愿意牺牲速度精度,或者最好的程序需要一周以上才能生成精确的解决方案。但是,如果能够生成一个精确的解决方案,我很高兴让程序运行长达一周。

如果程序无法在合理的时间范围内优化矩阵,那么我会接受一个解释为什么 n选择k 这个特定种类的任务无法在合理范围内解决的答案时限。

4x4 correlation matrix

4 个答案:

答案 0 :(得分:3)

有几种方法可以找到近似解(例如,对松弛问题或贪婪搜索的二次规划),但是finding the exact solution is an NP-hard problem

免责声明:我不是二元二次规划专家,您可能需要查阅学术文献以获得更复杂的算法。

数学上等效的配方:

您的问题相当于:

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&#39; quadprog)。您可以尝试删除条目,在轻松的问题上,quadprog会在x向量中指定最低值,但确实可以解决原始问题!

另请注意,放松的问题会为您提供原始问题最佳值的下限。如果您对松弛问题的解决方案的离散化版本导致目标函数的值接近下限,那么这种特殊解决方案可能与真正的解决方案相差甚远

要解决轻松的问题,您可以尝试以下方法:

% 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是解决原始问题的下限。

解决您确切问题的软件

你应该check out this link并进入混合整数二次规划(MIQP)部分。在我看来,Gurobi可以解决您的类型问题。另一个list of solvers is here

答案 1 :(得分: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

是生成耦合两个父母的新个体的函数。

此时您可以定义您的问题:

gaproblem2.fitnessfcn=@(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)
gaproblem2.options.MutationFcn=@(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)是仅包含所需人员的矩阵。

答案 2 :(得分:3)

如果我理解了你的问题陈述,你有一个 N x N 矩阵 M (恰好是一个相关矩阵),你希望找到整数 n ,其中2&lt; = n &lt; N n x n 矩阵 m ,可最大限度地减少 m 所有元素的总和strong>我表示 f (m)

在Matlab中,获得矩阵的子矩阵相当容易和快速(参见例如Removing rows and columns from matrix in 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)   
    }
}

最后你最终会得到一个维数n的m,这是你的问题的解决方案和一个向量JJ,它包含在每次迭代时删除的索引(你应该很容易将它们转换回适用于完整的索引)矩阵M)

答案 3 :(得分:1)

根据Matthew Gunn的建议以及Gurobi论坛的一些建议,我想出了以下功能。它似乎工作得很好。

我会给它答案,但是如果有人能够提出效果更好的代码,我会删除这个答案中的勾号并将其放在他们的答案上。

### Start My Block
 -A INPUT -s 4.4.4.4 -j ACCEPT
 -A INPUT -s 4.4.2.2 -j ACCEPT
### End My Block