Generate random non-repeating integers from a small range - matlab

What I'm trying to accomplish is the following:
I wish to create a vector of integers, from a relatively small range, and ensure that none of the integers will be followed by the same integer.
i.e., This is a "legal" vector:
[ 1 3 4 2 5 3 2 3 5 4 ]
and this is an "illegal" vector (since 5 follows 5):
[ 1 3 4 2 5 5 2 3 5 4 ]
I've experimented with randi, and all sorts of variations with randperm, and I always get stuck when i try to generate a vector of around 100 elements, from a small range (i.e., integers between 1 and 5).
The function just runs for too long.
Here's one of the attempts that i've made:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
while any(diff(result) == 0)
result = randi(top, 1, count);
end
end
Any and all help will be much appreciated. Thanks !

The kind of sequence you are looking for can be defined by generating differences from 1 to top - 1 and then computing the cumulative sum modulus top, starting from a random initial value:
function result = nonRepeatingRand(top, count)
diff = randi(top - 1, 1, count);
result = rem(cumsum(diff) + randi(1, 1, count) - 1, top) + 1;
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 0.58 seconds.

you can use the following code for generate Non Repeating Random Numbers from 1 to M
randperm(M);
and for K Non Repeating Random Numbers from 1 to M
randperm(M, K);
enjoy

Do not regenerate the sequence every time, but fix the repetitions. E.g.:
function result = nonRepeatingRand(top, count)
result = randi(top, 1, count);
ind = (diff(result) == 0);
while any(ind)
result(ind) = [];
result(end + 1 : count) = randi(top, 1, count - numel(result));
ind = (diff(result) == 0);
end
end
On my machine, this generates a non-repeating sequence of 10 million numbers out of 1:5 in 1.6 seconds.

Taking the idea from A. Donda but fixing the implementation:
r=[randi(top,1,1),randi(top - 1, 1, count-1)];
d=rem(cumsum(r)-1,top)+1;
The first element of r is a randomly chosen element to start with. The following elements of r randomly choose the difference to the previous element, using modulo arithmetic.

How this?
top = 5;
count = 100;
n1 = nan;
out = [];
for t = 1: count
n2 = randi(top);
while n1 == n2
n2 = randi(top);
end
out = [out, n2];
n1 = n2;
end

Related

Is there a way to modify the isdag matlab function in order for it to ignore cycles of length zero?

recently I've been tasked with programming an algorithm that optimizes job-shop scheduling problems and I'm following an approach which uses directed graphs for this. In these directed graphs nodes represent events and edges represent time precedence constraints between events, i.e. time inequalities. So, for example, 2 consecutive nodes A & B separated by a directed edge of length 2 which goes from A to B would represent the inequality tB-tA>=2. It follows that equality would be represented by 2 directed edges of opposite directions, one with positive length and the other one with negative length. Thus, we end up with a graph which has some cycles of length zero.
Matlab has a function called isdag which returns true if a directed graph has no cycles and false otherwise; is there a way to modify this function in order for it to ignore the cycles of length zero? If not, has anyone got any idea on how to program this? Thanks in advance!!
I also tried this but it doesn't work. I've tried it with the adjacency matrix adjMatrix = [0, 10, 0; -9, 0, 5; 0, 0, 0] it should return true as it has a cycle between nodes 1 and 2 of length 10+(-9)=1, but it returns false,
function result = hasCycleWithPositiveWeight(adjMatrix)
n = size(adjMatrix,1);
visited = false(1,n);
path = zeros(1,n);
result = false;
for i = 1:n
pathStart = 1;
pathEnd = 1;
path(pathEnd) = i;
totalWeight = 0;
while pathStart <= pathEnd
node = path(pathStart);
visited(node) = true;
for j = 1:n
if adjMatrix(node,j) > 0
totalWeight = totalWeight + adjMatrix(node,j);
if visited(j)
if j == i && totalWeight > 0
result = true;
return;
end
else
pathEnd = pathEnd + 1;
path(pathEnd) = j;
end
end
end
pathStart = pathStart + 1;
totalWeight = totalWeight - adjMatrix(node, path(max(pathStart-1,1)));
visited(node) = false;
end
end
end
If you want to find the cycle between just two consecutive nodes use this:
a = [0, 10, 0; -9, 0, 5; 0, 0, 0];
b = a.';
And = (a & b);
Add = (a + b);
result = any( And .* Add, 'all');
It returns true if there is a cycle that its length isn't 0.
Explanation:
In the graph if the length between node 2 and 5 is 12 we set the element A(2 , 5) of the adjacency matrix to 12 and if the length between node 5 and 2 is -8 we set the element A(5, 2) of the adjacency matrix to 8. So there is a symmetry between nodes relationship. The matrix transpose changes the position of (2, 5) to (5, 2) and (5, 2) to (2, 5).
If we And a matrix with its transpose the result matrix shows that there is a cycle between two nodes if it is 1.
If we Add a matrix to its transpose the result matrix shows the sum of the pairwise lengths between nodes.
If we multiplyelement-wise the two matrices Add and And the result matrix shows the sum of the pairwise lengths between nodes but the sum of lengths of two nodes that don't form a cycle is set to 0.
Now the function any can be used to test if the matrix has a cycle that its length isn't 0.

