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

1 Ansicht (letzte 30 Tage)
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.

Akzeptierte Antwort

Guillaume
Guillaume am 20 Dez. 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 Kommentare
Guillaume
Guillaume am 20 Dez. 2017
Bearbeitet: Guillaume am 20 Dez. 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 am 21 Dez. 2017
Bearbeitet: Guillaume am 21 Dez. 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.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (5)

James Tursa
James Tursa am 20 Dez. 2017
Bearbeitet: James Tursa am 20 Dez. 2017
E.g.,
n = largest number (e.g., 3)
result = cell2mat(arrayfun(@(x)[ones(x,1)*x,(1:x)'],1:n,'uni',false)');
  2 Kommentare
James Tursa
James Tursa am 20 Dez. 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).

Melden Sie sich an, um zu kommentieren.


Image Analyst
Image Analyst am 20 Dez. 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 am 21 Dez. 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 Kommentare
Image Analyst
Image Analyst am 21 Dez. 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.

Melden Sie sich an, um zu kommentieren.


Jos (10584)
Jos (10584) am 21 Dez. 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) am 21 Dez. 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

Kategorien

Mehr zu Loops and Conditional Statements finden Sie in Help Center und File Exchange

Tags

Community Treasure Hunt

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

Start Hunting!

Translated by