How to distribute values randomly over a given time period? - matlab

I am trying to distribute a certain value over a random period of time. To clarify more ,
Suppose I want to distribute product x and y over 30 days. I have 1500 items of product x that has to be distributed over 30 days randomly. There is a restriction on the number of items that can be distributed over 1 day i.e.max 60.
I have been trying to scratch out something but am really unsucessful with this problem. I am really new to programming so it would be a real help if somebody could point me to the right approach.
As an addendum, if I have more than 1 items to be distributed (like suppose there are x,y and z) with different values (ex. 1500, 1000, 900) and there is a limitation on how many items can be distributed on a particular day (max 150 per day) will this logic still work or should I look at something new. Also, should there be a check, like suppose 100 of x, 20 of y and 30 of z are distributed, then subtract the value (for the next day I have 1400 of x, 980 of y and 870 of z available for distribution) as this will change the permutation values ?
Thank you guys !

This should work for you!
days = 30;
elem = 1500;
max_x = 60;
x = randi(max_x,days,1);
remain = elem - sum(x);
while remain > 0
idx_1 = find(x < max_x); % Numbers that can be increased
idx_fill = randperm(numel(idx_1),remain);
% idx_fill = idx_fill(:,1); % Might be needed
x(idx_1(idx_fill)) = x(idx_1(idx_fill)) + 1;
remain = elem - sum(x);
end
while remain < 0
idx_2 = find(x > 0); % Numbers that can be reduced
idx_red = randperm(numel(idx_2),abs(remain));
% idx_red = idx_red(:,1); % Might be needed
x(idx_2(idx_red)) = x(idx_2(idx_red)) - 1;
remain = elem - sum(x);
end
sum(x)
max(x)
min(x)
ans = 1500
ans = 60
ans = 34