How to do following in Matlab?

Suppose I have a sequence of n numbers
e=[5,4,45,63,22,22,1,12,3,2,2,16,14,14,16,17,1,19,21,15,32,32,27,27,43,41,7,8,13,23,23]
then for first 10 numbers i.e.
[5,4,45,63,22,22,1,12,3,2]
count numbers other than 1 to 5 and then divide by 10, i.e.
[45,63,22,22,12]
total 5, so result should be 5/10,now for first 20 numbers i.e.
[5,4,45,63,22,22,1,12,3,2,2,16,14,14,16,17,1,19,21,15]
then
[45,63,22,22,12,16,14,14,16,17,19,21,15]
total = 13, so 13/20, like this for 10, 20, 30,... up to n numbers
and then plot figure with x axis points 0 10 20 30 ... n and y axis with 5/10, 13/20, ... how to do this
I tried by this
for e=10:10:400
for u=1:length(e)
d=(numel(u)>5)
h=d/u
end
end
but it shows different.
Try this
e= [5,4,45,63,22,22,1,12,3,2,2,16,14,14,16,17,1,19,21,15,32,32,27,27,43,41,7,8,13,23,23];
bins = 10:10:numel(e);
counts = NaN(1,numel(bins)); %// pre-allocation. I'm pre-allocating with NaN here instead of zeros because 0 is a valid result for an element of counts and thus could make us miss errors should something go wrong
for k = 1:numel(bins)
counts(k) = sum(e(1:bins(k)) > 5)/bins(k);
end
plot(bins, counts) %// or you might prefer bar(bins, counts)
Here e(1:bins(k)) will be the first 10 elements of e in the first iteration of the loop, the first 20 in the second and so on. sum(... > 10) just counts how many elements are greater than 5. To understand how this works, consider x = [3 4 5 6 7 8 5 1 2]. Now x > 5 will return the logical array [0 0 0 1 1 1 0 0 0], so sum(x>10) is the same as sum([0 0 0 1 1 1 0 0 0]) which is 3 i.e. the count of elements in x greater than 5. Now you just need to store this count in a different element of counts at each iteration hence we have counts(k) = ... and not counts = ... as the latter (i.e. how your code has it) just overrides counts with the scalar count at each iteration rather than creating a vector that records each count at each iteration.
In MATLAB you can often do away with loops and you can do so in this case as well:
counts = cumsum(e > 5)./(1:numel(e));
h = counts(10:10:end);
Hope, this can help you
n=30 % this because of the 'e' size
Lim = 5 % your limit
Steps = 10 %
xValues = Steps:Steps:n
PlotSeries = NaN(size(e,2)/Steps,2)
for x = 1:1:size(e,2)/Steps
PlotSeries(x,:) = [xValues(x),size(e(e(1:xValues(x))>Lim),2)/xValues(x)]
end
plot(PlotSeries)

generate non-consecutive samples

