TransWikia.com

Randomly Choose from list but meet conditions

Mathematica Asked by Kenneth Eaves on June 4, 2021

I have a list of integers from 0-99, but some of the integers have been randomly removed.

I want to pick a random integer that is still in the list, let’s call it ‘a’ but if a+1, a+2, and a+3 are not also in the list I need to pick a new random integer from the list and check again until I get a number that works.

5 Answers

Here is a procedural approach that does not require the list to be sorted.

I will first make some test data:

testlist = RandomSample[Range[0, 99], 50];

Then define a selector function:

testlist = RandomSample[Range[0, 99], 50];

ClearAll[selector]
selector[list_] :=
 TimeConstrained[
  Module[{choice},
   While[! 
     ContainsAll[list, (choice = RandomChoice[list]) + {0, 1, 2}]];
   choice
   ],
  0.5,
  "None found"
  ]

selector[testlist]

Wrapping the function in TimeConstrained ensures that, in case there are no such elements, the function won't get stuck in an endless loop. The return value could be the default ($Aborted), or it could be modified to taste as shown above. For example:

selector[Range[0, 99, 2]]

(* Out: None found *)

Correct answer by MarcoB on June 4, 2021

To create some data for testing we get even numbers <100 and insert one odd number:

d = Table[i, {i, 0, 99, 2}];
d = Insert[d, 33, 18];

Now we can pick out a sequence of 3 integers in a row by:

SequenceCases[d, {x_, y_, z_} /; z == (y + 1) == x + 2, 1]
(*  {{32, 33, 34}}  *)

If you only want the first number, you would say:

SequenceCases[d, {x_, y_, z_} /; z == (y + 1) == x + 2 -> x, 1][[1]]
(* 32 *)

Answered by Daniel Huber on June 4, 2021

Here's a way that constructs a set of candidates by sorting the list and then breaking it into runs of consecutive elements and only keeping elements with enough successors to be viable. This is probably a good approach if you are going to be making multiple draws.

RandomSeed[1337];
test = RandomInteger[{0, 99}, 95]
(* {37, 84, 80, 98, 26, 32, 51, 65, 19, 33, 10, 88, 25, 13,
    77, 68, 20, 39, 27, 83, 89, 75, 78, 87, 22, 58, 94, 49, 
    70, 73, 60, 44, 59, 86, 30, 12, 90, 5, 55, 63, 38, 35, 
    72, 81, 31, 36, 29, 93, 95, 8, 34, 15, 9, 42, 52, 50, 
    48, 4, 92, 71, 56, 2, 69, 54, 74, 91, 45, 64, 0, 82, 
    96, 85, 16, 6, 7} *) 
runs = Split[Sort[test], Subtract /* EqualTo[-1]];

Only runs of length $ > 3 $ will contain elements $ a $ such that $ a + 1 $, $ a + 2 $, and $ a + 3 $ are present:

longRuns = Select[runs, Length /* GreaterThan[3]];

Drop elements that don't have enough successors and flatten.

candidates = Flatten[Drop[#, -3] & /@ longRuns]
(* {4, 5, 6, 7, 29, 30, 31, 32, 33, 34, 35, 36, 48, 49, 68, 
    69, 70, 71, 72, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
    90, 91, 92, 93} *)

Now you can make random draws.

RandomChoice[candidates, 10]
(* {34, 85, 89, 90, 4, 81, 83, 29, 93, 85} *)

Answered by Pillsy on June 4, 2021

First generate a random sample of integers between 0 and 99 (and this can be with or without replacement - but I've used "without replacement" in the example):

nOriginal = 60;
original = RandomSample[Range[0, 99], nOriginal]

Now find all of the available starting numbers (a) that have a+1, a+2, and a+3 available:

available = Select[original,
  MemberQ[original, # + 1] && MemberQ[original, # + 2] && MemberQ[original, # + 3] &]

Now you can select from available with or without replacement as appropriate for your sampling objective.

This approach should work whether or not the original list has duplicate numbers or not. (However, this approach is much slower than that of @Pillsy.)

Answered by JimB on June 4, 2021

Since the input list not too large, we can also play with alternative approaches, among them RelationGraph:

SeedRandom[1]
length = 60;
inputlist = RandomSample[Range[0, 99], length]
{80, 14, 0, 67, 3, 65, 23, 68, 74, 15, 24, 4, 83, 70, 1, 30, 48, 25, 44, 73,
 69, 56, 47, 28, 92, 26, 75, 10, 43, 33, 81, 18, 38, 29, 84, 17, 27, 85, 5, 40,
 82, 22, 2, 39, 36, 20, 99, 46, 52, 54, 35, 9, 63, 42, 95, 89, 98, 49, 64, 45}
rg = RelationGraph[#2 == # + 1 &, inputlist, ImageSize -> Large, 
  VertexSize -> .7, VertexStyle -> White, 
  VertexLabels -> Placed["Name", Center]]

enter image description here

candidates = VertexList[rg, _?(Length[VertexOutComponent[rg, #]] >= 4 &)]
 {80, 0, 67, 23, 24, 1, 25, 44, 26, 43, 81, 27, 82, 22, 2, 46, 42, 45}
HighlightGraph[rg, Style[#, Orange] & /@ candidates]

enter image description here

Use RandomSample or RandomChoice to pick any number of elements from candidates:

SeedRandom[123]
randomselection = RandomSample[candidates, 5]
{2, 80, 67, 25, 44}
HighlightGraph[rg, 
 Join[Style[# + Range[3], Yellow] & /@ randomselection, List /@ randomselection]]

enter image description here

Answered by kglr on June 4, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP