Alex K
Alex K

Reputation: 11165

Looking for a smarter way to randomly split a 1D range of values

I am in need of a way to randomly split a 1-dimensional range of values (i.e. continuous integers) into k sections. Just using a pseudo-random generator to pick split points would technically get the job done. However it allows for possibility of ranges either being very tiny (conversely very large). I've been looking for a way to generally solve this problem without resorting to hard-coded range limits.

I have found this article. It is concerned with 2d terrain generation. But it faces the same issue and presents a solution. You can see the Polygons section, where the author mentions Lloyd relaxation. What that whole thing derives from is Voronoi diagrams, and it works with 2d ranges. Furthermore, if you look at the algorithm for building the Voronoi diagram that Lloyd relaxation needs, it starts with:

let *(z) be the transformation *(z) = (zx, zy + d(z)), where d(z) is a parabola with minimum at z

Naturally, I don't have parabolas in 1d.

It is not clear to me how to achieve the same results in my case of 1d range. Or maybe there is a different/better approach to the problem?

Upvotes: 2

Views: 831

Answers (1)

Shahbaz
Shahbaz

Reputation: 47503

I didn't get into the details of his code, but what he did with Voronoi diagrams in 2d, then choosing the center of the polygons as new points and remaking the Voronoi diagrams gives me this idea:

1. Randomly select your points
2. Compute midways between your points
    -> The two midways on the two sides of each point, is like
       its Voronoi polygon in the Voronoi diagram
    -> So let's call the range between these two "midways" a Voronoi range!
3. Replace each point by the center of its Voronoi range
4. If you want the values to be less random, loop back to step 2
5. The ranges you are looking for are the Voronoi ranges of the last results.

Let's follow this by an example. For simplicity, let's assume we are working on continuous ranges.

So you start with range [0, 80] and you want to divide it in 15 ranges.

Let's say your 15 random numbers after being sorted are (generated by a C program:)

1 5 12 17 19 21 26 31 38 47 52 54 56 67 71

The midpoints are:

1   5   12   17   19   21   26   31   38   47   52   54   56   67   71
  ^   ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^
  |   |    |    |    |    |    |    |    |    |    |    |    |    |
  3  8.5 14.5  18   20  23.5 28.5 34.5 42.5 49.5  51   55  61.5  69

So your ranges become:

[0, 3], [3, 8.5], [8.5, 14.5], [14.5, 18], [18, 20], 
[20, 23.5], [23.5, 28.5], [28.5, 34.5], [34.5, 42.5], [42.5, 49.5],
[49.5, 51], [51, 55], [55, 61.5], [61.5, 69], [69, 80]

If you want to visualize this, it looks like this (as best I can show it in text):

+..+.....+.....+..+.+...+....+.....+.......+......++...+......+......+.........+

Where the . show numbers from 0 to 80 and + show the edges of Voronoi ranges.

Now, let's apply step 3.

1   5   12   17   19   21   26   31   38   47   52   54   56   67   71
  ^   ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^    ^
  |   |    |    |    |    |    |    |    |    |    |    |    |    |
0 3  8.5 14.5  18   20  23.5 28.5 34.5 42.5 49.5  51   55  61.5  69 80
 ^  ^   ^     ^   ^    ^    ^    ^    ^    ^    ^    ^    ^     ^  ^
 |  |   |     |   |    |    |    |    |    |    |    |    |     |  |
1.5 | 11.5  16.25 19 21.75 26  31.5 38.5  46  50.25 53  58.25 65.25|
   5.75                                                          74.5

So let's see now see how the Voronoi ranges look like with the new points:

1.5  5.75 11.5  16.25  19  21.75 26  31.5 38.5  46  50.25 53  58.25 65.25 74.5
   ^     ^    ^       ^   ^     ^   ^    ^    ^    ^     ^    ^    ^     ^
   |     |    |       |   |     |   |    |    |    |     |    |    |     |
 3.625 8.625 13.875 17.625|  23.875 |   35  42.25 48.125 | 55.625 61.75 69.875
                        20.375    28.75               51.625

Now your ranges are (it's starting to look ugly, but bear with me)

[0, 3.625], [3.625, 8.625], [8.625, 13.875],
[13.875, 17.625], [17.625, 20.375], [20.375, 23.875],
[23.875, 28.75], [28.75, 35], [35, 42.25],
[42.25, 48.125], [48.125, 51.625], [51.625, 55.625],
[55.625, 61.75], [61.75, 69.875], [69.875, 80]

So now let's take a look at how this distribution of points looks like:

+...+....+....+...+..+..+....+.....+.......+....+...+...+.....+.......+........+

Now let's compare the two distributions:

First one 
  |
  V
+..+.....+.....+..+.+...+....+.....+.......+......++...+......+......+.........+
+...+....+....+...+..+..+....+.....+.......+....+...+...+.....+.......+........+
  ^
  |
Second one

Looks better, doesn't it? That's exactly what he has done in the article you found with 2d Voronoi polygons applied to 1d ranges.

(Excuse me for any possible computation errors)

Upvotes: 7

Related Questions