How can we efficiently generate k random and non-consecutive samples out of [1,...,N]?
Non-desired Example with (N=10, k=4):
2,3,8,10
This is not a desired example since 2 and 3 are consecutive.
Desired Example with (N=10, k=4):
2,6,8,10
This is a good example since the difference between every pair of samples is greater than 1
sort(randperm(N-(k-1),k))+[0:(k-1)]
There is a simple obersavation behind this solution, if you take any sorted solution to your problem and substract [0:(k-1)], you end up with a random choice of k numbers out of N-(k-1)
Let S denote the set of all k-element vectors with values taken from [1,...,N] that don't have any consecutive values. To randomly sample with a uniform distribution over S, you can use a rejection method:
Sample uniformly on a larger sample space, T.
If the sample belongs to the target region S, accept the sample. Else return to step 1 (the sample is rejected).
In Matlab, it's easy to generate uniformly distributed k-element vectors with values taken from [1,...,N] without replacement (function randsample). So this is used as the sample space T:
k = 4;
N = 10;
result = [1 1]; % // just to get while loop started
while any(diff(result)<=1) % // does not meet condition: try again
result = sort(randsample(N, k).'); %'// random sample without replacement
end
A Python class which correctly checks every pair of samples. You're responsible for not passing it a set of numbers that is impossible, though (like N = 10, k = 100).
>>> class NonConsecutiveSampler(object):
def __init__(self,N):
import random
self.num = N
def get_samples(self,k):
possibilities = [i for i in range(1,self.num + 1)]
samples = []
while len(samples) < k:
r = random.sample(possibilities,1)[0]
samples.append(r)
for i in range(r - 1, r + 2):
if i in possibilities:
possibilities.remove(i)
samples.sort()
return samples
>>> n = NonConsecutiveSampler(10)
>>> n.get_samples(4)
[2, 5, 8, 10]
>>> n.get_samples(4)
[1, 5, 7, 10]
>>> n.get_samples(4)
[3, 6, 8, 10]
>>> n.get_samples(4)
[1, 3, 5, 8]
EDIT: Made it much more efficient
Sometimes it's faster and easier to generate more samples than you need and then throw away the undesireable values.
One (slow) example.
vec= randi(100,1,1);
for j = 2:50,
while(abs(vec(j)-vec(j-1)<2) vec(j)= randi(100,1,1);end;
end
Another way. Suppose you want 50 samples
vec = rand(100,100,1);
badindex = find(abs(vec(1:99)-vec(2:100) < 1));
vec(badindex) = vec(badindex-1)+vec(badindex+1);
% if you don't want big values,
vec(vec>100) = vec (vec>100) -100; % to ensure, I hope, that neighbors
% are nonconsecutive
(This would be easier in R) .
You can make the increment between samples evenly distributed between 2 and N-1 (to avoid consecutive and repeated numbers):
N=10;
k=4;
increments = floor(rand(1,k)*(N-2))+2 %// increments allowed are from 2 to N-1 inclusive
out = mod(cumsum(increments), N)+1 %// sum increments
Same in python:
from numpy import cumsum, floor, mod, random
N=5
k=100
increments = floor(random.rand(1,k)*(N-2))+2
out = mod(cumsum(increments), N)+1
print(out)
[ 5. 3. 1. 5. 2. 4. 3. 2. 4. 2. 4. 3. 1. 5. 4. 3. 5. 4.
2. 5. 4. 2. 5. 2. 4. 1. 5. 4. 1. 5. 3. 1. 3. 2. 4. 1.
5. 4. 1. 3. 5. 4. 3. 5. 2. 1. 3. 2. 4. 3. 1. 4. 2. 1.
3. 2. 1. 4. 3. 2. 1. 3. 5. 3. 5. 4. 2. 4. 2. 1. 3. 2.
1. 3. 5. 2. 5. 4. 3. 1. 4. 1. 4. 3. 5. 4. 2. 1. 5. 2.
1. 5. 4. 2. 4. 3. 5. 2. 4. 1.]
Over 100 iterations, even if I limit the number to 1..5, there is no repeated/consecutive number.
A solution in MATLAB (perhaps inelegant) could be something like this:
N = 10;
k = 4;
out = zeros(1,k);
vec = 1 : N;
for idx = 1 : k
ind = randi(numel(vec), 1);
left = max(ind-1, 1); right = min(numel(vec), ind+1);
out(idx) = vec(ind);
to_remove = ind;
if vec(left) == vec(ind)-1
to_remove = [to_remove left];
end
if vec(right) == vec(ind)+1
to_remove = [to_remove right];
end
vec(to_remove) = [];
end
We first declare N and k, then declare an output array of zeroes that is k long. We then generate a sampling vector vec that goes from 1 up to as N initially. Next, for each value we want to place into the output, we generate a random position to sample from the vector, then take a look at the position from the left and from the right... ensuring that we are within the boundaries of the array. Also, we only remove to the left or right if the value at the left of the index to remove and also the right are equal to each other (thanks beaker!)
We use this location and sample from this vector, place the value at this location to the output, then remove the indices in this vector that are to the left, to the right, and the actual index itself from this vector. This removes the possibility of sampling from those values again. We repeat this until we run out of values to place in the output.
Here are a few of trial runs:
>> out
out =
9 7 1 5
>> out
out =
7 1 4 10
>> out
out =
10 8 1 6
>> out
out =
10 4 8 1
A not-particularly-elegant python solution:
def nonseq(n, k):
out = [random.randint(0, n)]
while len(out) < k:
x = random.randint(0, n)
if abs(x - out[-1]) > 1:
out.append(x)
return out
This is a recursive elegant version, I just added a check on k and N to avoid infinite recursion, if k>N/2 no solution exists.
The result is guaranteed random.
import random
def myFunc(N,k):
if k>(N+1)/2:
return "k to big for N"
returnValue = sorted(random.sample(range(1,N+1),k))
toTest = [x - returnValue[i - 1] for i, x in enumerate(returnValue)][1:]
if 1 in toTest:
return myFunc(N,k)
else:
return returnValue
print myFunc(10,4)
My implementation:
def ncsample(population, k):
import random
if k > 0:
i = random.randrange(0, len(population) - 2*(k-1))
return [population[i]] + ncsample(population[i+2:], k-1)
else:
return []
Note: it randomly finds the sequence in one shot (no rejection sampling in a while loop).
MATLAB implementation:
function r = ncsample(population, k)
if k > 0
i = randi(length(population) - 2*(k-1));
r = [population(i) ncsample(population((i+2):end), k-1)];
else
r = [];
end
end
Some tests:
>> for i=1:10; fprintf('%s\n',sprintf('%d ', ncsample(1:10, 4))); end
1 5 7 9
3 5 8 10
3 5 8 10
4 6 8 10
2 6 8 10
1 4 8 10
1 4 7 9
3 6 8 10
1 6 8 10
2 4 7 9

Shuffle a vector of repeated numbers so the numbers do not repeat in MATLAB

Okay, so I have a script that will produce my vector of repeated integers of a certain interval, but now theres a particular instance where I need to make sure that once it is shuffled, the numbers do not repeat. So for example, I produced a vector of repeating 1-5, 36 times, shuffled. How do I ensure that there are no repeated numbers after shuffling? And to make things even more complex, I need to produce two such vectors that do not ever have the same value at the same index. For example, lets say 1:5 was repeated twice for these vectors, so then this would be what I'm looking for:
v1 v2
4 2
2 4
3 2
5 3
4 5
1 4
5 1
1 5
3 1
2 3
I made that right now by taking an example of 1 vector and just shifting it off by 1 to create another vector that will satisfy the requirements, but in my situation, that wont actually work because I can't have them be systematically dependent like that.
So I tried a recursive technique to make the script start over if the vectors did not make the cut and as expected, that did not go over so well. I hit my maximum recursive iterations and I've realized this is clearly not the way to go. Is there some other alternative?
EDIT:
So I found a way to satisfy some of the conditions I needed above in the following code:
a = nchoosek(1:5,2);
b = horzcat(a(:,2),a(:,1));
c = vertcat(a,b);
cols = repmat(c,9,1);
cols = cols(randperm(180),:);
I just need to find a way to shuffle cols that will also enforce no repeating numbers in columns, such that cols(i,1) ~= cols(i+1,1) and cols(i,2) ~= cols(i+1,2)
This works, but it probably is not very efficient for a large array:
a = nchoosek(1:5, 2);
while (any(a(1: end - 1, 1) == a(2: end, 1)) ...
|| any(a(1: end - 1, 2) == a(2: end, 2)))
random_indices = randperm(size(a, 1));
a = a(random_indices, :);
end
a
If you want something faster, the trick is to logically insert each row in a place where your conditions are satisfied, rather than randomly re-shuffling. For example:
n1 = 5;
n2 = 9;
a = nchoosek(1:n1, 2);
b = horzcat(a(:,2), a(:,1));
c = vertcat(a, b);
d = repmat(c, n2, 1);
d = d(randperm(n1 * n2), :);
% Perform an "insertion shuffle"
for k = 2: n1 * n2
% Grab row k from array d. Walk down the rows until a position is
% found where row k does not repeat with its upstairs or downstairs
% neighbors.
m = 1;
while (any(d(k,:) == d(m,:)) || any(d(k,:) == d(m+1,:)))
m = m + 1;
end
% Insert row k in the proper position.
if (m < k)
ind = [ 1: m k m+1: k-1 k+1: n1 * n2 ];
else
ind = [ 1: k-1 k+1: m k m+1: n1 * n2 ];
end
d = d(ind,:);
end
d
One way to solve this problem is to think both vectors as being created as follows:
For every row of arrays v1 and v2
Shuffle the array [1 2 3 4 5]
Set the values of v1 and v2 at the current row with the first and second value of the shuffle. Both values will always be different.
Code:
s = [1 2 3 4 5];
Nrows = 36;
solution = zeros(Nrows,2);
for k=1:Nrows
% obtain indexes j for shuffling array s
[x,j] = sort(rand(1,5));
%row k takes the first two values of shuffled array s
solution(k,1:2) = s(j(1:2));
end
v1 = solution(:,1);
v2 = solution(:,2);
Main edit: random => rand,
With this method there is no time wasted in re-rolling repeated numbers because the first and second value of shuffling [1 2 3 4 5] will always be different.
Should you need more than two arrays with different numbers the changes are simple.

Create matrix of row-wise increasing differences based on vector

I have a column vector in MATLAB and am trying to construct a matrix of differences with row-wise varying size of difference.
It is hard to explain in words, so I will illustrate with an example:
lets say my data is:
data = [ 1 2 3 4 5 6 ];
what i am trying to do, is make a matrix that takes the differences as such (each column difference size changes [increasing by one]):
diff =
[(2 - 1) ...
(3 - 2) (3 - 1) ...
(4 - 3) (4 - 2) (4 - 1) ...
(5 - 4) (5 - 3) (5 - 2) (5 - 1) ...
(6 - 5) (6 - 4) (6 - 3) (6- 2) (6 - 1)]
My best guess of doing this was to make a triangle matrix with nested loops. My MATLAB code looks like this:
differences = zeros(length(data) - 1, length(data) - 1);
step = 0;
for j = 1:1:size(data) - 1;
for i = 1:size(logquarterly) - 1 - step;
if j <= i;
differences(i,j) = data(i + 1 + step , 1) - data(i,1);
step = step + 1;
end
end
end
What I am trying to do is calculate the first column of differences with distance 1, then calculate the second column of differences with distance 2 and so on... To accommodate the necessary row values I need, I am using the "step" variable which is set to zero for calculating column one, I then want it to increase by 1 when calculating column 2 to have the correct dimensions. But I can not make it work. If I take the "step" out and use this:
differences = zeros(length(data) - 1, length(data) - 1);
for j = 1:1:size(data) - 1;
for i = 1:size(logquarterly) - 1;
if j <= i;
differences(i,j) = data(i + 1 , 1) - data(i,1);
end
end
end
everything works, but each column has the same distance of differences and it does not increase by one. Any ideas guys?
If I understand right, you want to do that:
data = [ 1 2 3 4 5 6 ];
n = numel(data);
%// calculate differences
diffs = bsxfun(#minus, data(end:-1:1), data(end:-1:1).')
%'
%// get linear indices from circulant sub-indices for rows and
%// linear indices for columns
idx = sub2ind([n n], gallery('circul',n:-1:1), ndgrid(1:n,1:n))
%// mask output and get lower triangular matrix
output = tril(diffs(idx(n-1:-1:1,n-1:-1:1)))
so the output is:
output =
1 0 0 0 0
1 2 0 0 0
1 2 3 0 0
1 2 3 4 0
1 2 3 4 5
The problem with your solution is that it will only work with column vectors, because of the loop j = 1:1:size(data)-1. The call of size will return [1,6]; then the -1 is applied yielding [0,5]. Then only the first value of this vector is taken and in turn the for loop will only run from 1 to 1-1==0, i.e. NOT.
Use numel or size(.,1)/size(.,2) instead. (Also don't use semicola ; after the loop initialization). (Try out the MATLAB debugger!)
Here is my take on how to repair your approach:
differences = zeros(length(data)-1, length(data)-1);
for j = 1:size(differences,2)
for i = j:size(differences,1)
differences(i,j) = data(i+1) - data(i-j+1);
end
end
I like the use of gallery('circul',n:-1:1), in thewaywewalk's answer, I do however find the rest a bit too complicated.
Here is my take reusing his idea:
n = numel(data);
L = ndgrid(2:n,2:n); % // Generate indices for Left side of operator
R = gallery('circul',1:n-1).'; %'// Generate indices for Right side of operator
out = tril(data(L) - data(R)) % // Do subtraction of corresponding indices