Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

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++;]
share|improve this question
2  
Welcome to the site Daniel. That's a good question. –  Mr.Wizard Jun 30 '12 at 14:28
add comment

3 Answers

up vote 7 down vote accepted

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"}} *)
share|improve this answer
add comment

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]])

share|improve this answer
add comment

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]]
share|improve this answer
    
+1 for the pass-by-reference method! –  István Zachar Jul 1 '12 at 8:45
    
@István Thanks. I decided that taking the OP's question literally made for a much more interesting answer. –  Mr.Wizard Jul 1 '12 at 8:50
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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