6
$\begingroup$

I need a function which splits a sequence of arbitrary length

at x random points and which guarantees that each partition (bin) contains a minimum of y elements

RandomSplit[data_?VectorQ, x_Integer, y_Integer] := "?" 

TakeList or SequenceSplit might be candidates to do this, but I don' t know how to get the partition lengths in a random manner and with the minimum binning size.

An example with 100 elements, 4 partitions and a minimum bin length of 10:

TakeList[Range[100], {10, 10, 70, 10}];

Or

TakeList[Range[100], {37, 21, 31, 10}];

But not

TakeList[Range[100], {5, 15, 50, 30}];
$\endgroup$
5
  • 1
    $\begingroup$ Try: IntegerPartitions[100, {4}, Range[10, 100, 10]] and IntegerPartitions[100, {4}, Range[10, 100]] to see if these meet your needs. The second one has 1906 entries. $\endgroup$
    – Syed
    Commented Sep 11, 2023 at 16:47
  • 2
    $\begingroup$ RandomChoice[IntegerPartitions[100, {4}, Range[10, 100]]] is what I was looking for - please post this as an answer $\endgroup$
    – eldo
    Commented Sep 11, 2023 at 16:56
  • 2
    $\begingroup$ you probably need RandomSample@RandomChoice[IntegerPartitions[100, {4}, Range[10, 100]]]? $\endgroup$
    – kglr
    Commented Sep 11, 2023 at 17:56
  • $\begingroup$ Thank you, @kglr, that seems to work even better $\endgroup$
    – eldo
    Commented Sep 11, 2023 at 18:02
  • $\begingroup$ Will RandomChoice not give a single value? Why use the RandomSample as well? @eldo $\endgroup$
    – Syed
    Commented Sep 12, 2023 at 8:15

2 Answers 2

8
$\begingroup$

The following will split the integer 100 into exactly four partitions, but with numbers restricted to multiples of 10.

IntegerPartitions[100, {4}, Range[10, 100, 10]]

{{70, 10, 10, 10}, {60, 20, 10, 10}, {50, 30, 10, 10}, {50, 20, 20,
10}, {40, 40, 10, 10}, {40, 30, 20, 10}, {40, 20, 20, 20}, {30, 30,
30, 10}, {30, 30, 20, 20}}


The following will partition the integer 100 into four partitions and selects results such that 37 appears in each set at least once.

IntegerPartitions[100, {4}, Range[20, 100]] // 
 Cases[#, {___, 37, ___}] &

{{37, 23, 20, 20}, {37, 22, 21, 20}, {37, 21, 21, 21}}


As another example the integer 100 can be divided using only the prime numbers:

primes = Table[Prime[i], {i, 1, PrimePi[100]}]

{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61,
67, 71, 73, 79, 83, 89, 97}

IntegerPartitions[100, 2, primes]

{{97, 3}, {89, 11}, {83, 17}, {71, 29}, {59, 41}, {53, 47}}

$\endgroup$
2
  • $\begingroup$ Thank you, @Syed, I've always regarded IntegerPartition as a number theory thing. Now I see that it can be useful in other contexts too. $\endgroup$
    – eldo
    Commented Sep 12, 2023 at 8:11
  • $\begingroup$ @eldo You are most welcome. $\endgroup$
    – Syed
    Commented Sep 12, 2023 at 8:12
3
$\begingroup$

FrobeniusSolve

possibilities=Select[FrobeniusSolve[{1, 1, 1, 1}, 100],
    #[[1]]>=10 && #[[2]]>=10 && #[[3]]>=10 && #[[4]]>=10&];

RandomChoice@possibilities

(* {39,22,13,26} *)

Table[RandomChoice@possibilities,10]

(*{
   {42,10,29,19},
   {18,24,48,10},
   {17,37,17,29},
   {30,21,31,18},
   {17,14,43,26},
   {11,25,25,39},
   {41,19,24,16},
   {14,18,15,53},
   {37,22,19,22},
   {25,19,12,44}
  }*) 
possibilities//Short

Length @possibilities

Length@FrobeniusSolve[{1, 1, 1, 1}, 100]

(* 

  {10,10,10,70},{10,10,11,69},<<39707>>,{69,11,10,10},{70,10,10,10}}

  39711 

  176851 

*)

Although this method is much less efficient that the IntegerPartitions method posted by @Syed, and may even come under the heading 'just for fun', it does provide a random sample with a minimum bin length of 10.

For an integer range between 10-100, there are 39711 four-element partitions where the total of each partition is 100.

(For a range 0-100, there are 176851 four-element partitions with a total of 100, and all of these are initially generated by the FrobeniusSolve method).

IntegerPartitions

As shown by @Syed:

(pList=IntegerPartitions[100,{4},Range[10,100]])//Short


(* {{70,10,10,10},{69,11,10,10},<<1902>>,{26,25,25,24},{25,25,25,25} *)

Length@pList

(* 1906 *) 

However, the elements in each sublist produced by InterPartitions are ordered highest-to-lowest. {18,24,48,10}, for example, is not present.

MemberQ[pList, {18,24,48,10}]

(* False *) 

As pointed out by @kglr in a comment, calling RandomSample on a RandomChoice from pList solves this problem.

RandomSample@RandomChoice@pList

(* {38,25,22,15} *)

As most, but not all, of the elements of pList (those of the form {a,b,c,d}) will produce 24 random samples, one would expect the total number of possibilities to be less than 1906x24 (45744). (I'm sure there is a way to mathematically calculate the value).

The FrobeniusSolve method produced a value of 39711 and the 'brute force' method can be used to check that calling RandomSample on the results of IntegerPartitions generates an identical number of possibilities:

Length@Union@((Table[RandomSample[#], 
  1000]&/@IntegerPartitions[100,{4},Range[10,100]])//Catenate)

(* 39711 *)
Union@((Table[RandomSample[#], 1000]&/@IntegerPartitions[100,{4},Range[10,100]])//Catenate)//Short

    (* {{10,10,10,70},{10,10,11,69},<<39707>>,{69,11,10,10},{70,10,10,10}} *)
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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