2

I have 3 matrices x, y and z of order 3*3. I want to create a new matrix k with value = 1./(x.^2+y.^2+z.^2) if (x.^2+y.^2+z.^2 > 1) and value = 0 otherwise. I am trying to use this :

  k(x.^2+y.^2+z.^2>1)= 1./(x.^2+y.^2+z.^2)

but it gives error : In an assignment A(I) = B, the number of elements in B and I must be the same. Can anyone provide a simple solution in a single line where I don't need to use for loops

Bosnia
  • 333
  • 2
  • 3
  • 10
  • If (x.^2+y.^2+z.^2 > 1) for any of x's, y'x and z's or all x's, y's and z's? If the condition fails, should all 9 values in `z` must become 0? – Divakar Mar 16 '14 at 17:05
  • we don't have to change z at all. we have to elementwise check the value of x.^2+y.^2+z.^2 – Bosnia Mar 16 '14 at 17:09
  • In the condition part, you are comparing 9 values with 1. So my question was how do you want to compare? Should `all` 9 values be greater than 1 or `any` of the 9 values be greater than 1? – Divakar Mar 16 '14 at 17:11

3 Answers3

2

I am not sure why you'd want to do this as opposed to splitting it up into two operations. This way, you save the cost of computing the sum of squares twice.

x = rand(3,3);
y = rand(3,3);
z = rand(3,3);
k = 1./(x.^2+y.^2+z.^2);
k(k>1)=0;

In any case, another way to do it would be using principles of Functional Programming:

x = rand(3,3);
y = rand(3,3);
z = rand(3,3);
myfun = @(x,y,z) 1/(x^2+y^2+z^2) * (x^2+y^2+z^2>1);
k = arrayfun(myfun, x, y, z);

Alternately, you can mix everything into one line as:

k = arrayfun(@(x,y,z) 1/(x^2+y^2+z^2) * (x^2+y^2+z^2>1), x, y, z);

What this code does is maps the function myfun to each of the data elements. The function myfun is quite simple. It computes the required quantity but multiplies it with the binding condition. However, you might want to beware.


EDIT: To address the comment. If you don't want to compute the quantity at all, we can use conditional anonymous functions. For more details, you can refer to this guide.

iif = @(varargin) varargin{2 * find([varargin{1:2:end}], 1, 'first')}();
myfun = @(x,y,z) iif( x^2+y^2+z^2 <= 1, @() 0, x^2+y^2+z^2>1 ,@() 1/(x^2+y^2+z^2));
k = arrayfun(myfun, x, y, z);
Community
  • 1
  • 1
Nitish
  • 6,358
  • 1
  • 15
  • 15
  • actually I just wanted that matlab should not compute the value of 1./(x.^2+y.^2+z.^2) for those elements where zero value is to be assigned. Can your suitably be modified to adjust this? – Bosnia Mar 16 '14 at 18:00
  • @Bosnia, I have edited the answer. Using the new code snippet, you will not compute the quantity if 0 is to be assigned. – Nitish Mar 16 '14 at 18:12
0

How about

k = x.^2+y.^2+z.^2;
k(k < 1) = 0;
k(k~= 0) = 1 ./ k(k~=0);
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
-1

If you are trying to save some processing time (i.e. do not compute at all the sum of squares for those cases when it is less than one) then pretty much the only solution is a table lookup Otherwise the following code should work

k=1./(x.^2+y.^2+z.^2)
k(k<=1)=0

you can cut some time (assuming x, y and z could be greater than 1)

idx0=x<1 & y<1 & z<1
k=zeros(3)
k(idx0)=1./(x(idx0).^2+y(idx0).^2+z(idx0)^2)
k(k<=1)=0

Your original solution will work if you change it to use an indexer (I haven't profiled it, but I am pretty sure it will take longer, than mine :) )

idx0=x.^2+y.^2+z.^2>1
k=zeros(3)
k(idx0)=1./(x(idx0).^2+y(idx0).^2+z(idx0)^2)
Alex Lizz
  • 425
  • 1
  • 8
  • 19
  • I am trying to save on computation of 1./(x.^2+y.^2+z.^2) for those elements where a zero is to be assigned. (It is actually a complicated function of 1./(x.^2+y.^2+z.^2) so it'll take long computation time) – Bosnia Mar 16 '14 at 18:05
  • In your original example, than can be modified to work, the sum of squares will be computed more than once for each element (one in a conditional statement and one for those cases that fit the conditional). Mine saves at least the second time. Anyway, what is the type and the range of values for x,y,z and what is the precision of the result do you need? – Alex Lizz Mar 16 '14 at 18:08
  • type double x,y,z are from 0-5.038 – Bosnia Mar 16 '14 at 18:12