Random unique integer values - matlab

Is there an alternative to randi, I need unique integer values. Using randi the PianoSperimentale matrix may contain repeated integer values.
lover_bound = 10;
upper_bound = 180;
steps = 10;
NumeroCestelli = 8;
livello = [lover_bound:steps:upper_bound];
L = length(livello);
n_c = 500 000
NumeroCestelli = 8
randIdxs = randi([1,L],n_c,NumeroCestelli);
PianoSperimentale = single(livello(randIdxs));
The alternative needs to be fast and support very large matrix. In the past i was using this:
[PianoSperimentale] = combinator(L,NumeroCestelli,'c','r');
for i=1:L
PianoSperimentale(PianoSperimentale==i)=livello(i);
end
but is too slow and painfull. (see Combinator)

Yes, there is:
randsample(10,3)
gives a vector of 3 integers taken from 1 to 10, without replacement.
If you need a matrix instead of a vector:
matrix = NaN(8,12);
matrix(:) = randsample(1000,numel(matrix));
gives an 8x12 matrix of unique integers taken from 1 to 1000.
The function randsample is in the Statistics Toolbox. If you don't have it you can use randperm instead, as noted by #RodyOldenhuis and #Dan (see #Dan's answer).

Also if you don't have the stats toolbox then you could use randperm:
randperm(m, k) + n - 1
This will also give you k random integers between n and n+m without replacement

Related

Repeat random, unique sampling of k values n times

In Matlab, I would like to generate a matrix with 4 random, unique samples (out of 10) 7 times.
In order to avoid a for-loop, I thought I could just repeat my data and use datasample from Statistics and Machine Learning Toolbox on the first dimension. But it always chooses the same 4 values from each column, so this is kind of useless.
Consider the following MWE:
randomData = [50.29; 47.72; 48.38; 48.02; 44.23; 47.17; 48.19; 49.11; 50.44; 53.40];
numOfReps = 7;
numOfSamples = 4;
randomDataRepMatrix = randomData*ones(1, numOfReps);
s = RandStream('mlfg6331_64');
y = datasample(s, randomDataRepMatrix, numOfSamples, 'Replace', false);
Even without the RandStream part, I get the same results...
Any idea? Or do I need to use the for-loop after all?
I don't think datasample or randsample can produce several sets of samples in one go. Here's a "manual" way to do it (not necessarily faster than using datasample with a loop):
[~, ind] = sort(rand(numel(randomData), numOfReps)); % each column is a permutation
ind = ind(1:numOfSamples,:); % keep only the first values in each column
y = randomData(ind); % index into data

Generating all ordered samples with replacement

I would like to generate an array which contains all ordered samples of length k taken from a set of n elements {a_1,...,a_n}, that is all the k-tuples (x_1,...,x_k) where each x_j can be any of the a_i (repetition of elements is allowed), and whose total number is n^k.
Is there a built-in function in Matlab to obtain it?
I have tried to write a code that iteratively uses the datasample function, but I couldn't get what desired so far.
An alternative way to get all the tuples is based on k-base integer representation.
If you take the k-base representation of all integers from 0 to n^k - 1, it gives you all possible set of k indexes, knowing that these indexes start at 0.
Now, implementing this idea is quite straightforward. You can use dec2base if k is lower than 10:
X = A(dec2base(0:(n^k-1), k)-'0'+1));
For k between 10 and 36, you can still use dec2base but you must take care of letters as there is a gap in ordinal codes between '9' and 'A':
X = A(dec2base(0:(n^k-1), k)-'0'+1));
X(X>=17) = X(X>=17)-7;
Above 36, you must use a custom made code for retrieving the representation of the integer, like this one. But IMO you may not need this as 2^36 is quite huge.
What you are looking for is ndgrid: it generates the grid elements in any dimension.
In the case k is fixed at the moment of coding, get all indexes of all elements a this way:
[X_1, ..., X_k] = ndgrid(1:n);
Then build the matrix X from vector A:
X = [A(X_1(:)), ..., A(X_k(:))];
If k is a parameter, my advice would be to look at the code of ndgrid and adapt it in a new function so that the output is a matrix of values instead of storing them in varargout.
What about this solution, I don't know if it's as fast as yours, but do you think is correct?
function Y = ordsampwithrep(X,K)
%ordsampwithrep Ordered samples with replacement
% Generates an array Y containing in its rows all ordered samples with
% replacement of length K with elements of vector X
X = X(:);
nX = length(X);
Y = zeros(nX^K,K);
Y(1,:) = datasample(X,K)';
k = 2;
while k < nX^K +1
temprow = datasample(X,K)';
%checknew = find (temprow == Y(1:k-1,:));
if not(ismember(temprow,Y(1:k-1,:),'rows'))
Y(k,:) = temprow;
k = k+1;
end
end
end

Converting matlab sparse matrix to single precision

