Is there an easy way of making a 'random' sparse matrix with a specific number of nonzero entries?
Here is my attempt:
r = randperm(n,m) % n = size of matrix, m = number of nonzeros in each column
H = sparse(r, r,1,n,n);
But the matrix H doesn't have exactly m nonzeros in each column. For example if I use this to make a 100 x 100 matrix with 10 nonzeros in each column only 10 columns have exactly 10 1's in them.
I'm sure there's an easy way to do this but I can't see it.
This will generate a 100-by-100 matrix with exactly ten 1s per column:
n = 100;
m = 10;
nonzerosPerColumn = repmat(m, 1, n);
%%// Build vector of linear indices to nonzero entries
pos = cell2mat(arrayfun(#(i)randperm(n,nonzerosPerColumn(i))+(i-1)*n,1:n,'uni',0));
%%// Generate the matrix
M = reshape(sparse(pos,1,1,n*n,1),n,n);
Here's a vectorized approach:
r = 100; %// number of rows
c = 100; %// number of columns
m = 10; %// number of ones that there should be in each column
H = sparse([], [], [], r, c, c*m); %// preallocate and initiallize to zero
[~, ind] = sort(rand(r,c)); %// randomly generate...
ind = ind(1:m,:); %// ... m row indices per column
H(bsxfun(#plus, ind, (0:c-1)*r)) = 1; %// fill in ones, using linear indexing
Related
I want to generalise to any n the Matlab code below.
Let A be an n-dimensional array:
clear
rng default
n=4;
A=randn(n,n,n,n);
n=5;
A=randn(n,n,n,n,n);
Note that A is composed of n^(n-2) 2-dimensional matrices, each of size nxn.
For example, when n=4 these matrices are A(:,:,1,1),...,A(:,:,4,1),A(:,:,1,2),...,A(:,:,4,4).
Suppose I'm interested in a code which:
1) deletes the last column and row in each of the n^(n-2) 2-dimensional matrices
%when n=4
A(n,:,:,:)=[];
A(:,n,:,:)=[];
%when n=5
A(n,:,:,:,:)=[];
A(:,n,:,:,:)=[];
2) deletes the 2-dimensional matrices with the 3-th,4-th,5-th,n-th index equal to n.
%when n=4
A(:,:,n,:)=[];
A(:,:,:,n)=[];
%when n=5
A(:,:,n,:,:)=[];
A(:,:,:,n,:)=[];
A(:,:,:,:,n)=[];
Question: could you help me to generalise the code above to any n? I cannot see how to proceed.
You can index your matrix with a cell containing multiple elements. Each element will be interpreted as a new index (more information here):
%Example 1: A(:,:,1:3,1:3,1:3}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(:,:,1:3,1:3,1:3}, so we create c = {1:3,1:3,1:3}
c = repmat({1:n-1},d-2,1);
%Get the new matrix
A = A(:,:,c{:});
%Example 2: A(1:3,1:3,:,:,:}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(1:3,1:3,:,:,:}, so we create c1 = {1:3,1:3} and c2 = {':',':',':'}
c1 = repmat({1:n-1},2,1);
c2 = repmat({':'},d-2,1); %thanks to #LuisMendo for the suggestion.
%Get the new matrix
A = A(c1{:},c2{:});
Let's say I have a 3-dimensional matrix and have computed the max along the second dimension, and want to get the linear indices of the max values. However, the max-function only returns the subscripts along one dimension.
A = randn([5,5,5]); % Generate random matrix
[M, Ind] = max(A,[],2); % Take the max along dimension 2
How do I transfer the index to linear indexing, such that
M == A(Ind)
becomes true?
My intention for this problem is that I have two multi-dimensional matrices and need to compute the max in the first one. Then, I want to access the values in the second matrix at exactly those positions where I found a max in the first one.
One way is to use sub2ind:
A = randn([5,5,5]);
[M, col] = max(A,[],2);
[m,n,o] = size(A);
dim1 = mod((0:m*o-1)', m)+1;
dim2 = col(:);
dim3 = ceil((1:m*o)/m)';
ind = sub2ind(size(A), dim1, dim2, dim3)
verify it works with
isequal(M(:), A(ind))
to get them to have the same shape as M:
reshape(ind, m, 1, o)
Create the indices for the other dimensions.
In dim 1 the index needs to change fastest: [1,2,...,size(A,1)] and this size(A,3) times:
idx1 = repmat((1:size(A,1))',size(A,3),1);
In dim 2 the index is given by Ind.
In dim 3 the index need to change slowest: [1,1,...,1] for size(A,1) times and then [2,2,...,2] and so on until size(A,3).
idx3 = ones(size(A,1),1)*(1:size(A,3));
Access single values:
M_ = A(sub2ind(size(A),idx1(:),Ind(:),idx3(:)));
Compare:
M(:) == M_
3-dimensional case:
[m, n, p] = size(A);
[M, Ind] = max(A,[],2);
LinInd = bsxfun(#plus, (1:m).', (0:p-1)*m*n); %'//
LinInd = LinInd(:) + (Ind(:)-1)*m;
The desired linear index is LinInd. This produces
A(LinInd) == M(:)
with all true entries (note you need (:) on the right-hand side so that the comparison makes sense).
General multi-dimensonal case:
d = 3; %// dimension along which max will be computed
s = size(A);
sLow = prod(s(1:d-1));
sHigh = prod(s(d+1:end));
[M, Ind] = max(A,[],d);
LinInd = bsxfun(#plus, (1:sLow).', (0:sHigh-1)*sLow*s(d)); %'//
LinInd = LinInd(:) + (Ind(:)-1)*sLow;
Let's suppose A and B are the two matrices you have and you need to get max indices from A and use those indices to index into B for the desired output. One approach to achieve the same could be like this -
%// Your code to get Ind
A = randn([5,5,5]); % Generate random matrix
[M, Ind] = max(A,[],2); % Take the max along dimension 2
%// ------- Solution code -------------
%// Get the size of A
[n1,n2,n3] = size(A)
%// Linear indices corresponding to column and third dimension indices
col_dim3_lin_idx = bsxfun(#plus,(Ind-1)*n1,permute([0:n3-1]*n1*n2,[1 3 2]))
%// Finally get the overall linear indices
linear_index = bsxfun(#plus,col_dim3_lin_idx,[1:n1]') %//'
%// Get the corresponding elements from B
out = B(linear_index)
Slightly different way to have the desired linear indices as a 2D array would be like this -
[n1,n2,n3] = size(A) %// Get the size of A
idx = bsxfun(#plus,bsxfun(#plus,squeeze((Ind-1)*n1),[0:n3-1]*n1*n2),[1:n1]')
idx(:) would be the column vector of linear indices with this new approach, which you can index into B i.e. B(idx(:)) to have the desired output as a column vector.
How in Matlab we can form a matrix X, 1000 by 1000, which is sparse with, say,
5% of independent Bernoulli +-1 nonzero entries?
I.e. such a matrix would have rho = ||X||_0/10^6 = 0.05.
Randomly choose 5% of elements
n = numel(X);
ind = randi(n, round(.05*n), 1);
Assign these elements with random variable
X(ind) = binornd(1, .5, length(ind), 1) *2-1;
Check binornd's documentation for more details.
To avoid duplicate randi numbers, you can use randsample from the Statistics Toolbox, or something like randperm as mentioned in this post, or something like
EDIT
ind = [];
t0 = round(.05*n);
t1 = length(ind);
while t1 < t0
ind(end+1:t0) = randi(n, t0-t1, 1);
ind = unique(ind);
t1 = length(ind);
end
If you need to build the matrix as sparse (in Matlab's sense):
M = 1000; %// number of rows
N = 1000; %// number of columns
perc = 5/100; %// percentage (fraction) of +/-1 entries
n = round(M*N*perc); %// compute number of nonzero entries
nz = 2*(rand(1,n)<.5)-1; %// generate nonzero entries: +/-1 with .5 probability
ind = randsample(M*N,n); %// choose linear indices of nonzero entries
X = sparse(ind, 1 ,nz , M*N, 1, n); %// build matrix as linearized
X = reshape(X,M,N); %// put into shape
I'm new to Matlab and I'm trying to solve a problem that involves creating a d dimensional multiplication table where each edge goes from 1 to n. The problem statement says that inputting d = 0 should return the number 1 and d = 1 should return a column vector with the elements 1 to n.
Ideally, I would just create a matrix of 1 to n along d dimensions and then iterate through for each element setting it equal to the product of the indices, but I don't know how to create the d dimensional matrix.
Can anyone help me with this problem?
You can create the table with repeated use of bsxfun. At each iteration, the vector 1,2,...,n is shifted to a new dimension and multiplied (with singleton expansion) by the previous result.
%// Data
d = 3;
n = 10;
%// Computations
vector = (1:n).'; %// first dimension: column vector
result = 1; %// initialization
for n = 1:d
result = bsxfun(#times, result, vector); %// new dimension
vector = shiftdim(vector,-1); %// shift to the next dimension
end
I need to create a list (of size n) of random, non-repeating set of coordinates on a matrix of predefined size.
Is there a fast way to generate this in Matlab?
My initial idea was to create a list of size n with permutations the size of (width x length) and to translate them back to Row and Col values, but it seems to me too much.
Thanks,
Guy
You can use randperm to generate a linear index, and convert it to [row,col] if needed using ind2sub.
x = rand(7,9);
n = 20;
ndx = randperm(numel(x), n);
[row,col] = ind2sub(size(x), ndx);
As long as n is less than the number of elements in the matrix this is simple:
% A is the matrix to be sampled
% N is the number of coordinate pairs you want
numInMat = numel(A);
% sample from 1:N without replacement
ind = randperm(numInMat, N);
% convert ind to Row,Col pairs
[r, c] = ind2sub( size(A), ind )
Your idea is a good one, although you don't even have to convert your linear indices back to row and col indices, you can do linear indexing directly into a 2D array.
idx = randperm(prod(size(data)))
where data is your matrix. This will generate a vector of random integers between 1 and prod(size(data)), i.e. one index for each element.
e.g.
n = 3;
data = magic(n);
idx = randperm(prod(size(data)));
reshape(data(idx), size(data)) %this gives you your randomly indexed data matrix back