ehsun
ehsun

Reputation: 157

How can I make this matrix of permutations more efficiently?

I have written this code:

a = repelem(ones(7,8)-2.*eye(7,8), 7:-1:1, 1);
for i=1:7
    a(i,i+1)=-1;
end
for i=8:13
    a(i,i-5)=-1;
end
for i=14:18
    a(i,i-10)=-1;
end
for i=19:22
    a(i,i-14)=-1;
end
for i=23:25
    a(i,i-17)=-1;
end
for i=26:27
    a(i,i-19)=-1;
end
for i=28:28
    a(i,i-20)=-1;
end

To produce this matrix:

-1  -1   1   1   1   1   1   1
-1   1  -1   1   1   1   1   1
-1   1   1  -1   1   1   1   1
-1   1   1   1  -1   1   1   1
-1   1   1   1   1  -1   1   1
-1   1   1   1   1   1  -1   1
-1   1   1   1   1   1   1  -1
 1  -1  -1   1   1   1   1   1
 1  -1   1  -1   1   1   1   1
 1  -1   1   1  -1   1   1   1
 1  -1   1   1   1  -1   1   1
 1  -1   1   1   1   1  -1   1
 1  -1   1   1   1   1   1  -1
 1   1  -1  -1   1   1   1   1
 1   1  -1   1  -1   1   1   1
 1   1  -1   1   1  -1   1   1
 1   1  -1   1   1   1  -1   1
 1   1  -1   1   1   1   1  -1
 1   1   1  -1  -1   1   1   1
 1   1   1  -1   1  -1   1   1
 1   1   1  -1   1   1  -1   1
 1   1   1  -1   1   1   1  -1
 1   1   1   1  -1  -1   1   1
 1   1   1   1  -1   1  -1   1
 1   1   1   1  -1   1   1  -1
 1   1   1   1   1  -1  -1   1
 1   1   1   1   1  -1   1  -1
 1   1   1   1   1   1  -1  -1

I'm looking for a more efficient way to produce this matrix. One way to do so is:

S=[-1 -1 1 1 1 1 1 1];
P=unique(perms(S),'rows');

But I don't want to use permutation at all because I want to use this code and make matrices with bigger dimensions and using permutation makes it impossible.

Upvotes: 2

Views: 67

Answers (2)

Joseph Wood
Joseph Wood

Reputation: 7597

The answer by @gnovice is excellent, however I'd like to add an alternative answer for pedagogical purposes. As gnovice states "You're producing every permutation of positioning 2 values of -1 in a 1-by-8 vector of ones". We can apply this to our problem by thinking about how we can generate successive permutations of [-1 -1 1 1 1 1 1 1].

Coming from C++, this is very intuitive as the algorithm library offers std::next_permutation which generates the next lexicographical permutation of your vector. The algorithm is quite simple and can be found here: https://en.cppreference.com/w/cpp/algorithm/next_permutation. In fact, a much more generalized version of this algorithm has been implemented in matlab by Jos. We will utilize nextperm_local that can be found at the very bottom of the "Functions" tab on the file exchange page for nextperm.

myP = [-1 -1 1 1 1 1 1 1];

function P = nextperm_local(P)
    k1 = find(P(2:end) > P(1:end-1), 1, 'last');
    if isempty(k1)
        k1 = 0;
    else
        k2 = find(P(k1)<P, 1, 'last');
        P([k1 k2]) = P([k2 k1]);
    end
    P((k1+1):end) = P(end:-1:(k1+1));
end

total = nchoosek(8, 2);
output = zeros(total, 8);

for i = 1:total
    output(i,:) = myP ;
    myP = nextperm_local(myP) ;
end

And produces the following matrix:

output =

  -1  -1   1   1   1   1   1   1
  -1   1  -1   1   1   1   1   1
  -1   1   1  -1   1   1   1   1
  -1   1   1   1  -1   1   1   1
  -1   1   1   1   1  -1   1   1
  -1   1   1   1   1   1  -1   1
  -1   1   1   1   1   1   1  -1
   1  -1  -1   1   1   1   1   1
   1  -1   1  -1   1   1   1   1
   1  -1   1   1  -1   1   1   1
   1  -1   1   1   1  -1   1   1
   1  -1   1   1   1   1  -1   1
   1  -1   1   1   1   1   1  -1
   1   1  -1  -1   1   1   1   1
   1   1  -1   1  -1   1   1   1
   1   1  -1   1   1  -1   1   1
   1   1  -1   1   1   1  -1   1
   1   1  -1   1   1   1   1  -1
   1   1   1  -1  -1   1   1   1
   1   1   1  -1   1  -1   1   1
   1   1   1  -1   1   1  -1   1
   1   1   1  -1   1   1   1  -1
   1   1   1   1  -1  -1   1   1
   1   1   1   1  -1   1  -1   1
   1   1   1   1  -1   1   1  -1
   1   1   1   1   1  -1  -1   1
   1   1   1   1   1  -1   1  -1
   1   1   1   1   1   1  -1  -1

Upvotes: 2

gnovice
gnovice

Reputation: 125854

You're producing every permutation of positioning 2 values of -1 in a 1-by-8 vector of ones. Therefore, you can generate the column indices of the -1 values using nchoosek, then modify the matrix a in a single indexing step using sub2ind:

indices = nchoosek(1:8, 2);
N = size(indices, 1);
a = ones(N, 8);
a(sub2ind([N 8], [1:N 1:N].', indices(:))) = -1;

And the output:

a =

    -1    -1     1     1     1     1     1     1
    -1     1    -1     1     1     1     1     1
    -1     1     1    -1     1     1     1     1
    -1     1     1     1    -1     1     1     1
    -1     1     1     1     1    -1     1     1
    -1     1     1     1     1     1    -1     1
    -1     1     1     1     1     1     1    -1
     1    -1    -1     1     1     1     1     1
     1    -1     1    -1     1     1     1     1
     1    -1     1     1    -1     1     1     1
     1    -1     1     1     1    -1     1     1
     1    -1     1     1     1     1    -1     1
     1    -1     1     1     1     1     1    -1
     1     1    -1    -1     1     1     1     1
     1     1    -1     1    -1     1     1     1
     1     1    -1     1     1    -1     1     1
     1     1    -1     1     1     1    -1     1
     1     1    -1     1     1     1     1    -1
     1     1     1    -1    -1     1     1     1
     1     1     1    -1     1    -1     1     1
     1     1     1    -1     1     1    -1     1
     1     1     1    -1     1     1     1    -1
     1     1     1     1    -1    -1     1     1
     1     1     1     1    -1     1    -1     1
     1     1     1     1    -1     1     1    -1
     1     1     1     1     1    -1    -1     1
     1     1     1     1     1    -1     1    -1
     1     1     1     1     1     1    -1    -1

Upvotes: 2

Related Questions