2

I've the following code in C:

for(i=0;i<m;i++)
{
    for(j=0;j<n;j++)
    {
        a[b[i]][c[j]]+=1;
    }
}

Is there a way to write this in Matlab without using for loops? I mean the Matlab way using (:) which is faster.

Something like a(b(:),c(:))=a(b(:),c(:))+1 gives me out of memory error.

Amro
  • 123,847
  • 25
  • 243
  • 454
Malice
  • 1,457
  • 16
  • 32

3 Answers3

3

Interesting. While I don't (yet) have a solution for you (solution at bottom), I have a few notes and pointers:

1. The out of memory error is because you're creating a 512*256 by 512*256 element temporary matrix on the right hand side (a(b(:),c(:))+1). That is 2^34 bytes — 17GB! So that's why you're getting an out of memory error. Note, too, that this array isn't even what you want! Look at this example:

>> a = magic(5);
>> b = [1 5 4]; % The rows that contain the numbers 1,2,3 respectively
>> c = [3 4 5]; % The columns that contain ^ ...

Now, a(1,3) == 1, a(5,4) == 2, etc. But when you say a(b,c), you're selecting rows (1,5,4) and columns (3,4,5) for every one of those rows!

>> a(b,c)
ans =

     1     8    15
    25     2     9
    19    21     3

All you care about is the diagonal. The solution is to use sub2ind to convert your subscript pairs to a linear index.

>> a(sub2ind(size(a),b,c))
ans =

     1     2     3

2. Your proposed solution doesn't do what you want it to, either. Since Matlab lacks an increment operator, you are simply incrementing all indices that exist in (b,c) by one. And no more. It'll take some creative thinking to vectorize this. Use a smaller array to see what's going on:

>> a = zeros(4,4);
>> b = ones(8,4);
>> c = ones(8,4);
>> a(b,c) = a(b,c) + 1;
>> a
a =

     1     0     0     0
     0     0     0     0
     0     0     0     0
     0     0     0     0

Edit Here we go! Vectorized incrementation:

>> idxs = sub2ind(size(a),b(:),c(:)); % convert subscripts to linear indices
>> [unique_idxs,~,ic] = unique(idxs); % Get the unique indices and their locations
>> increment_counts = histc(ic,1:max(ic)); % Get the number of occurrences of each idx
>> a(unique_idxs) = a(unique_idxs) + increment_counts;
mbauman
  • 30,958
  • 4
  • 88
  • 123
  • Oh . My mistake .I Should have tried it out on smaller scale . If i've to explicitly ask matlab to do increment for all indices , i'll have to loop i guess – Malice Aug 13 '13 at 18:05
  • If you want to use mask, you need to use logical mask. Otherwise `b` and `c` are treated as `unique(c(:))`, which results scalar `1`, therefore they update only the first value of `a`. – Serg Aug 13 '13 at 18:08
  • I think Serg mistook my ones() calls as an attempt to create a [logical indexing mask](http://blogs.mathworks.com/steve/2008/01/28/logical-indexing/). I was simply trying to demonstrate that repeated indices didn't get incremented more than once. See my update for a solution that should work without generating out-of-memory errors. – mbauman Aug 13 '13 at 18:30
1

Assuming you have the following matrices:

a = zeros(256);  % or initialized with other values
b = randi(256, [512 256]);
c = randi(256, [512 256]);

Here is an even faster vectorized solution:

a = a + sparse(b,c,1,size(a,1),size(a,2));

Here is another one:

a = a + accumarray([b(:) c(:)], 1, size(a));
Amro
  • 123,847
  • 25
  • 243
  • 454
-1

Answer: Yes.

a(b, c) = a(b, c) + 1;

Example:

>> a = zeros(5);
>> b = [1,3];
>> c = [2,4,5];
>> a(b,c) = a(b,c) + 1;
>> a

a =

     0     1     0     1     1
     0     0     0     0     0
     0     1     0     1     1
     0     0     0     0     0
     0     0     0     0     0
Serg
  • 13,470
  • 8
  • 36
  • 47
  • As Matt pointed out it doesnt increment for each index of a in (b,c),only for once if the index exists . Your example doesnt have a duplicate index – Malice Aug 13 '13 at 18:08