This is an intuitive approach and works nicely for 2D arrays, without "randperm":
N = 36000; % for three hundred years
days = 30; % days
elem = 1500; % elements in ten years
min_x = 0; % daily minimum
max_x = 60; % daily maximum
tic
x = zeros(days, N);
for hh = 1:elem
% Add new candidates
inds = randi(days, N, 1);
inds = ((1:N).' - 1) * days + inds;
x(inds) = x(inds) + 1;
% Check
inds_chck = x > max_x;
any_inds_chck = any(inds_chck);
find_any_inds_chck = find(any_inds_chck);
ctrl = numel(find_any_inds_chck);
while ctrl>0
% First remove baddies
inds = inds(find_any_inds_chck);
x(inds) = x(inds) - 1;
% Then reassign to new candidates
inds = randi(days, ctrl, 1);
inds = (find_any_inds_chck.' - 1) * days + inds;
x(inds) = x(inds) + 1;
% Check again
inds_chck = x(:, find_any_inds_chck) > max_x;
any_inds_chck = any(inds_chck);
find_any_inds_chck = find(any_inds_chck);
ctrl = numel(find_any_inds_chck);
end
end
toc
But the price is a weird probability function:
hist(x(:), max_x - min_x + 1)
Note that the constraint has an obvious effect on the degrees of freedom as well.
Also note that they have tried to answer a similar question in Generate a random number with max, min and mean (average) in Matlab .

Related

How to improve this function?

The problem is there are c no. of firms bidding on p no. of projects. The winning bidders should collectively have the lowest cost on the client. Each firm can win a maximum of 2 projects.
I have written this code. It works, but takes forever to produce the result, and is very inefficient.
==========================================================================
function FINANCIAL_RESULTS
clear all; clc;
%This Matlab Program aims to select a large number of random combinations,
%filter those with more than two allocations per firm, and select the
%lowest price.
%number of companies
c = 7;
%number of projects
p = 9;
%max number of projects per company
lim = 2;
%upper and lower random limits
a = 1;
b = c;
%Results Matrix: each row represents the bidding price of one firm on all projects
Results = [382200,444050,725200,279250,750800,190200,528150,297700,297700;339040,393420,649520,243960,695760,157960,454550,259700,256980;388032,499002,721216,9999999,773184,204114,512148,293608,300934;385220,453130,737860,287480,9999999,188960,506690,274260,285670;351600,9999999,9999999,276150,722400,9999999,484150,266000,281400;404776,476444,722540,311634,778424,210776,521520,413130,442160;333400,403810,614720,232200,656140,165660,9999999,274180,274180];
Output = zeros(1,p+1);
n=1;
i=1;
for i = 1:10000000
rndm = round(a + (b-a).*rand(1,p));
%random checker with the criteria (max 2 allocations)
Check = tabulate(rndm);
if max(Check(:,2)) > lim
continue
end
Output(n,1:end-1) = rndm;
%Cumulative addition of random results
for k = 1:p
Output(n,end) = Output(n,end) + Results(rndm(k),k);
end
n = n+1;
end
disp(Results);
[Min_pay,Indx] = min(Output(:,end));
disp(Output(Indx,:));
%You know the program is done when Handel plays
load handel
sound(y,Fs);
%Done !
end
Since the first dimension is much greater than the second dimension it would be more efficient to perform loop along the second dimension:
i = 10000000;
rndm = round(a + (b-a) .* rand(i, p));
Check = zeros(size(rndm, 1), 1);
for k = 1:p
Check = max(Check, sum(rndm == k, 2));
end
rndm = rndm(Check <= lim, :);
OutputEnd = zeros(size(rndm, 1), 1);
for k = 1:p
OutputEnd = OutputEnd + Results(rndm(:, k), k);
end
Output = [rndm OutputEnd];
Note that if the compute has a limited memory put the above code inside a loop and concatenate the results of iterations to produce the final result:
n = 10;
Outputc = cell(n, 1);
for j = 1:n
i = 1000000;
....
Outputc{j} = Output;
end
Output = cat(1, Outputc{:});

Seeking advice on trying to read a moore neighbourhood for a 2D cellular automata in MATLAB for an epidemic simulator

I'm currently working on a code that makes use of a 2D cellular automata as an epidemic simulator in MATLAB. The main basic rule I'm trying to implement is that if any neighbour within a Moore Neighbourhood with a 1-cell radius is infected, the cell will become infected. But I can't seem to get a good code working for it.
Basically what I'm trying to do is say with for a cell with a one cell radius Moore neighbourhood, if any values in this neighbourhood = 2, then the initial cell will become 2.
I've tried using the forest fire code on the rosetta code as a basis for my code behaviour but it doesnt work very well. The rules don't really work that well when applying it to mine. I've tried using the mod function and a series of if loops to attach. I'll put in some code of each to give context.
This example doesn't really function well as an epidemic simulator to be honest.
matlab
clear; clc;
n = 200;
N = n/2;
E = 0.001; % Creating an arbitrary number for population exposed to
the disease but not infected
p = 1 + (rand(n,n)<E);
%p = ceil(rand(n,n)*2.12) - 1;
% ratio0 = sum(p(:)==0)/n^2;
% ratio1 = sum(p(:)==1)/n^2;
% ratio2 = sum(p(:)==2)/n^2;
% ratio3 = sum(p(:)==3)/n^2;
S = ones(3); S(2,2) = 0;
ff = 0.00000000002;
p(N,N) = 3;
%% Running the simulation for a set number of loops
colormap([1,1,1;1,0,1;1,0,0]); %Setting colourmap to Green, red and
grey
count = 0;
while(count<365) % Running the simulation with limited number of runs
count = count + 1;
image(p); pause(0.1); % Creating an image of the model
P = (p==1); % Adding empty cells to new array
P = P + (p==2).*((filter2(S,p==3)>0) + (rand(n,n)<ff) + 2); % Setting
2 as a tree, ignites based on proximity of trees and random
chance ff
P = P + (p==3); % Setting 3 as a burning tree, that becomes 1,
p = P;
end
second idea. this basically returns nothing
matlab
clear;clf;clc;
n = 200;
pos = mod((1:n),n) + 1; neg = mod((1:n)-2,n) + 1;
p = (ceil(rand(n,n)*1.0005));
for t = 1:365
if p(neg,neg) ==2
p(:,:) = 2;
end
if p(:,neg)==2
p(:,:) = 2;
end
if p(pos,neg)==2
p(:,:) = 2;
end
if p(neg,:)==2
p(:,:) = 2;
end
if p(pos,:)==2
p(:,:) = 2;
end
if p(neg,pos)==2
p(:,:) = 2;
end
if p(:,pos)==2
p(:,:) = 2;
end
if p(pos,pos)== 2
p(:,:) = 2;
end
image(p)
colormap([1,1,1;1,0,1])
end
third I tried using logic gates to see if that would work. I don't know if commas would work instead.
matlab
clear;clf;clc;
n = 200;
pos = mod((1:n),n) + 1; neg = mod((1:n)-2,n) + 1;
p = (ceil(rand(n,n)*1.0005));
%P = p(neg,neg) + p(:,neg) + p(pos,neg) + p(neg,:) + p(:,:) + p(pos,:)
+ p(neg,pos) + p(:,pos) + p(pos,pos)
for t=1:365
if p(neg,neg)|| p(:,neg) || p(pos,neg) || p(neg,:) || p(pos,:) ||
p(neg,pos) || p(:,pos) || p(pos,pos) == 2
p(:,:) = 2;
end
image(p)
colormap([1,1,1;1,0,1])
end
I expected the matrix to just gradually become more magenta but nothing happens in the second one. I get this error for the third.
"Operands to the || and && operators must be convertible to logical scalar values."
I just have no idea what to do!
Cells do not heal
I assume that
Infected is 2, non-infected is 1;
An infected cell remains infected;
A non-infected cell becomes infected if any neighbour is.
A simple way to achieve this is using 2-D convolution:
n = 200;
p = (ceil(rand(n,n)*1.0005));
neighbourhood = [1 1 1; 1 1 1; 1 1 1]; % Moore plus own cell
for t = 1:356
p = (conv2(p-1, neighbourhood, 'same')>0) + 1; % update
image(p), axis equal, axis tight, colormap([.4 .4 .5; .8 0 0]), pause(.1) % plot
end
Cells heal after a specified time
To model this, it is better to use 0 for a non-infected cell and a positive integer for an infected cell, which indicated how long it has been infected.
A cell heals after it has been infected for a specified number of iterations (but can immediately become infeced again...)
The code uses convolution, as the previous one, but now already infected cells need to be dealt with separately from newly infected cells, and so a true Moore neighbourhood is used.
n = 200;
p = (ceil(rand(n,n)*1.0005))-1; % 0: non-infected. 1: just infected
T = 20; % time to heal
neighbourhood = [1 1 1; 1 0 1; 1 1 1]; % Moore
for t = 1:356
already_infected = p>0; % logical index
p(already_infected) = p(already_infected)+1; % increase time count for infected
newly_infected = conv2(p>0, neighbourhood, 'same')>0; % logical index
p(newly_infected & ~already_infected) = 1; % these just became infected
newly_healed = p==T; % logical index
p(newly_healed) = 0; % these are just healed
image(p>0), axis equal, axis tight, colormap([.4 .4 .5; .8 0 0]), pause(.1) % plot
% infected / non-infected state
end

Simulate a queue using Lindley's equation in Matlab

I have to simulate a simple queue in Matlab using Lindley's equation:
W_{n+1}^Q = max(0, W_n^Q + S_n - X_{n+1}
I think I have done so, with the following code, but I am trying to run it several times and cannot save the information correct. The variables I want to save at the end of running the simulation are only saving the information from the last attempt (here for m=3).. while I clearly would like to see this for all runs (m=1,2,3).
for m=1:3
l = 1.1; % try this value for lambda
N = 10000; % let 1000 people arrive
X = exprnd(l,[1,N]); % make 1000 exponential interarrivals
S = 2*rand(1,N); % uniform on [0,2]
w = zeros(1,N);
sum1 = zeros(1,m);
avg1 = zeros(1,m);
max1 = zeros(1,m);
for i=1:N
if i==1 % first customer doesn't have to wait
w(i) = 0;
else % following customers follow lindley's equation
w(i) = max(w(i-1) + S(i-1) - X(i), 0); % n-th customer's waiting time
count(i) = w(i) > 15; % count number of times greater than 15
end
end
max1(m) = max(w);
sum1(m) = sum(count); % sum number of times greater than 15
avg1(m) = sum1(m)/N; % divide by 1000 to get probability delay is greater than 15
end
You are initializing sum1, avg1 and max1 inside the for loop so in every iteration, these variables are set to zero (i.e. by initialization). This is the reason you loose your previous iteration value. To avoid this, initialize sum1, avg1 and max1 before you for loop. Refer below code for reference. HTH
sum1 = zeros(1,m);
avg1 = zeros(1,m);
max1 = zeros(1,m);
for m=1:3
l = 1.1; % try this value for lambda
N = 10000; % let 1000 people arrive
X = exprnd(l,[1,N]); % make 1000 exponential interarrivals
S = 2*rand(1,N); % uniform on [0,2]
w = zeros(1,N);
for i=1:N
if i==1 % first customer doesn't have to wait
w(i) = 0;
else % following customers follow lindley's equation
w(i) = max(w(i-1) + S(i-1) - X(i), 0); % n-th customer's waiting time
count(i) = w(i) > 15; % count number of times greater than 15
end
end
max1(m) = max(w);
sum1(m) = sum(count); % sum number of times greater than 15
avg1(m) = sum1(m)/N; % divide by 1000 to get probability delay is greater than 15
end

Matlab code to analyze data on a grid

I have a point set with (x,y) coordinates and their corresponding weights in matrix a where the 1st, 2nd and 3rd columns are x, y, and weight respectively. I want to divide this point set into grid cells, and count the number of points in each grid and the total weight of each grid.
I tried the small example below, but it did not work. Here I tried to divide this data set into a 2x2 small grid and tried to count number of points and their sum of weights. Further, I have big data set, so I can not extend this approach further when I need different step sizes for grid.
Can someone please help me to develop an easier approach?
function dataTree
count=zeros(9,1);
avg=zeros(9,1);
data=[1 3 100; 2 1 120; 3 5 110; 4 2 100; 5 3 150; 6 2 100];
for i=1:6
if data(i,1)<=2
for j=1:6
if data(j,2)<=2
count(1) = count(1) + 1;
avg(1) = avg(1) + data(j,3);
elseif data(j,2)<=4
count(2) = count(2) + 1;
avg(2) = avg(2) + data(j,3);
elseif data(j,2)<=6
count(3) = count(3) + 1;
avg(3) = avg(3) + data(j,3);
end
end
elseif data(i,1)<=4
for j=1:6
if data(j,2)<=2
count(4) = count(4) + 1;
avg(4) = avg(4) + data(j,3);
elseif data(j,2)<=4
count(5) = count(5) + 1;
avg(5) = avg(5) + data(j,3);
elseif data(j,2)<=6
count(6) = count(6) + 1;
avg(6) = avg(6) + data(j,3);
end
end
elseif data(i,1)<=6
for j=1:6
if data(j,2)<=2
count(7) = count(7) + 1;
avg(7) = avg(7) + data(j,3);
elseif data(j,2)<=4
count(8) = count(8) + 1;
avg(8) = avg(8) + data(j,3);
elseif data(j,2)<=6
count(9) = count(9) + 1;
avg(9) = avg(9) + data(j,3);
end
end
end
end
count'
avg'
If your x and y are not yet rounded to some arbitrary units, do so first:
x = round((x - min(x))/edgelength+1);
this makes sure that you obtain grid with edgelength sized squares, which is indicated by non-zero integers. Do the same for y.
Then you can use either sparse or accumarray to get the total weight. sparse is faster, but is less wide applicable:
gridWeight = sparse(x,y,weight);
if you want to get average weights, add 1 for each entry and divide by that matrix:
NumEntries = sparse(x,y,1);
MeanWeights = gridWeight./NumEntries;
accumarray can do both of these operations in one go:
gridWeight = accumarray([x y],weight);
MeanWeights = accumarray([x y], weight,[],#mean); %//add ,[], 'issparse' for sparse matrix
Note that sparse is a sub-functionality of accumarary by setting accumarray=([i,j],val,[],#sum,[],'issparse'). The only function sparse can handle is #sum and it's sole fill-value is 0, whilst for accumarray other functions and values can be used.

Recursively divide a square field - Matlab crashes

I am working with simulation of wireless sensor networks in matlab.
I have a 200*200 by field in which 100 sensor nodes have been plotted randomly. Each node has an associated load value with it. I have to place charging stations in this field. I am trying to divide this square recursively as long as I do not found a small sub-square in which I can place only one charging station. Here is the code I wrote to divide the square recursively and count number of stations that can be placed in a subsquare:
%Inputs to the function
%numstations - No. of stations to be placed = 10
%boundCoords - A 2*2 matrix with min and max coordinates of square . e.g [0 0;200 200]
% sensors - A 100*3 matrix for nodes with 1st column as randomly generated 100 x-coordinates,
%second column as randomly generated 100 y-coordinates,
%third column as corresponding load of each node (can be random)
function stationPoss = deploy(numStations, boundCoords)
global sensors;
centerCoord = mean(boundCoords, 1);
numSensors = size(sensors, 1);
sumQuadLoad = zeros(1, 4);
for i = 1:numSensors
if sensors(i, 1) < boundCoords(2, 1) && sensors(i, 2) < boundCoords(2, 2)...
&& sensors(i, 1) > boundCoords(1, 1) && sensors(i, 2) > boundCoords(1, 2)
isIn34Quads = sensors(i, 1) > centerCoord(1); % N
isIn24Quads = sensors(i, 2) > centerCoord(2);
biQuadIndex = [isIn34Quads, isIn24Quads];
quadIndex = bi2de(biQuadIndex) + 1;
sumQuadLoad(quadIndex) = sumQuadLoad(quadIndex) + sensors(i, 3);
end
end
if numStations == 1
[maxQuadLoad, quad] = max(sumQuadLoad); %#ok<ASGLU>
delta = (centerCoord - boundCoords(1, :)) .* de2bi(quad - 1);
assoQuadCoords = [boundCoords(1, :); centerCoord] + repmat(delta, 2, 1);
stationPoss = mean(assoQuadCoords, 1);
else
sumLoad = sum(sumQuadLoad);
quadNumStations = zeros(1, 4);
for i = 1:3
if sumQuadLoad(i) == 0
quadNumStations(i) = 0;
else
quadNumStations(i) = floor(numStations * sumQuadLoad(i) / sumLoad);
end
end
quadNumStations(4) = numStations - sum(quadNumStations);
stationPoss = zeros(numStations, 2);
for i = 1:4
delta = (centerCoord - boundCoords(1, :)) .* de2bi(i - 1);
newBoundCoords = [boundCoords(1, :); centerCoord] + repmat(delta, 2, 1);
if quadNumStations(i) ~= 0
indexRange = sum(quadNumStations(1:i-1)) + (1:quadNumStations(i));
stationPoss(indexRange, :) = deploy(quadNumStations(i), newBoundCoords);
end
end
end
The problem is while trying to run this code with numStations=2 it works fine and with numStations=3 it sometimes crashes. For numStation > 3 it almost always crashes.
I tried to come up with a non-recursive way to write this function but wasn't able to.
Will anyone please help me to figure out the crash problem or in writing non recursive solution to the above function. I have already tried increasing the recursion limit.