4

Given a sequence of N elements (say std::vector or T*), is there any efficient way to iterate over its elements in random order, visiting each element exactly once. The solution must avoid creating additional array with shuffled indices.

EDIT:

Also we need to be able to track original indices

Oleg Shirokikh
  • 3,447
  • 4
  • 33
  • 61
  • Does that mean you can't have additional space at all, or just can't use a pre-generated permutation? – Leeor Sep 24 '13 at 23:54
  • 1
    Use [`std::shuffle`](http://en.cppreference.com/w/cpp/algorithm/random_shuffle) and then iterate from begin to end. – Praetorian Sep 24 '13 at 23:59
  • Leeor, I'm trying to find a way how to iterate over the elements randomly with no additional memory usage and still be able to track original indices – Oleg Shirokikh Sep 25 '13 at 00:17
  • @user2028058: What if I create an additional array of `bool` flags that mark the elements that I already iterated over. Formally, it is not an "array with shuffled indices". Yet it is still an additional array. Is this allowed? – AnT stands with Russia Sep 25 '13 at 01:28

3 Answers3

15

Not particularly random, but given your restrictions, you've left few options.

A is the array
S is the size of the array
Generate a random prime number (P), such that P > S
Q = P % S
for i = 1 to S
    process A[Q]
    Q = (Q + P) % S
Benjamin Lindley
  • 101,917
  • 9
  • 204
  • 274
  • 1
    Is there a guarantee that Q will go over all the elements of A exactly once? – Oleg Shirokikh Sep 25 '13 at 01:15
  • 1
    @user2028058: Yes, it will. – Benjamin Lindley Sep 25 '13 at 01:27
  • I'm still trying to figure out WHY and HOW this works. I'm not really into math. Thanks! – WoLfulus Jun 09 '15 at 00:04
  • How to avoid linear access? Example: with an array of 10 elements, and P = 839, Q will go down from 9 to 0 in a linear way. What causes this condition to happen? – WoLfulus Jun 09 '15 at 00:24
  • 4
    @WoLfulus: Yes, it will do that. It moves by the same increment every time. You just happened to pick a prime number that made that increment be -1. Like I said, it's not particularly random, but by picking a prime number, you can at least guarantee that it will go through all the numbers. – Benjamin Lindley Jun 09 '15 at 03:29
  • This is perfect for AI in my game. Very fast! and if the search has to give up early it doesn't only check the first half – Farzher Mar 11 '17 at 00:02
  • Brilliant! Perfect for a somewhat random segmentation of parallel computations! – Louis LC Oct 20 '22 at 11:31
9

Use std::random_shuffle, so you code will like this:

std::random_shuffle ( myvector.begin(), myvector.end() );  // in place no extra array
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
   std::cout << ' ' << *it;
CS Pei
  • 10,869
  • 1
  • 27
  • 46
  • Thanks, John. Sorry, I forgot to mention that I need to know the original index of the element that is being processed now. Thanks! – Oleg Shirokikh Sep 25 '13 at 00:06
  • @user2028058: Create a vector of indices to your data and shuffle that instead, then step through the shuffled indices and retrieve the corresponding element of your data. – beldaz Sep 25 '13 at 00:18
  • @beldaz: This would surely work, but the question was if it all possible to avoid additional memory usage (for indices). Thanks – Oleg Shirokikh Sep 25 '13 at 00:20
  • @user2028058: If you want a non-deterministic (random) order you'll at least need to keep track of which elements you've visited before, so I suspect it's impossible to avoid some memory usage. Is this purely theoretical, or can you relax these restrictions? – beldaz Sep 25 '13 at 00:24
  • @beldaz: It's completely practical - in this case I don't care about the "purity" of random generator. But I think you are right, perhaps it's impossible... – Oleg Shirokikh Sep 25 '13 at 00:27
  • @user2028058: Best you can do is a deterministic quasi-random process. I reckon Benjamin's answer is the way to go in that case. http://stackoverflow.com/a/18994414/290182 – beldaz Sep 25 '13 at 01:36
0

Well, I'm not entirely clear on the limits to making extra arrays. But essentially, we randomly generate an index, and then repeat, regenerating if we hit an index we already hit. It's not necessarily efficient. But, I'd hazard a guess that the bound is certainly between O(n^2) and O(n!) (possibly (O(n^n)). With a little work, we could clean that up and get the bound to almost always fall on n^2.

Dylan Lawrence
  • 1,503
  • 10
  • 32