Clear Filters
Clear Filters

creating a matrix where the element of the second column is smaller than the element of the first column

1 view (last 30 days)
I would like to create a matrix where the element of the second column is smaller than the element of the first column. For example,
A = [1 1; 2 1; 2 2; 3 1; 3 2; 3 3]
I would like to know a simpler way rather than going through loops.

Accepted Answer

Guillaume
Guillaume on 20 Dec 2017
In terms of clarity and speed, a loop is probably the best, I'd implement it as:
n = 4; %largest number
result = zeros(n*(n+1)/2, 2);
row = 1;
for i = 1:n
result(row:row+i-1, :) = [repmat(i, i, 1), (1:i)'];
row = row+i;
end
You can implement the above with arrayfun as James has done, but it's likely to be slower.
Another fancy non-loopy way of getting the result:
n = 4;
result = [repelem((1:n)', 1:n), flipud(nonzeros(hankel(n:-1:1)))]
The use of hankel to obtain the 2nd column is fairly obscure so I wouldn't recommend using that.
  2 Comments
Guillaume
Guillaume on 20 Dec 2017
Edited: Guillaume on 20 Dec 2017
For comparison, a quick performance test on my machine (R2017a) of the 3 solutions proposed:
For some reason they all slow down suddenly at around n=350. For smaller n, the hankel version is actually faster, but for larger n the performance degrades quadratically (probably). The arrayfun version is always significantly slower than the loop version.
Guillaume
Guillaume on 21 Dec 2017
Edited: Guillaume on 21 Dec 2017
New comparison that includes all the valid proposed solutions, on a different machine and different version (R2017b):
For low n Roger's answer is the fastest. nchoose2 (without the sort) and my hankel version are on par. Jame's arrayfun solution is consistently slower than the explicit loop and nchoose2 with sorting is a disaster. The explicit loop is never much slower than the fastest solution and for n>500 (on that machine) is the best solution.
I'd say go with a loop as it's by far the clearest as to what it does.

Sign in to comment.

More Answers (5)

James Tursa
James Tursa on 20 Dec 2017
Edited: James Tursa on 20 Dec 2017
E.g.,
n = largest number (e.g., 3)
result = cell2mat(arrayfun(@(x)[ones(x,1)*x,(1:x)'],1:n,'uni',false)');
  2 Comments
James Tursa
James Tursa on 20 Dec 2017
Start with the function:
@(x)[ones(x,1)*x,(1:x)']
For an integer input x, this creates a 2-column matrix. The first column is all the number x, and the second column is the numbers 1 through x. E.g.,
>> f = @(x)[ones(x,1)*x,(1:x)']
f =
@(x)[ones(x,1)*x,(1:x)']
>> f(1)
ans =
1 1
>> f(2)
ans =
2 1
2 2
>> f(3)
ans =
3 1
3 2
3 3
>> f(4)
ans =
4 1
4 2
4 3
4 4
The @(x) anonymous function is fed into the arrayfun( ) function as the first argument.
The second argument to arrayfun( ) is the array 1:n. So, for each number in the second argument, arrayfun( ) will execute the @(x) function with this number as the input. The output of each "iteration" of arrayfun( ) is a matrix and not a scalar. So to gather up all of these outputs into a single cell array result, we also give the last two arguments 'uni' and false.
The cell2mat call simply concatenates all of the cell array results that came from the arrayfun( ) call into a single matrix. The transpose operator ' is used to ensure the concatenation happens vertically (1:n is a row vector so the cells come out as a row vector as well, but we want them stacked vertically).

Sign in to comment.


Image Analyst
Image Analyst on 20 Dec 2017
Here are a couple of ways, one by sorting and one by replacing the second column:
A = [1 1; 2 1; 2 2; 3 1; 3 2; 3 3]
% Sort descending.
ASorted = sort(A, 2, 'descend')
% Replace second row
A2 = A;
A2(:, 2) = A(:, 1) - 1; % Column 2 is one less than col 1

Roger Stafford
Roger Stafford on 21 Dec 2017
Here's another way:
N = 10; % Choose any integer N (largest number)
n = (1:N*(N+1)/2)';
c1 = round(sqrt(2*n-3/4));
c2 = n-(c1-1).*c1/2;
A = [c1,c2];
  3 Comments
Image Analyst
Image Analyst on 21 Dec 2017
Oh, I see now. You and Guillaume were generalizing the matrix alpedhuez gave to larger versions, assuming some algorithm, while I didn't do that and just took the "A" as the given matrix, A = [1 1; 2 1; 2 2; 3 1; 3 2; 3 3], which he needed to create/modify a matrix from that given matrix where the second column was less than the first. I can see there is a possibly ambiguity in interpretation when he gives A and says he needs to create a matrix, that hopefully the poster can clear up.

Sign in to comment.


Jos (10584)
Jos (10584) on 21 Dec 2017
Another approach:
n = 7 ;
M = [n+1-nchoose2(1:n) ; repmat(1:n,2,1).'] % voila!
M_sorted = sortrows(M) % in sorted order, but why bother
NCHOOSE2 is my very fast simple utility function to get combinations of 2 elements from a vector (like nchoosek(V,2), but way more efficient). It can be downloaded from the File Exchange:

Jos (10584)
Jos (10584) on 21 Dec 2017
And here is a one-liner (using only MatLab functions).
n = 5 ;
M = flipud(nchoosek(n:-1:0,2) + [0 1]) % + expansion works in later ML releases
Replace NCHOOSEK by NCHOOSE2 (see my other answer) for an efficient improvement :D

Categories

Find more on Loops and Conditional Statements in Help Center and File Exchange

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!