views:

176

answers:

7

I already wrote a random generator which take arguments a and b, where a is minimun and b is maximum value, like this randomGenerator(int a, int b)

What I want to do next is: Using a loop, then generate unique number from a to b. Example:

I want to have 8 unique numbers,
int a = 1;
int b = 10;
int value;

If I do the loop, there is a high % that same number will appear more than once. Any idea how to do it?

My own way is:

while(int i <= 8){
  randomGenerator(a,b);
  // if value is not in array, then insert into array
}

I am stuck at the comment part. Is there any way to check if a variable is exists in an array?

Edit, based on nailxx's answer, what I understand is:

  • take the list from a to b (if follow my example, 1 - 10)

  • "shuffle" it

  • take the first 8 items. Is that what you mean?

In java world, is there a "shuffle" function or I need to create my own?

+9  A: 

Take a list with elements sequentially distributed from a to b, shuffle it and return element with subsequent index on each request.

nailxx
I dont get it.. :(
javaLearner.java
This will work very well if the range is small. If the range is large you wind up allocating an enormous temporary array.
JSBangs
I upvoted your answer, but if your language does not implement "shuffle", it is easy to get it wrong. See http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html for one funny write-up about a spectacular example.
Pascal Cuoq
Suppose `a` is 3 and `b` is 7. Take a list [3, 4, 5, 6, 7]. Shuffle it (for example with Fisher-Yates algorithm as Mark Byers suggests) and you'll get [4, 7, 6, 5, 3]. Than take numbers one by one
nailxx
I think I get it. Thanks =)
javaLearner.java
I think your method is much more efficient than mine, thanks for answering my question.
javaLearner.java
I usually do shuffles by creating an array of objects. I add a member for the information I want in each item. I also add a member to store the result of a random number generator. You then sort the array by the random number. Simple and reliable.
Jay
A: 

Since the size of your sample is almost the same as the size of your rage you could just generate all the integers from 1 to 10, shuffle them then take the first 8 elements.

If the sample size (k) is a lot less than the range size (n) you can use a variation on the Fisher-Yates shuffle where you stop the algorithm after k steps instead of shuffling the entire array.

If the sample size is small but the range size [0,n) is too large to store in memory then you can use yet another variation where you pick first a random number s1 in [0, n). Then you pick a number in [0, n-1) and add one to it if it is equal to or greater than s1, giving s2. Then pick a number in [0, n-2) and add one to it for each of s1, s2 it is greater than or equal to, etc...

Mark Byers
A: 

If your idea is to get 8 random numbers you can always use a dictionary/hash or List (which has a Contains(int i) method) and then convert the Dictionary or List to an Array:

List<int> x = ...;
int[] data = x.ToArray();
Andre
A: 
List<Integer> list=new ArrayList<Integer>(8);

while(int i <= 8){ 
  do {
    Integer n=randomGenerator(a,b); 
  } while (list.contains(n))
  list.add(n);
} 

Of course if there aren't at least 8 distinct values between a and b this will get stuck in an infinite loop. I'd put a trap for that before starting.

With just 8 values a simple ArrayList is probably the easiest thing to use. If you had hundreds or thousands of values, running through all of them for each new entry could be prohibitive, and you might want to do something more sophisticated, like use a HashMap, or keep a separate array of flags of what values were used, or some such.

Jay
A: 

If your range is small (or close to the number of elements you're to select), the solutions proposed above are fine.

If your range gets large, a far more efficient (and clear) way to do it is as follows:


    int[] pickRandom(int numberOfChoices, int lowerBound, int upperBound) {
      Set<Integer> choices = new HashSet<Integer>();
      while (choices.size() <= numberOfChoices) {
        choices.add(yourRandomGenerator (lowerBound, upperBound));
      }
      int[] ret = new int[choices.size()];
      int ii=0;
      for (Integer choice: choices) {
        ret[ii++] = choice.intValue();
      }
      return ret;
    }
CPerkins
A: 

There are some good answers here, but I'd add one important caveat: don't use a List, use a HashSet. Dusting off my Computer Science nerd terminology, checking whether an element already exists in a List runs in O(n) time; doing the same thing with HashSet gives you O(lg n) performance, which is much faster. For a small data set (like 8 numbers between 1 and 10), it's trivial. For a large data set, it can make a difference.

Something like this ought to do it:

    HashSet resultSet = new HashSet();
    while (resultSet.size() < targetSize) {
       resultSet.add(randomGenerator(a,b));
    }

... with a sanity check in your preconditions to make sure your target size doesn't exceed your range, of course.

If the order is important, you stick them in aList after using the HashSet to confirm it's a new number. Or just dump the HashSet into a new List and shuffle it.

BlairHippo
A: 

There's a built in shuffle function in the Collections class.

e.g.


List values = new ArrayList();

for( int i = 1; i < 10; i++ ) {
    values.add(i);
}

Collections.shuffle(values);

Tom