Negating every second entree in a matrix column
Afficher commentaires plus anciens
My goal is pretty simple: I have an arbitrary sized matrix holding only positive real numbers and there will not be anymore than two nonzero elements per column at any time, for example:
b = [1 0 1; 0 1.2 2; 1 3 0];
or
b = [1 2 1 0; 0 1.2 0 0; 0 0 0 3; 1.6 0 4 2.2];
Now what I want is to negate every second nonzero entree of a column. The solution I came up with works, but is probably not the most elegant and/or efficient:
for n=1:numel(b(1,:))
isPositive = 0;
for m=1:numel(b(:,n))
if (isPositive == 1 && b(m,n) ~= 0)
b(m,n) = b(m,n)*-1;
break;
end
if (abs(b(m,n)) == b(m,n) && b(m,n) ~= 0)
isPositive = 1;
end
end
end
I am quite new to MATLAB, so if anybody knows a more elegant solution, perhaps not involving any for loops, please share.
Thanks in advance
2 commentaires
Jan
le 13 Déc 2011
The BREAK wil stop the "for m" loop - is this intented?
Instead of "(abs(b(m,n)) == b(m,n) && b(m,n) ~= 0)" you could write "b(m,n) > 0", but if the matrix b has positive values only at first, the test "abs(b(m,n)) == b(m,n)" is useless.
Why do you check if "b(m,n)~=0" before multiplying with -1? In Matlab "-0" is the same as "0".
Michael Dzjaparidze
le 13 Déc 2011
Réponse acceptée
Plus de réponses (2)
Jan
le 13 Déc 2011
This negates every second element per column:
b = [1 2 1 0; 0 1.2 0 0; 0 0 0 3; 1.6 0 4 2.2];
gt0 = (b > 0);
even = mod(cumsum(gt0, 1) - 1, 2);
idx = and(gt0, even);
b(idx) = -b(idx);
[EDITED]: After reading your comment, I understand, that you want to negate one element per column only:
b = [1 2 1 0; 0 1.2 0 0; 0 0 0 3; 1.6 0 4 2.2];
gt0 = (b > 0);
idx = and(gt0, cumsum(gt0, 1) == 2);
b(idx) = -b(idx);
Or with a cleaner loop:
[nx, ny] = size(b);
for iy = 1:ny
isPositive = false;
for ix = 1:nx
if b(ix, iy) > 0
if isPositive
b(ix, iy) = -b(ix, iy);
break;
else
isPositive = true;
end
end
end
end
1 commentaire
Michael Dzjaparidze
le 13 Déc 2011
Daniel Shub
le 13 Déc 2011
While there are probably more efficient, and some would argue elegant, solutions, the real goal should be to do what you think makes the most sense. It is generally a bad idea to spend time trying to speed up sections of code until you know it is a bottle neck.
I like Andrei's solution, but it might be more confusing 6 months later to figure out what it is doing. I think loops often allow for easier documentation. I think a loop like yours with lots of useful comments is a very elegant solution.
My answer:
for column = 1:size(b, 2)
row = find(b(:, column), 1, 'last');
b(row, column) = -b(row, column);
end
4 commentaires
Jan
le 13 Déc 2011
The question is not very clear. But as far I understand, Michael wants teh 2nd positive element in each column to be neagted. Then you need:
for column = 1:size(b, 2)
row = find(b(:, column), 2, 'first');
b(row(2), column) = -b(row(2), column);
end
Daniel Shub
le 13 Déc 2011
@Jan, but according to the question: "there will not be anymore than two nonzero elements per column at any time." As long as there are always exactly 2 non-zero elements the second non-zero element is the last non-zero element. I assumed that there would always be exactly 2 non-zero elements. If there are not, my answer may not give the correct result. The code you put in the comment crashes if there is only one non-zero element, which might be better.
Jan
le 13 Déc 2011
@Daniel: Aaaaarrrgh. "every second" and "not be anymore than 2 nonzero". I still do not get the question completely.
I'm taking a break now and have a cup of coffee. Please fix all problems here. Thanks!
Michael Dzjaparidze
le 13 Déc 2011
Catégories
En savoir plus sur Creating and Concatenating Matrices dans Centre d'aide et File Exchange
Produits
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!