Azzera filtri
Azzera filtri

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

1 visualizzazione (ultimi 30 giorni)
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.

Risposta accettata

Guillaume
Guillaume il 20 Dic 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 Commenti
Guillaume
Guillaume il 20 Dic 2017
Modificato: Guillaume il 20 Dic 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 il 21 Dic 2017
Modificato: Guillaume il 21 Dic 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.

Accedi per commentare.

Più risposte (5)

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

Accedi per commentare.


Image Analyst
Image Analyst il 20 Dic 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 il 21 Dic 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 Commenti
Image Analyst
Image Analyst il 21 Dic 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.

Accedi per commentare.


Jos (10584)
Jos (10584) il 21 Dic 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) il 21 Dic 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

Categorie

Scopri di più su Loops and Conditional Statements in Help Center e File Exchange

Tag

Community Treasure Hunt

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

Start Hunting!

Translated by