1

I'm programming a 3-dimensional cellular automata. The way I'm iterating through it right now in each generation is:

  1. Create a list of all possible coordinates in the 3D space.
  2. Shuffle the list.
  3. Iterate through the list until all coordinates have been visited.
  4. Goto 2.

Here's the code:

I've a simple 3 integer struct

public class Coordinate
{
    public int x;
    public int y;
    public int z;

    public Coordinate(int x, int y, int z) {this.x = x; this.y = y; this.z = z;}
}

then at some point I do this:

List<Coordinate> all_coordinates = new ArrayList<>();
[...]
for(int z=0 ; z<length ; z++)
{
    for(int x=0 ; x<diameter ; x++)
    {
        for(int y=0 ; y<diameter ; y++)
        {
            all_coordinates.add(new Coordinate(x,y,z));
        }
    }
}

and then in the main algorithm I do this:

private void next_generation() 
{ 
    Collections.shuffle(all_coordinates);
    for (int i=0 ; i < all_coordinates.size() ; i++) 
    {
        [...]
    }
}

The problem is, once the automata gets too large, the list containing all possible points gets huge. I need a way to shuffle through all the points without having to actually store all the possible points in memory. How should I go about this?

Kovalainen
  • 249
  • 1
  • 10
  • How good does the shuffling have to be? For example you could iterate through them by using random prime numbers - e.g. Replace x++ with x = (x + p) % diameter. – cpp beginner Oct 23 '17 at 20:50
  • @cpp-beginner it should be as uniform as possible. How's that method called, does it have a name so I can research further? – Kovalainen Oct 23 '17 at 20:59
  • I don't think it has a specific name beyond modular arithmetic. – cpp beginner Oct 23 '17 at 21:46
  • @cpp-beginner cool, thought maybe it'd have a specific name like Fisher–Yates shuffling – Kovalainen Oct 23 '17 at 21:50
  • It might do but I don't know it. Your question is quite interesting and I bet there's a good answer. If you add some more tags like math and algorithm it might get looked at by people who can help. – cpp beginner Oct 23 '17 at 21:52
  • 1. How many iterations do you contemplate? IOW, how many shuffled sequences will you require? (LCG and friends can generally produce only N different sequences of N items.) – rici Oct 24 '17 at 06:20
  • 2. Is some space available? For example, would 2 bits/cell be possible? 2.5 bits? (Presumably you are already storing the state of every cell..2 more bits per cell would be noticeable, but would it be catastrophic? – rici Oct 24 '17 at 06:23
  • 1
    3. Why do you need a random shuffle? (How could or complete does it have to be. – rici Oct 24 '17 at 06:24

1 Answers1

3

One way to do this is to start by mapping your three dimensional coordinates into a single dimension. Let's say that your three dimensions' sizes are X, Y, and Z. So your x coordinate goes from 0 to X-1, etc. The full size of your space is X*Y*Z. We'll call that S.

To map any coordinate in 3-space to 1-space, you use the formula (x*X) + (Y*y) + z.

Of course, once you generate the numbers, you have to convert back to 3-space. That's a simple matter of reversing the conversion above. Assuming that coord is the 1-space coordinate:

x = coord/X
coord = coord % X
y = coord/Y
z = coord % Y

Now, with a single dimension to work with, you've simplified the problem to one of generating all the numbers from 0 to S in pseudo-random order, without duplication.

I know of at least three ways to do this. The simplest uses a multiplicative inverse, as I showed here: Given a number, produce another random number that is the same every time and distinct from all other results.

When you've generated all of the numbers, you "re-shuffle" the list by picking a different x and m values for the multiplicative inverse calculations.

Another way of creating a non-repeating pseudo-random sequence in a particular range is with a linear feedback shift register. I don't have a ready example, but I have used them. To change the order, (i.e. re-shuffle), you re-initialize the generator with different parameters.

You might also be interested in the answers to this question: Unique (non-repeating) random numbers in O(1)?. That user was only looking for 1,000 numbers, so he could use a table, and the accepted answer reflects that. Other answers cover the LFSR, and a Linear congruential generator that is designed with a specific period.

None of the methods I mentioned require that you maintain much state. The amount of state you need to maintain is constant, whether your range is 20 or 20,000,000.

Note that all of the methods I mentioned above give pseudo-random sequences. They will not be truly random, but they'll likely be close enough to random to fit your needs.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
  • The 3D to 1D mapping seems so obvious in retrospect, that's most likely the way to go. Thanks for the thorough answer – Kovalainen Oct 24 '17 at 07:02
  • Jim and @Kovalainen , couldn't you also use the same integer-mapping function (referred to in the answer as "pseudorandom sequence") with each x, y and z dimension individually rather than first converting them to a single space? – גלעד ברקן Oct 24 '17 at 10:37
  • גלעד ברק: You could, but then you're maintaining three separate pseudo random number generators. Not necessarily a bad thing, but I prefer to convert to one dimension and use just one. You do bring up an interesting point, though. I'm going to add to my answer. – Jim Mischel Oct 24 '17 at 13:08
  • @Kovalainen: See my "additional info", which presents another option. – Jim Mischel Oct 24 '17 at 13:22
  • @גלעדברקן (and @jim-mischel) this is an interesting idea, but if I'm unterstanding it correctly, it'd still be traversing the 3D space somewhat sequentally, right? In your example code, for example, it would traverse a whole 'slice' (as in, every X,Y pair in a given Z coordinate) before moving on to the next one. – Kovalainen Oct 24 '17 at 13:52
  • @Kovalainen and Jim, I thought the whole point was not to keep the points in memory, but for each `next_generation()`, to be able to iterate through a new (pseudo)random order of points, which means using a new integer mapping on the regular, sequential point iteration. My point (pun not intended) was that the integer-mapping could be applied either to xyz space or to each dimension separately. (My understanding is the integer-mapping is `O(1)` per single integer input.) – גלעד ברקן Oct 24 '17 at 18:56
  • @גלעדברקן: Yes. But to do that we'd have to make sure to generate all the numbers in each dimension the requisite number of times, without repeating any x, y, z triplet. My code does that, but it does so in a uniform way, with all the points in one z,x plane, then all the points in the next z,x plane, etc. Your idea is interesting, but I can't see how to use it to generate random x,y,z triplets without duplication. – Jim Mischel Oct 24 '17 at 19:58
  • Why wouldn't `for x = 1 to n; for y = 1 to n; for z = 1 to n` work? I thought the randomness comes from applying the unique integer mapping to the ordered sequence. – גלעד ברקן Oct 24 '17 at 22:30