Daniel B
Daniel B

Reputation: 11

Algorithm for visiting all grid cells in pseudo-random order that has a guaranteed uniformity at any stage

Context:

I have a hydraulic erosion algorithm that needs to receive an array of droplet starting positions. I also already have a pattern replicating algorithm, so I only need a good pattern to replicate.

The Requirements:

I need an algorism that produces a set of n^2 entries in a set of format (x,y) or [index] that describe cells in an nxn grid (where n = 2^i where i is any positive integer).

Note I was/am trying to find solutions through fractal-like patterns built through recursion, but at the time of writing this, my solution is a lookup table of a checkerboard pattern(list of black cells + list of white cells) (which is bad, but yields fewer artifacts than an ordered list)

C, C++, C#, Java implementations (if any) are preferred

Upvotes: 1

Views: 280

Answers (2)

Daniel B
Daniel B

Reputation: 11

I have solved the problem myself and just sharing my solution:

here are my outputs for the i between 0 and 3:

power: 0
ordering:
0
matrix visit order:
0

power: 1
ordering:
0 3 2 1
matrix visit order:
0       3
2       1

power: 2
ordering:
0 10 8 2 5 15 13 7 4 14 12 6 1 11 9 3
matrix visit order:
0       12      3       15
8       4       11      7
2       14      1       13
10      6       9       5

power: 3
ordering:
0 36 32 4 18 54 50 22 16 52 48 20 2 38 34 6
9 45 41 13 27 63 59 31 25 61 57 29 11 47 43 15
8 44 40 12 26 62 58 30 24 60 56 28 10 46 42 14
1 37 33 5 19 55 51 23 17 53 49 21 3 39 35 7
matrix visit order:
0       48      12      60      3       51      15      63
32      16      44      28      35      19      47      31
8       56      4       52      11      59      7       55
40      24      36      20      43      27      39      23
2       50      14      62      1       49      13      61
34      18      46      30      33      17      45      29
10      58      6       54      9       57      5       53
42      26      38      22      41      25      37      21

the code:

public static int[] GetPattern(int power, int maxReturnSize = int.MaxValue)
{
    int sideLength = 1 << power;
    int cellsNumber = sideLength * sideLength;
    int[] ret = new int[cellsNumber];
    for ( int i = 0 ; i < cellsNumber && i < maxReturnSize ; i++ ) {
        // this loop's body can be used for per-request computation
        int x = 0;
        int y = 0;
        for ( int p = power - 1 ; p >= 0 ; p-- ) {
            int temp = (i >> (p * 2)) % 4; //2 bits of the index starting from the begining
            int a = temp % 2; // the first bit
            int b = temp >> 1; // the second bit
            x += a << power - 1 - p;
            y += (a ^ b) << power - 1 - p;// ^ is XOR
            // 00=>(0,0), 01 =>(1,1) 10 =>(0,1) 11 =>(1,0) scaled to 2^p where 0<=p 
        }
        //to index
        int index = y * sideLength + x;
        ret[i] = index;
    }
    return ret;
}

I do admit that somewhere along the way the values got transposed, but it does not matter because of how it works.

After doing some optimization I came up with this loop body:

int x = 0;
int y = 0;

for ( int p = 0 ; p < power ; p++ ) {
    int temp = ( i >> ( p * 2 ) ) & 3;
    int a = temp & 1;
    int b = temp >> 1;

    x = ( x << 1 ) | a;
    y = ( y << 1 ) | ( a ^ b );
}
int index = y * sideLength + x;

(the code assumes that c# optimizer, IL2CPP, and CPP compiler will optimize variables temp, a, b out)

Upvotes: 0

M Oehm
M Oehm

Reputation: 29126

You can use a linear congruential generator to create an even distribution across your n×n space. For example, if you have a 64×64 grid, using a stride of 47 will create the pattern on the left below. (Run on jsbin) The cells are visited from light to dark.

That pattern does not cluster, but it is rather uniform. It uses a simple row-wide transformation where

k = (k + 47) mod (n * n)
x = k mod n
y = k div n

You can add a bit of randomness by making k the index of a space-filling curve such as the Hilbert curve. This will yield the pattern on the right. (Run on jsbin)

      LCG distribution       LCG + Hilbert distribution

You can see the code in the jsbin links.

Upvotes: 3

Related Questions