正如其他人所说,最佳解决方案可能是不可能的,所以我将专注于具体案例。
首先我假设每列的分布是独立的。
然后我在累加器空间上工作以减少数据大小并加快代码速度。
我将每个-1 视为0 并将每一行视为一个数字,并加1以避免使用0作为索引,例如:
data(1,:)=[-1 1 -1 1 -1 1 -1 1 -1 1] -> '0101010101' -> 341 -> 342
这样我们可以将数据累积为:
function accum=mat2accum(data)
[~,n]=size(data);
indexes=bin2dec(num2str((data+1)/2))+1;
accum=accumarray(indexes,1,[2^n 1]);
我考虑的第一种情况是,与数据大小相比,每列的总和很小,这意味着所有列中的 1 和 -1 数量相似。
sum(data) << size(data)
对于这种情况,您可以找到所有相互抵消的对,例如:
data(1,:)=[-1 1 -1 1 -1 1 -1 1 -1 1] -> '0101010101' -> 341 -> 342
data(2,:)=[1 -1 1 -1 1 -1 1 -1 1 -1] -> '1010101010' -> 682 -> 683
而且我们知道每一对都将位于累加器索引中的镜像位置,因此我们可以通过以下方式获得所有可能的对:
function [accumpairs, accumleft]=getpairs(accum)
accumpairs=min([accum,accum(end:-1:1)],[],2);
accumleft=accum-accumpairs;
使用随机生成的数据,我能够在一组 250k 行中获得 >100k 对,并且对的子集在每列中的总和为零。因此,如果 1 和 -1 均等分布,这可能就足够了。
我考虑的第二种情况是每列的总和远不为零,这意味着 1 和 -1 之间存在很大的不成比例。
abs(sum(data)) >> 0
通过反转总和为负的每一列,这不会影响数据,因为最后可以再次反转这些列。可以强制不成比例为 1 多于 -1。并且通过首先提取这些数据的可能对,这种不成比例更加明显。
通过这样准备的数据,可以将问题视为最小化所需集合中 1 的数量。为此,我们首先将可能的索引随机化,然后计算并排序每个索引的汉明权重(二进制表示中 1 的数量),然后收集可能具有最小汉明权重的数据。
function [accumlast,accumleft]=resto(accum,m)
[N,~]=size(accum);
columns=log2(N);
indexes=randperm(N)'; %'
[~,I]=sort(sum((double(dec2bin(indexes-1,columns))-48),2));
accumlast=zeros(N,1);
for k=indexes(I)' %'
accumlast(k)=accum(k);
if sum(accumlast)>=m
break
end
end
accumleft=accum-accumlast;
对于随机生成的数据,其中 1 比 -1 多 2 倍,每列的总和约为 80k,我可以找到 100k 数据的子集,每列总和约为 5k。
第三种情况,是一些列的总和接近于零,而另一些则不是。在这种情况下,您将列分为总和大的列和总和小的列,然后按大总和列的汉明权重对数据进行排序,并在每个大列索引中获取小总和列的对.这将为大和列的每个索引创建一个矩阵,其中包含可能对的数量、不成对的行数以及小列的不成对行的总和。
现在您可以使用该信息来保持运行总和,并查看大总和列的哪些索引要添加到您的子集中,以及是否值得在每种情况下添加比喻或不可配对的数据。
function [accumout,accumleft]=getseparated(accum, bigcol, smallcol, m)
data=accum2mat(accum);
'indexing'
bigindex=bin2dec(num2str((data(:,bigcol)+1)/2))+1;
[~,bn]=size(bigcol);
[~,sn]=size(smallcol);
'Hamming weight'
b_ind=randperm(2^bn)'; %'
[~,I]=sort(sum((double(dec2bin(b_ind-1,bn))-48),2));
temp=zeros(2^bn,4+sn);
w=waitbar(0,'Processing');
for k=1:2^bn;
small_data=data(bigindex==b_ind(I(k)),smallcol);
if small_data
small_accum=mat2accum(small_data);
[small_accumpairs, small_accum]=getpairs(small_accum);
n_pairs=sum(small_accumpairs);
n_non_pairs=sum(small_accum);
sum_non_pairs=sum(accum2mat(small_accum));
else
n_pairs=0;
n_non_pairs=0;
sum_non_pairs=zeros(1,sn);
end
ham_weight=sum((double(dec2bin(b_ind(I(k))-1,bn))-48),2);
temp(k,:)=[b_ind(I(k)) n_pairs n_non_pairs ham_weight sum_non_pairs];
waitbar(k/2^bn);
end
close(w)
pair_ind=1;
nonpair_ind=1;
runningsum=[0 0 0 0 0 0 0 0 0 0];
temp2=zeros(2^bn,2);
while sum(sum(temp2))<=m
if pair_ind<=2^bn
pairsum=[(((double(dec2bin((temp(pair_ind,1)-1),bn))-48)*2)-1)*temp(pair_ind,2) zeros(1,sn)];
end
if nonpair_ind<=2^bn
nonpairsum=[(((double(dec2bin((temp(nonpair_ind,1)-1),bn))-48)*2)-1)*temp(nonpair_ind,3) temp(nonpair_ind,5:5+sn-1)];
end
if nonpair_ind==(2^bn)+1
temp2(pair_ind,1)=temp(pair_ind,2);
runningsum=runningsum+pairsum;
pair_ind=pair_ind+1;
elseif pair_ind==(2^bn)+1
temp2(nonpair_ind,2)=temp(nonpair_ind,3);
runningsum=runningsum+nonpairsum;
nonpair_ind=nonpair_ind+1;
elseif sum(abs(runningsum+pairsum))<=sum(abs(runningsum+nonpairsum))
temp2(pair_ind,1)=temp(pair_ind,2);
runningsum=runningsum+pairsum;
pair_ind=pair_ind+1;
elseif sum(abs(runningsum+pairsum))>sum(abs(runningsum+nonpairsum))
temp2(nonpair_ind,2)=temp(nonpair_ind,3);
runningsum=runningsum+nonpairsum;
nonpair_ind=nonpair_ind+1;
end
end
accumout=zeros(2^(bn+sn),1);
for k=1:2^bn
if temp2(k,:)
small_data=data(bigindex==temp(k,1),smallcol);
if small_data
small_accum=mat2accum(small_data);
[small_accumpairs, small_accum]=getpairs(small_accum);
pairs=accum2mat(small_accumpairs);
non_pairs=accum2mat(small_accum);
else
pairs=zeros(1,sn);
non_pairs=zeros(1,sn);
end
if temp2(k,1)
datatemp=zeros(temp2(k,1),sn+bn);
datatemp(:,bigcol)=((double(dec2bin(ones(temp2(k,1),1)*(temp(k,1)-1),bn))-48)*2)-1;
datatemp(:,smallcol)=pairs;
accumout=accumout+mat2accum(datatemp);
end
if temp2(k,2)
datatemp=zeros(temp2(k,2),sn+bn);
datatemp(:,bigcol)=((double(dec2bin(ones(temp2(k,2),1)*(temp(k,1)-1),bn))-48)*2)-1;
datatemp(:,smallcol)=non_pairs;
accumout=accumout+mat2accum(datatemp);
end
end
end
accumleft=accum-accumout;
使用由第一种情况的 5 列和第二种情况的 5 列组成的数据,可以构造一组 100k 行,其中小和列的总和小于 1k,而大的总和列在 10k 到 30k 之间.
值得注意的是,数据的大小、所需子集的大小以及 1 和 -1 的分布情况都会对算法的性能产生很大影响。