Home > OS >  Random Numbers to a Sum value in Matlab
Random Numbers to a Sum value in Matlab

Time:08-06

I have list of numbers from Row 1 to Row 12 say in Column A and I need to have the number of each row distributed in to four different columns (Column B, C, D, E) in Matlab so that: (i) the sum of values shown in columns B, C, D, E is equal to the value of Column A. (ii) The values shall only be whole numbers. (iii) Columns B, C, D, E have maximum limit of 10, 10, 5 and 5 respectively. (iv) Columns B, C, D, E have minimum limit of 5, 5, 3, 3 respectively.

That is the values generated in these columns should be with in these maximum limit values.

Example: | A | B | C | D |

| 17 | 6 | 5 | 3 | 3 |

Any way to pick values from Column A and then get the values in different columns (B,C,D,E) so that the sum is equal to A for each row?

I tried like this but it still lacks the exact target. Some of the issues are mentioned after coding:

clear all
while true
 A=randi([3 5],12,4);
     if((sum(A,2)>=16) | (sum(A,2)<=30));
   break;
end
end

disp(A);
disp(sum(A,2));

All the values generated are random, however they are between 16 and 30. But the lowest value limit I can not sort out how to do for BCDE? And if I already have a data of column A then how can I make the BCDE values equal to that sum for each row?

CodePudding user response:

I suggest you do this with a simple loop:

M = [0,0,0,0];  % A, B, C, D
For each A
    While sum(M) is not A
        M(A) = [(2 random numbers 5-10), (2 random numbers 3-5)]
    end
end

Use randi to get the random numbers.

This is not optimal for large numbers/lists but I'm fairly sure will be fast enough for the numbers you work with unless you do this millions of times.

I'll leave implementing this in MATLAB to you as an exercise. :) Shout out if you get stuck.


Update:

This is quick and dirty, but it works :) It uses 130 ms on average on tio.run.

A = 15 randi(15,12,1);
BCDE = zeros(numel(A),4);
temp = [0, 0, 0, 0];
for ii = 1:numel(A)
    temp = [4 randi(6,1,2), 2 randi(3,1,2)];
    while sum(temp) ~= A(ii)
        temp = [4 randi(6,1,2), 2 randi(3,1,2)];
    end
    BCDE(ii,:) = temp;
end
[A, BCDE]

ans = 
   16    5    5    3    3
   18    5    5    4    4
   18    5    6    4    3
   29   10   10    5    4
   24    6   10    3    5
   27   10    9    4    4
   18    5    6    3    4
   23    5   10    5    3
   27    9    8    5    5
   24    7    9    3    5
   28   10   10    3    5
   20    9    5    3    3

CodePudding user response:

Fast and deterministic approach without using loops. The idea is to first split the number A into two halves L, R. The condition for both L and R is they lie between [8 15]. The function randSplit(x, lo, hi) will do that.

Choose L = B D and R = C E. Now the remaining part is two split L into [B, D] and split R into [C, E]; the function randSplit(x, lo, hi, LO, HI) will do that. The condition in this case is B, C both lie between [5 10] and D, E both lie between [3 5].

This method is fast as it calls randi exactly 3 times per row of the output matrix.

mat = zeros(12,5);
for i = 1:12
    A = randi([16 30]);
    [L, R] = randSplit(A, 8, 15); % L = B   D, R = C   E
    [B, D] = randSplit(L, 3, 5, 5, 10);
    [C, E] = randSplit(R, 3, 5, 5, 10);
    mat(i,:) = [A B C D E];
end

function [a, b] = randSplit(x, lo, hi, LO, HI)
    if nargin == 3
        a = randi([max(lo,x-hi) min(hi,x-lo)]);
        b = x - a;
    elseif nargin == 5
        a = randi([max(LO,x-hi) min(HI,x-lo)]);
        b = x - a;
    end
end

Sample output:

mat =
    29    10    10     5     4
    21     8     5     5     3
    23    10     5     5     3
    21     5     9     4     3
    17     5     6     3     3
    27     8     9     5     5
    19     8     5     3     3
    30    10    10     5     5
    17     5     6     3     3
    27    10     9     5     3
    18     5     6     3     4
    25     9    10     3     3
  • Related