Matlab 使用项约束计算成对项的组合

Matlab 使用项约束计算成对项的组合,matlab,combinations,combinatorics,Matlab,Combinations,Combinatorics,我有一组120对,它们是从一组16项(即c(16,2)=120)中派生出来的 我想确定可以从120对中选择56对的多少个组合,但每个组合必须包含16个项目中每个项目的7个(即,在56对的每个子集中,16个项目中的每个项目都是相等的) 除了确定组合的数量外,我还需要列出它们。有人能帮我用Matlab编写代码吗 这是一个有趣的组合问题,因为nchoosek(120,56)给出了7.4149e+34从120对中选择56对的可能方法。所以你不应该通过循环所有的选择来解决这个问题 为了解决这个问题,我引入

我有一组120对,它们是从一组16项(即c(16,2)=120)中派生出来的

我想确定可以从120对中选择56对的多少个组合,但每个组合必须包含16个项目中每个项目的7个(即,在56对的每个子集中,16个项目中的每个项目都是相等的)


除了确定组合的数量外,我还需要列出它们。有人能帮我用Matlab编写代码吗

这是一个有趣的组合问题,因为
nchoosek(120,56)
给出了
7.4149e+34
从120对中选择56对的可能方法。所以你不应该通过循环所有的选择来解决这个问题

为了解决这个问题,我引入了一个由矩阵
a=(aij)
给出的所选对的表示,其中
aij==1
当且仅当
(I,j)
order
(j,I)
是56个所选对中的一对。该矩阵表示如上所述的56对选择,当且仅当行和为7,即
a*one(16,1)=7*one(16,1)
(对称!)。系统地解决这个问题很容易,可以打印所有可能的解决方案,但这会导致大约
1e20
候选方案

但是根据这个高数字,您可以通过一些愚蠢的代码轻松地随机选择一个示例,如下所示:

%% find some random example
A = zeros(16,16);
b1 = ones(16,1);
b7 = 7*b1;

while norm(A*b1 - b7) ~= 0
    A = zeros(16,16);
    change = true;
    while norm(A*b1 - b7) ~= 0 && change
        change = false;
        todo = find(A*b1<7);
        idx = todo(randperm(length(todo)));
        for k = 1:2:length(idx)-1
            if A(idx(k),idx(k+1)) == 0
                change = true;
            end
            A(idx(k),idx(k+1)) = 1;
            A(idx(k+1),idx(k)) = 1;
        end        
    end    
end

%% print it
pIdx = 0;
for lIdx = 1:16
    for cIdx = 1:lIdx-1
        if A(lIdx,cIdx) == 1
            fprintf(['(' num2str(cIdx) ',' num2str(lIdx) ')  ']);
            pIdx = pIdx + 1;
            if mod(pIdx,7) == 0
                fprintf('\n');
            end            
        end
    end
end
更新:添加代码以扫描所有可能的解决方案…

function main()
    %% provide n-choose-k sequences (compute only once!)
    global ncks maxResult;
    disp('Prepare n choose k sequences...');
    for n = 1:15
        fprintf([num2str(n) '...  ']);
        for k = 1:min(7,n)
            ncks{n}{k} = nchoosek_sequence(n,k);
        end
    end
    fprintf('\n');

    %% start finding solutions
    disp('Scan space of solutions...')
    A = zeros(16,16);
    maxResult = -Inf;
    tic;
    findMats(A,1)
    toc;
end

function findMats(A,curRow)
    global ncks maxResult;
    remain = 7-sum(A+A');
    if curRow == 16
        if remain(16) == 0
            A = A + A';
            if norm(sum(A)-7*ones(1,16)) ~= 0
                error('AHHH');
            end
            %A
            %pause();
            maxResult = max(YOUR_FUNCTION(A),maxResult); % <- add your function here
        end
        return;
    end
    if remain(curRow) == 0
        findMats(A,curRow+1);
    elseif remain(curRow) > 0
        remainingRows = curRow + find(remain(curRow+1:end) > 0);
        if ~isempty(remainingRows) && length(remainingRows) >= remain(curRow)
            for r = 1:nchoosek(length(remainingRows),remain(curRow))
                row = remainingRows(ncks{length(remainingRows)}{remain(curRow)}(r,:));
                Anew = A;
                Anew(curRow,row) = 1;
                findMats(Anew,curRow+1);
            end
        end
    end
end

% very simple and not optimized way to get a sequence of subset-selections
% in increasing order
%
% Note: This function was easy to implement and sufficient for our
% purposes. For more efficient implementations follow e.g.
%  http://stackoverflow.com/questions/127704/algorithm-to-return-all-combin
%  ations-of-k-elements-from-n
function s = nchoosek_sequence(N,k)
    s = zeros(nchoosek(N,k),k);
    idx = 1;
    for n=(2^k-1):(2^N-1)
        if sum(dec2bin(n)=='1') == k
            temp = dec2bin(n);
            s(idx,:) = find([zeros(1,N-length(temp)), temp=='1']);
            idx = idx + 1;
        end
    end    

    % just for convinience ;-)
    s = s(end:-1:1,:);