I want to convert a sparse matrix in matlab to single precision, however it appears that matlab doesn't have single sparse implemented.
Instead of that, I am just planning on checking it the values are outside of the single precision range and rounding them off to the highest and lowest values of the single precision range.
I'd like to do something like this:
for i = 1:rows
for j = 1:cols
if (abs(A(i,j) < 2^-126))
A(i,j) == 0;
end
end
end
However, ths is extremely slow. Is there another command I can use that will work on sparse matrix class type in MATLAB? I notice most commands don't work for the sparse data type.
EDIT 1:
I also tried the following, but as you can see I run out of memory (the matrix is sparse and is 200K x 200K with ~3 million nonzeros):
A(A < 2^-126) = 0
Error using <
Out of memory. Type HELP MEMORY for your options.
EDIT 2:
Current solution I developed based on input from #rahnema1:
% Convert entries that aren't in single precision
idx = find(A); % find locations of nonzeros
idx2 = find(abs(A(idx)) < double(realmin('single')));
A(idx(idx2)) = sign(A(idx(idx2)))*double(realmin('single'));
idx3 = find(abs(Problem.A(idx)) > double(realmax('single')));
A(idx(idx3)) = sign(A(idx(idx3)))*double(realmax('single'));
You can find indices of non zero elements and use that to change the matrix;
idx = find(A);
Anz = A(idx);
idx = idx(Anz < 2^-126);
A(idx) = 0;
Or more compact:
idx = find(A);
A(idx(A(idx) < 2^-126)) = 0;
However if you want to convert from double to single you can use single function:
idx = find(A);
A(idx) = double(single(full(A(idx))));
or
A(find(A)) = double(single(nonzeros(A)));
To convert Inf to realmax you can write:
A(find(A)) = double(max(-realmax('single'),min(realmax('single'),single(nonzeros(A)))));
If you only want to convert Inf to realmax you can do:
Anz = nonzeros(A);
AInf = isinf(A);
Anz(AInf) = double(realmax('single')) * sign(Anz(AInf));
A(find(A)) = Anz;
As one still reaches this thread when interested in how to convert sparse to single: an easy answer is to use full(). However, this can be problematic if you rely on the memory savings that sparse gives you, as it basically converts to double first.
sparseMatrix = sparse(ones(2, 'double'));
single(full(sparseMatrix)) % works
% ans =
% 2×2 single matrix
% 1 1
% 1 1
single(sparseMatrix) % doesn't work
% Error using single
% Attempt to convert to unimplemented sparse type

Elegant way to create two different random integers

I want to create two random integers on the interval [1,n] which are guaranteed to be different from each other. I feel like
ri(1)=randi([1 n]);
ri(2)=randi([1 n]);
while ri(1)==ri(2)
ri(2)=randi([1 n]);
end
is not really the smoothest thing you can do.
One method is to use randperm so that you generate a random permutation of n values that are enumerated from 1 up to and including n, and only return the first two elements of the result:
ri = randperm(n, 2);
Older versions of MATLAB do not support calling randperm this way. Older versions only accept the one input variant, which by default returns the entire permutation of the n values. Therefore, you can call randperm using the one input version, then subset into the final result to return what you need:
ri = randperm(n);
ri = ri([1 2]);
Use randperm to create two unique values in range 1...n
out = randperm(n, 2)
out(1) = number 1
out(2) = number 2
If you wish to include 0's in your range. then:
out = randperm(n+1, 2);
out = out-1;
out(1) = number 1
out(2) = number 2
Here's another way:
ri(1) = randi([1 n]); % choose ri(1) uniformly from the set 1,...,n
ri(2) = randi([1 n-1]); % choose ri(2) uniformly from 1,...,n-1
ri(2) = ri(2) + (ri(2)>=ri(1)); % transform 1,...,n-1 into 1,...,ri(1)-1,ri(1)+1,...,n

Generate numbers randomly from a set?

In MATLAB, I have a set of P numbers. I would like to generate a random array of size N from this set.
For the sake of example, let say I have the set {1, 4}. Let say I would like to generate an array of size 5 (e.g., [1 1 4 1 4]).
What I did is this: I generated the following array using randi.
N = 5;
v = randi([1 4],[1 N]);
The problem is that I got a random array which contains values in 1:4 and not in {1, 4}.
I can simply do this but I need a better way.
for i = 1:length(v)
if v(i) ~= 1 || v(i) ~= 4
v(i) = 1; % or v(i) = 4
end
end
I think I am missing a simple hint here.
You should use datasample,
y = datasample(data,k) returns k observations sampled uniformly at random, with replacement, from the data in data.
a = [1,4];
datasample(a,5)
Depending on the usage, you might consider using,
datasample(unique(a),5)
If you don't have the Statistics Toolbox (which contains the datasample function), you can use randi:
N = 5; %// desired number of samples
data = [1 4]; %// data values
sample = data(randi(numel(data),1,N));
And if you use a very old version of Matlab that doesn't have randi, you can employ rand:
sample = data(ceil(numel(data)*rand(1,N)));