2
$\begingroup$

Is it possible to generate $n$ random samples, and always ensure that $n$ samples are generated?

eg For $n=5$:

n=5; {n1, n2, n3, n4, n5} = (While[Total[set = Reverse@Sort@Round
[(tmp = RandomInteger[100, n])/Total@tmp 100]] != 100]; set);
s = Reverse[Sort[{n1, n2, n3, n4, n5}]];
a = rs[[1]]; b = s[[2]]; c = s[[3]]; d = s[[4]]; e = s[[5]];
PieChart[{a, b, c, d, e}, ImageSize -> 200, 
ChartLabels -> {Style[StringForm["A:``%", a], 10], 
Style[StringForm["B:``%", b], 10], 
Style[StringForm["C:``%", c], 10], 
Style[StringForm["D:``%", d], 10], 
Style[StringForm["E:``%", e], 10]}]

is ok, but for large enough $n$ the numbers are often "used up" before the last few get a chance to sample (so, in the example of the above pie chart, "E" often doesn't feature).

Is it possible to avoid the samples reaching $0$ before last $n$ is reached? (Obviously, by the time $n=100$, each sample should have value of $1$.)

$\endgroup$
  • $\begingroup$ This is very unclear. There is no $n$ in your code. $\endgroup$ – Simon Woods May 26 '14 at 12:09
  • $\begingroup$ Question edited accordingly. $\endgroup$ – martin May 26 '14 at 12:15
  • 1
    $\begingroup$ If your summed value (100 in your example) and n are not too large you could use RandomSample[IntegerPartitions[100, 5], 1]. However, do take care as IntegerPartitions can blow up extremely fast and fill up all of your memory (potentially crashing your kernel) if you're not cautious. $\endgroup$ – Sjoerd C. de Vries May 26 '14 at 12:24
  • $\begingroup$ Great - thank you - will give it a go :) $\endgroup$ – martin May 26 '14 at 12:25
  • $\begingroup$ So you are trying to create a list of n random integers such that Total[list]==100 and Min[list]==1 ? $\endgroup$ – Simon Woods May 26 '14 at 12:25
6
$\begingroup$

Partitioning n into k elements

Here's a way to efficiently use the logic of partitioning from my first attempt (which can be found in the edit history).

Imagine slicing the ordered set of integers at k-1 distinct places between integers 1 and n-1. Then we simply evaluate the size of each slice (in terms of the number of integers contained in the respective interval).

A suggestion by Simon Woods helped streamline the code.

g[n_,k_]:=Differences@Join[{0},Sort@RandomSample[Range[n-1],k-1],{n}]

Examples

g[100, 5] // AbsoluteTiming

{0.000068, {16, 26, 13, 16, 29}}


g[100, 15] // AbsoluteTiming
Total@%[[2]]

{0.000052, {5, 28, 7, 1, 3, 3, 5, 5, 12, 3, 1, 11, 9, 4, 3}}
100


g[100, 35] // AbsoluteTiming
Total@%[[2]]

{0.000063, {1, 1, 6, 2, 5, 1, 2, 1, 1, 8, 4, 1, 3, 1, 1, 2, 6, 4, 5, 1, 5, 1, 4, 1, 2, 12, 4, 2, 4, 1, 1, 1, 2, 1, 3}}
100


g[997, 55] // AbsoluteTiming
Total@%[[2]]

{0.000065, {20, 29, 72, 10, 4, 13, 43, 42, 9, 12, 1, 19, 10, 1, 9, 22, 63, 8, 9, 42, 8, 11, 27, 3, 8, 16, 18, 33, 23, 1, 33, 30, 1, 4, 49, 24, 1, 1, 6, 31, 2, 4, 23, 8, 32, 12, 9, 6, 39, 2, 13, 1, 39, 4, 37}}
997

$\endgroup$
  • 1
    $\begingroup$ +1 This must be the most efficient approach I think. You could write the output more succinctly as Differences@Join[{0}, slices, {n}] $\endgroup$ – Simon Woods May 26 '14 at 14:53
  • $\begingroup$ Thanks for the suggested shortening of code, which I will incorporate. $\endgroup$ – DavidC May 26 '14 at 15:40
6
$\begingroup$

If the problem is to create a list of $n$ random integers such that Total[list]==100 and Min[list]>=1, how about starting with a list of $n$ ones (taking care of the Min[list] >= 1 criterion) and then incrementing a random element of the list $100-n$ times.

n = 50;
list = ConstantArray[1, n];
Scan[list[[#]]++ &, RandomInteger[{1, n}, 100 - n]];

Reverse @ Sort @ list    
(* {4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} *)

Total @ list
(* 100 *)
$\endgroup$
  • $\begingroup$ Thanks for your help - very much appreciated :) $\endgroup$ – martin May 26 '14 at 16:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.