11
$\begingroup$

I am new in programming. I have an array containing, let's say 20 elements and first I want to randomly select two elements from it and record them. Then I will select two elements from the array of twenty elements again and record them. The process continues, let's say 10 times and in these 10 draws, I do not want to repeat any of the two elements that I have already drawn.

I used RandomSample to generate random numbers.

j = 0;
For[i = 0, i < 2, i++,
  Yarray[j] = RandomSample[Range[8], 2];
  Print[Yarray[j]];
  j++;]
$\endgroup$
1
  • 2
    $\begingroup$ Welcome to the site Daniel. That's a good question. $\endgroup$
    – Mr.Wizard
    Jun 30, 2012 at 14:28

3 Answers 3

13
$\begingroup$

You can do this in a number of ways:

  • Use RandomSample to generate a random list with twice the sample size you need and partition:

Example: Draw 5 random pairs from Range[20]:

 Partition[RandomSample[Range[20], 10], 2]
 (*  result:  {{20, 14}, {19, 8}, {5, 1}, {12, 15}, {17, 2}} *)
  • Nest a composite function that draws one pair and deletes it from the base list sequentially:

Example: Draw 5 random pairs from the list of letters {a,...,z}:

samples = {}; 
Nest[( samples = Append[samples, RandomSample[#, 2]]; 
      Complement[#, Flatten@samples]) &,  CharacterRange["a", "z"], 5]; 
samples
(* result: {{"m", "c"}, {"g", "h"}, {"r", "l"}, {"n", "x"}, {"u", "a"}} *)
$\endgroup$
8
$\begingroup$

Given

elements = Table[Unique["el"], {20}]

{el3, el4, el5, el6, el7, el8, el9, el10, el11, el12, el13, el14, \ el15, el16, el17, el18, el19, el20, el21, el22}

What you want is equivalent to

Partition[RandomSample[elements], 2]

{{el22, el17}, {el12, el6}, {el5, el19}, {el7, el18}, {el9, 
  el4}, {el13, el21}, {el11, el3}, {el10, el16}, {el14, el15}, {el8, 
  el20}}

If you want less sets (say 3), you can always do

Partition[RandomSample[elements], 2]~Take~3

{{el4, el18}, {el10, el8}, {el19, el21}}

or more efficiently

Partition[RandomSample[elements, 2 3], 2]

However, if we want to implement your algorithm as you described it (3 times in this example)

Reap[Nest[Complement[#, Sow@RandomSample[#, 2]] &, elements, 3]][[2, 1]]

I'll break this down. Complement[#, Sow@RandomSample[#, 2]] &. This is a function that takes one argument. The argument should be a list with the remaining elements. It takes 2 random elements of those (RandomSample[#, 2]) and Sows them (puts them aside for later recollection). Then, it returns the original list without those 2 elements.

We apply that function to the whole list of elements, 3 times (Nest[function, elements, 3])

Finally, we collect the stuff we put aside in the process (Reap[...][[2, 1]])

$\endgroup$
6
$\begingroup$

Here is one possible solution:

SetAttributes[draw, HoldFirst]

draw[var_, n_] /; n <= Length@var :=
  # &[Take[var, n], var = Drop[var, n]]

Initialize the set:

set = RandomSample@Range@20;

Draw your elements. I use Table rather than the For loop.

Table[
 draw[set, 2],
 {10}
]

Output:

{{7, 16}, {2, 11}, {17, 14}, {9, 12}, {10, 3},
 {15, 13}, {19, 1}, {20, 6}, {8,4}, {18, 5}}

Confirm that set is now empty:

set
{}

So how does this work? Let me break it down. The set of shuffled numbers is stored as an assignment to a Symbol named set. To handle this Symbol itself my function needs a Hold* attribute. I choose HoldFirst:

SetAttributes[draw, HoldFirst]

I then define a function using pattern matching that takes two parameters: var and n, and I include a check on these elements with Condition (/;). I want my function to do two things:

  1. take the first two elements from the set given as var. This is done with Take[var, n]

  2. remove these from set itself. This can be done with var = Drop[var, n]

Since I want to return the result of the first operation, but it must be performed before the second, I use a "pure function" (using Function and Slot): # & which simply returns (only) the first argument it is given, and then give each of the operations above as arguments:

draw[var_, n_] /; n <= Length@var :=
  # &[Take[var, n], var = Drop[var, n]]
$\endgroup$
2
  • $\begingroup$ +1 for the pass-by-reference method! $\endgroup$ Jul 1, 2012 at 8:45
  • $\begingroup$ @István Thanks. I decided that taking the OP's question literally made for a much more interesting answer. $\endgroup$
    – Mr.Wizard
    Jul 1, 2012 at 8:50

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.