tags:

views:

97

answers:

5

For example, say I enter '10' for the amount of values, and '10000' as a total amount.

The script would need to randomize 10 different numbers that all equal up to 10000. No more, no less.

But it needs to be dynamic, as well. As in, sometimes I might enter '5' or '6' or even '99' for the amount of values, and any number (up to a billion or even higher) as the total amount.

How would I go about doing this?

EDIT: I should also mention that all numbers need to be a positive integer

+1  A: 

Update: @Joe Blow has the perfect answer. My answer has the special feature of generating chunks of approximately the same size (or at least a difference no bigger than (10000 / 10)), leaving it in place for that reason.

The easiest and fastest approach that comes to my mind is:

  • Divide 10000 by 10 and store the values in an array. (10 times the value 10000)

  • Walk through every one of the 10 elements in a for loop.

  • From each element, subtract a random number between (10000 / 10).

  • Add that number to the following element.

This will give you a number of random values that, when added, will result in the end value (ignoring floating point issues).

Should be half-way easy to implement.

You'll reach PHP's maximum integer limit at some point, though. Not sure how far this can be used for values towards a billion and beyond.

Pekka
I think I know what you mean, but I'm not sure. I'll try a few things based on this and report back. In the meantime, any additional help would be greatly appreciated.
Rob
If you are trying to get "similar sized chunks" that is very difficult. Try this. Start with 10 EVEN-SPACED items. Write a "vibrator" that picks 1 randomly and moves it a little. Possibly include a bounce if it hits the neighbor. Next write a thing to displays the results of this graphically, say 100 runs for given tune. Make it possible to completely tune all factors involved (how many vibrations (20?, a million?), how big, how distributed bigness, etcetcetc). Sit there "tuning" it for hours until you get the result needed for your task. Random walk, etc, will affect outcome - deep
Joe Blow
@Joe Blow well, that's more or less what these steps do - they give you chunks that never deviate from each other in size more than (10000 / 10). By reducing the limits when calling the randomizer, it's possible to make chunks more and more similar in size. It's not what the OP asked for though, so fine-tuning this is a question for another day
Pekka
Pekka, I understand EXACTLY what you are trying to do with your algorithm. However, you have an error!!!!!!! When you subtract a random number (say, 276) from item one, you are then adding 276 to item two. No good! You need to add 276 UNIFORMLY TO THE OTHER ITEMS, example say there are ten items overall, add 276 divided by nine to the other nine items. **AND** you need to randomly work through them, rather than going from left to right. Random numbers are very difficult to work with, you have to consider random-walk theory and more. It sucks!
Joe Blow
BTW Pekka when you say "subtract a number" I assume you mean add or subtract. (And then vice versa to the neighbor.) If you always subtract a number from item one ........... then ...... item one will always be less than 100!! (Just as a mental exercise, if for some reason you wanted to "only subtract", you would have to visit randomly, or at least start at a random point in the circuit.) Randomness is very, very tricky...you have to be incredibly careful. If you used the algo you describe for MC sim for engineering a bridge or a crane or something, you could have killed someone! :-)
Joe Blow
@Joe Blow I'm no mathematician and I have no problem with being corrected, but I can't see a fundamental error in my idea. If I have 10 chunks sized 100, and I subtract a random number between 0 and 100 from the first chunk (say, 30) I need to add that random number to the second chunk. I will end up with one chunk sized 70, and one sized 130, equalling 200. Continue the same operation until I reach the tenth chunk, which will always be larger than 100 (just as the first chunk will always be smaller than 100). I don't see the problem?
Pekka
Pekka !!! The first number will ALWAYS BE less than 100. Honestly. The others will be more, or less, than 100.)
Joe Blow
Pekka !!! The first number will ALWAYS BE less than 100. Honestly. The fact that the first number is ALWAYS less than 100 will kill you! By definition, that is NOT random. I am a mathematician; we have developed commercial gaming software. Say you had a routine that tosses a coin ten times; say the first one was always heads. Not random! Your algorithm is plainly wrong, I'm sorry. You should remove it or whatever in case someone passing uses it somewhere! Viel Glück!
Joe Blow
I wish you would remove this answer, which is wrong, in case someone uses it!
Joe Blow
+3  A: 

maybe something like this:

set max amount remaining to the target number

loop for 1 to the number of values you want - 1

get a random number from 0 to the max amount remaining

set new max amount remaining to old max amount remaining minus the current random number

repeat loop

you will end up with a 'remainder' so the last number is determined by whatever is left over to make up the original total.

Randy
Nice idea! Better than mine. Mine is better only if you want "chunks" of approximately the same size. +1
Pekka
I'm really not sure what you mean. Can you perhaps give me a small example? Doesn't need to be exact, pseudocode works
Rob
This DOES NOT WORK. it is completely, totally, absolutely, fully, 100% wrong. (Sorry!) You DO NOT get random numbers from this. Specifically, you get a list of numbers that run from large to small. Utterly not-random. It's a shame to see completely wrong guesses on stackoverflow .... if someone uses this in production they are buggered!
Joe Blow
No, this would work, as long as you made sure your numbers generated lied in the range of 0..(max / num_required)
Thomas O
@Thomas - thanks - I'm pretty sure it is a valid approach.
Randy
It is ABSOLUTELY NOT valid Randy! You are rolling a DIFFERENT SIZED DIE each time. (!!!!!) (And following a pattern, getting smaller.) It's a non-starter.
Joe Blow
+1  A: 

