Reputation: 367
I have a vector of indices and want to increase values in matrix in every index. For example:
ind = [1 2 2 5];
m = zeros(3);
m(ind) = m(ind) + 1;
The result is as follow:
m = [1 0 0
1 1 0
0 0 0]
But I need the results to be
m = [1 0 0
2 1 0
0 0 0]
The time complexity is very important to me, and I can't use for. Thanks.
Upvotes: 4
Views: 626
Reputation: 221504
For a sorted array of indices, we can have a play of diff
-
out = zeros(M,N); % Output array of size(M,N)
df = diff([0,ind,ind(end)+1]);
put_idx = diff(find(df)); % gets count of dups
out(ind(df(1:end-1)~=0)) = put_idx;
The basic idea being we count the duplicates along the length with diff
. Those counts are the values to be assigned into the zeros array. The indices at which those values are to be assigned are simply the unique indices, which could be found out by looking for the start of each group of duplicate indices.
Script to create sorted indices array (create_data.m
) -
function ind = create_data(M,N, num_unq_ind, max_repeats)
unq_ind = unique(randi([1,M*N],1,num_unq_ind));
num_repeats = randi(max_repeats, [1,numel(unq_ind)]);
ind = repelem(unq_ind, num_repeats);
Benchmarking script (bench1.m
) to test out various scenarios -
clear all; close all;
M = 5000; % Array size
N = 5000;
% Input params and setup input indices array (edited for various runs)
num_unq_ind = 100000;
max_repeats = 100;
ind = create_data(M,N, num_unq_ind, max_repeats);
num_iter = 100; % No. of iterations to have reliable benchmarking
disp('Input params :')
disp(['num_unq_ind = ' int2str(num_unq_ind)])
disp(['max_repeats = ' int2str(max_repeats)])
disp('------------------ Using diff ----------------')
tic
for i=1:num_iter
out = zeros(M,N);
df = diff([0,ind,ind(end)+1]);
put_idx = diff(find(df));
out(ind(df(1:end-1)~=0)) = put_idx;
end
toc
% Luis's soln
disp('------------------ Using accumaray ----------------')
tic
for i=1:num_iter
m = reshape(accumarray(ind(:), 1, [N^2 1]), N, N);
end
toc
Various scenario runs -
>> bench1
Input params :
num_unq_ind = 10000
max_repeats = 10
------------------ Using diff ----------------
Elapsed time is 0.948544 seconds.
------------------ Using accumaray ----------------
Elapsed time is 1.502658 seconds.
>> bench1
Input params :
num_unq_ind = 100000
max_repeats = 10
------------------ Using diff ----------------
Elapsed time is 1.784576 seconds.
------------------ Using accumaray ----------------
Elapsed time is 1.533280 seconds.
>> bench1
Input params :
num_unq_ind = 10000
max_repeats = 100
------------------ Using diff ----------------
Elapsed time is 1.315998 seconds.
------------------ Using accumaray ----------------
Elapsed time is 1.391323 seconds.
>> bench1
Input params :
num_unq_ind = 100000
max_repeats = 100
------------------ Using diff ----------------
Elapsed time is 6.180565 seconds.
------------------ Using accumaray ----------------
Elapsed time is 3.576154 seconds.
With less sparsey and more repeats, accumarray
seems to be doing better.
Upvotes: 4
Reputation: 15837
You can use histcounts
n = 3;
m = reshape(histcounts(ind, [1:n^2 n^2]), n, n);
Upvotes: 3
Reputation: 112659
Here's a way. I haven't timed it.
ind = [1 2 2 5];
N = 3;
m = full(reshape(sparse(ind, 1, 1, N^2, 1), N, N));
Equivalently, you can use
ind = [1 2 2 5];
N = 3;
m = reshape(accumarray(ind(:), 1, [N^2 1]), N, N);
or its variation (thanks to @beaker)
ind = [1 2 2 5];
N = 3;
m = zeros(N);
m(:) = accumarray(ind(:), 1, [N^2 1]);
This one is probably slower than the others:
ind = [1 2 2 5];
N = 3;
m = zeros(N);
[ii, ~, vv] = find(accumarray(ind(:), 1));
m(ii) = vv;
Upvotes: 6