The problem is that random numbers don't work how the human brain expects. The article above explains it in detail, but suffice it to say that pure/pseudo random numbers suck for most things a user interacts with. What I wanted was a way to provide selections that are still nondeterministic, but closer to what users expect. The method itself is nothing more than random selection using weighted items. I wanted to keep a feeling of randomness, but make sure more recent selections were less likely to appear. This being Java, the first thing I did was make an interface to hold the object and it's priority:
public interface BetterRandomItem { int getPriority(); void setPriority(int newPriority); }
Then, I made a choice() function (name taken from Python's random.choice) that uses this priority.
public static BetterRandomItem choice(Listitems) { if(items == null || items.size() == 0) throw new IllegalArgumentException("items is null or empty"); // Sum all the priority values int itemSpace = 0; for(int i = 0; i < items.size(); i++) itemSpace += items.get(i).getPriority(); // Pull an int from that space int target = new Random().nextInt(itemSpace + 1); // Match the int to the corresponding item int tmpVal = 0; for(int i = 0; i < items.size(); i++){ tmpVal += items.get(i).getPriority(); if(tmpVal >= target) return items.get(i); } // If that failed, return the last in the list, I guess return items.get(items.size() - 1); }
Not the most complex piece of code ever written, but it'll go a long way to making users a bit happier with 'random' selections. Since putting this in, the emails and comments about my random function sucking have completely stopped.
Note: For my implementation, I kept track of the last 50 images displayed and gave them weights 1-50. Any unseen images were weighted as (num seen images * 10), giving them significantly more weight than those that had been seen.
Another Note: Watch out for int overflows in itemSpace. It would be nice if Random.nextLong took an argument.