Generate 10 random numbers till 10000 . Sort them from big to small : g0 to g9

g0 = 10000 - r0
g1 = r0 - r1
...
g8 = r8 - r9
g9 = r9

This will yield 10 random numbers over the full range which add up to 10000.

Peter Tillemans
I'm sorry, but I have no idea what you mean.
Rob
It's the same thing as @Joe Blow explained more eloquently.
Peter Tillemans
+6  A: 

The correct answer here is unbelievably simple.

Just imagine a white line, let's say 1000 units long.

You want to divide the line in to ten parts, using red marks.

VERY SIMPLY, CHOOSE NINE RANDOM NUMBERS and put a red paint mark at each of those points.

It's just that simple. You're done!

Thus, the algorithm is:

(1) pick nine random numbers between 0 and 1000 (2) put the nine numbers, a zero, and a 1000, in an array (3) sort the array (4) using subtraction get the ten "distances" between array values

You're done.

(Obviously if you want to have no zeros in your final set, in part (1) simply rechoose another random number if you get a collision.)

Ideally as programmers, we can "see" visual algorithms like this in our heads -- try to think visually whatever we do!

Joe Blow
+1 for having the undoubtedly best and simplest answer; -0.9 for boasting :P still +1.
Pekka
Wow its so simple now. Thanks
Rob
It may be simple but it does **not** generate uniformly distributed numbers... It is biased in favor of numbers of size 1000/10 (for a sum of 1000 and 10 numbers).
Artefacto
Artefacto - it absolutely, definitely, generates uniform numbers. It could be you are making a unity error in your head thinking about the algorithm. Try some tests if you don't believe!
Joe Blow
A: 

Related: http://www.mathworks.cn/matlabcentral/newsreader/view_thread/141395

See this MATLAB package. It is accompanied with a file with the theory behind the implementation.

This function generates random, uniformly distributed vectors, x = [x1,x2,x3,...,xn]', which have a specified sum s, and for which we have a <= xi <= b, for specified values a and b. It is helpful to regard such vectors as points belonging to n-dimensional Euclidean space and lying in an n-1 dimensional hyperplane constrained to the sum s. Since, for all a and b, the problem can easily be rescaled to the case where a = 0 and b = 1, we will henceforth assume in this description that this is the case, and that we are operating within the unit n-dimensional "cube".

This is the implementation (© Roger Stafford):

function [x,v] = randfixedsum(n,m,s,a,b)

% Rescale to a unit cube: 0 <= x(i) <= 1
s = (s-n*a)/(b-a);

% Construct the transition probability table, t.
% t(i,j) will be utilized only in the region where j <= i + 1.
k = max(min(floor(s),n-1),0); % Must have 0 <= k <= n-1
s = max(min(s,k+1),k); % Must have k <= s <= k+1
s1 = s - [k:-1:k-n+1]; % s1 & s2 will never be negative
s2 = [k+n:-1:k+1] - s;
w = zeros(n,n+1); w(1,2) = realmax; % Scale for full 'double' range
t = zeros(n-1,n);
tiny = 2^(-1074); % The smallest positive matlab 'double' no.
for i = 2:n
 tmp1 = w(i-1,2:i+1).*s1(1:i)/i;
 tmp2 = w(i-1,1:i).*s2(n-i+1:n)/i;
 w(i,2:i+1) = tmp1 + tmp2;
 tmp3 = w(i,2:i+1) + tiny; % In case tmp1 & tmp2 are both 0,
 tmp4 = (s2(n-i+1:n) > s1(1:i)); % then t is 0 on left & 1 on right
 t(i-1,1:i) = (tmp2./tmp3).*tmp4 + (1-tmp1./tmp3).*(~tmp4);
end

% Derive the polytope volume v from the appropriate
% element in the bottom row of w.
v = n^(3/2)*(w(n,k+2)/realmax)*(b-a)^(n-1);

% Now compute the matrix x.
x = zeros(n,m);
if m == 0, return, end % If m is zero, quit with x = []
rt = rand(n-1,m); % For random selection of simplex type
rs = rand(n-1,m); % For random location within a simplex
s = repmat(s,1,m);
j = repmat(k+1,1,m); % For indexing in the t table
sm = zeros(1,m); pr = ones(1,m); % Start with sum zero & product 1
for i = n-1:-1:1  % Work backwards in the t table
 e = (rt(n-i,:)<=t(i,j)); % Use rt to choose a transition
 sx = rs(n-i,:).^(1/i); % Use rs to compute next simplex coord.
 sm = sm + (1-sx).*pr.*s/(i+1); % Update sum
 pr = sx.*pr; % Update product
 x(n-i,:) = sm + pr.*e; % Calculate x using simplex coords.
 s = s - e; j = j - e; % Transition adjustment
end
x(n,:) = sm + pr.*s; % Compute the last x

% Randomly permute the order in the columns of x and rescale.
rp = rand(n,m); % Use rp to carry out a matrix 'randperm'
[ig,p] = sort(rp); % The values placed in ig are ignored
x = (b-a)*x(p+repmat([0:n:n*(m-1)],n,1))+a; % Permute & rescale x

return
Artefacto