end
函数main()
%%提供n-choose-k序列(仅计算一次!)
全球ncks maxResult;
disp('Prepare n choose k sequences…');
对于n=1:15
fprintf([num2str(n)“…”);
对于k=1:min(7,n)
ncks{n}{k}=nchosek_序列(n,k);
结束
结束
fprintf('\n');
%%开始寻找解决方案
disp('解决方案的扫描空间…')
A=零(16,16);
maxResult=-Inf;
抽搐;
findMats(A,1)
toc;
结束
函数findMats(A,curRow)
全球ncks maxResult;
剩余=7-和(A+A');
如果curRow==16
如果保持(16)=0
A=A+A';
如果范数(和(A)-7*1(1,16))~=0
错误('AHHH');
结束
%A
%暂停();
maxResult=max(你的函数(A),maxResult);%0
remainingRows=curRow+find(剩余(curRow+1:end)>0);
如果~isempty(remainingRows)&&length(remainingRows)>=剩余(curRow)
对于r=1:nchoosek(长度(剩余行),保留(当前行))
row=remainingRows(ncks{length(remainingRows)}{resident(curRow)}(r,:);
重新=A;
重新(当前行)=1;
findMats(新的,当前+1);
结束
结束
结束
结束
%获得子集选择序列的非常简单且未优化的方法
%依次递增
%
%注意:此功能易于实现,并且足以满足我们的需求
%目的。为了实现更高效的实施,请遵循以下步骤:。
%  http://stackoverflow.com/questions/127704/algorithm-to-return-all-combin
%k元素自n中取A
函数s=nchoosek_序列(N,k)
s=零(N,k,k);
idx=1;
对于n=(2^k-1):(2^n-1)
如果和(dec2bin(n)='1')==k
温度=12英寸(n);
s(idx,:)=find([zeros(1,N-length(temp)),temp=='1']);
idx=idx+1;
结束
结束
%只是为了方便;-)
s=s(结束:-1:1,:);
结束

但这可能需要一段时间…;-)

如果您想让Matlab专家查看它,您需要将其标记为Matlab。我还建议您添加您自己尝试过的内容,否则问题将被视为“给我代码”。查看帮助中心,了解如何发布更好的问题,并有更好的机会获得好的答案;)“不清楚你在问什么”,伙计们,你们是认真的吗?这个问题表述得很清楚,是一个非常有趣的组合问题!谢谢@matheburg,这非常有帮助。我的下一个挑战是简单的求和最大化。每一对实际上对应一个值,我需要找到56对的组合,使这些值的总和最大化。显然,这对于一组可管理的组合来说已经足够容易了,但是对于10e+20组合来说,事情就不那么简单了。我实际上不需要精确定义最大和值,只需要知道它是什么。因此,我正在考虑循环您的代码,并根据这种模拟方法接近最大值?有什么想法吗?谢谢为了获得一种感觉,我将循环代码(如您所说)并保存每个结果。然后,您可以绘制分布图,并了解这是否是一种足够的方法,或者您是否应该检查所有候选人。我不确定存在多少个解决方案,但我能想到的最简单的完整迭代算法必须运行~1e18个候选算法。这是可能的,因此如果需要,我们可以继续讨论;-)顺便说一句:您在每次迭代中评估的函数有多昂贵?我首先需要用一个单一(标量)值替换56对中的每一对,我为120对中的每一对都提供了这个值(我想类似于excel中的“vlookup”)。然后我需要对所有56个值求和,以生成上面提到的“和值”。所以在计算方面,非常简单。我可能还应该提到,我不是一名程序员/数学家,而是一名经济学家,因此这对我来说是一个全新的领域,非常感谢您的帮助(我当然也会在我们的论文中感谢您的帮助……如果您愿意,我可以使用您的笔名或真实姓名)。再次感谢!:)这将是一种荣誉,我会给你一个私人信息。我还添加了代码来扫描解决方案的整个空间。但是:这可能需要很长时间(我目前正在运行它,它已经为我提供了1.3Mio解决方案)。因此,如果需要太长的时间,你应该迭代随机的一个,并观察其分布,以了解最大值
function main()
    %% provide n-choose-k sequences (compute only once!)
    global ncks maxResult;
    disp('Prepare n choose k sequences...');
    for n = 1:15
        fprintf([num2str(n) '...  ']);
        for k = 1:min(7,n)
            ncks{n}{k} = nchoosek_sequence(n,k);
        end
    end
    fprintf('\n');

    %% start finding solutions
    disp('Scan space of solutions...')
    A = zeros(16,16);
    maxResult = -Inf;
    tic;
    findMats(A,1)
    toc;
end

function findMats(A,curRow)
    global ncks maxResult;
    remain = 7-sum(A+A');
    if curRow == 16
        if remain(16) == 0
            A = A + A';
            if norm(sum(A)-7*ones(1,16)) ~= 0
                error('AHHH');
            end
            %A
            %pause();
            maxResult = max(YOUR_FUNCTION(A),maxResult); % <- add your function here
        end
        return;
    end
    if remain(curRow) == 0
        findMats(A,curRow+1);
    elseif remain(curRow) > 0
        remainingRows = curRow + find(remain(curRow+1:end) > 0);
        if ~isempty(remainingRows) && length(remainingRows) >= remain(curRow)
            for r = 1:nchoosek(length(remainingRows),remain(curRow))
                row = remainingRows(ncks{length(remainingRows)}{remain(curRow)}(r,:));
                Anew = A;
                Anew(curRow,row) = 1;
                findMats(Anew,curRow+1);
            end
        end
    end
end

% very simple and not optimized way to get a sequence of subset-selections
% in increasing order
%
% Note: This function was easy to implement and sufficient for our
% purposes. For more efficient implementations follow e.g.
%  http://stackoverflow.com/questions/127704/algorithm-to-return-all-combin
%  ations-of-k-elements-from-n
function s = nchoosek_sequence(N,k)
    s = zeros(nchoosek(N,k),k);
    idx = 1;
    for n=(2^k-1):(2^N-1)
        if sum(dec2bin(n)=='1') == k
            temp = dec2bin(n);
            s(idx,:) = find([zeros(1,N-length(temp)), temp=='1']);
            idx = idx + 1;
        end
    end    

    % just for convinience ;-)
    s = s(end:-1:1,:);
end