0

I have 5 elements which I want to distribute into a 3 row high, 6 column wide table without overlapping.

The positions are designated by [x, y] coordinates.

The position of these elements changes with each tick of a Timer component.

The elements are of the TImage type.

I thought about having an array of points from which a point gets deleted if its chosen during a step-by-step process of assigning the values for each element, but I'm not sure arrays work that way.

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Tallmios
  • 15
  • 5

2 Answers2

5

What you are trying to do is to sample uniformly from a finite set, without replacement.

  1. Create an array containing the 18 possible locations.
  2. Perform a Fisher-Yates shuffle.
  3. Take the first five locations after shuffling.

Since the Fisher-Yates shuffle determines the first value after the first iteration, the second value and the second iteration, and so on, you can abort the shuffle after five iterations. This variation is known as a partial Fisher-Yates shuffle. The performance gain when choosing 5 from 18 is not great, but imagine choosing from a much greater population.

As for your 18 locations, an array containing the first 18 integers will do. Then it's a simple use of div and mod to map onto row and column.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks a lot for your advice, but I'm getting stuck at the array creation. Getting an error that my number of elements differs from the declared number:----- pozice: Array[1..6,1..3] of integer = ((50,210,370,530,690,850),(45,200,355)); – Tallmios Mar 22 '16 at 20:36
  • That makes no sense to me. You need a 1D array of length 18. – David Heffernan Mar 22 '16 at 20:41
  • That array is initialized with 0 to 17. You then shuffle it and take the first 5 entries. Map to row/col with div 6 and mod 6. – David Heffernan Mar 22 '16 at 21:07
  • I understand that I'm supposed to put the locations in it, but which coordinates? – Tallmios Mar 22 '16 at 22:06
  • Do it the way I explained. Or if that's too hard, declare a record with row and col, an array with them all in, and shuffle them. I don't think you understand what a shuffle is yet though. – David Heffernan Mar 22 '16 at 22:41
  • Sorry, I'm just trying to understand. A shuffle changes the position/index number of elements in an array, right? Then I'd choose the first five of the now-shuffled order. I just don't know how to get both x and y coordinates of the 18 positions into an array as 1 location. Is it a multidimensional array? I've never really used arrays that much, my questions must sound dumb. We have Delphi at school and our teacher only showed us the very basics. – Tallmios Mar 23 '16 at 05:49
  • It's a linear array. `type TLocationArray = array [0..17] of Integer`. Then use the div mod trick. Or replace integer with a record containing row/col. – David Heffernan Mar 23 '16 at 07:05
  • In the end I used this type of shuffling. It works fine, but there's a problem wherein some of the images disappear and less images show up, due to their coordinates being the same https://i.gyazo.com/4942f0366cdf8e06d1e4f02ad72bb143.png – Tallmios Mar 28 '16 at 19:25
  • I see. Thank you for your help. I'll mention you as a source in my project's documentation, if you don't mind. As a side note, is this piece of code still considered a Fisher-Yates shuffle? https://i.gyazo.com/5edc5c0dee93f8549c928beaa244ece9.png – Tallmios Mar 29 '16 at 20:57
  • No. That's a non-uniform shuffle. Use Fisher-Yates. – David Heffernan Mar 29 '16 at 21:03
  • Seeing as I cannot use the .exchange function from the example you've given on an array, does the regular swap through an extra variable work? https://i.gyazo.com/7458cdd86a18db1196148e1a9fd51cae.png – Tallmios Mar 29 '16 at 22:04
  • That's wrong too. The code I linked to is simple. Use it. Replace Exchange with a swap. Do you understand how the algo works? – David Heffernan Mar 29 '16 at 22:25
  • If I understood correctly, the correct way of uniformly shuffling is by swapping each element just once with an element from a range of i+1, starting with i:=length(array)-1, I have no idea how to deal with the swap function though. – Tallmios Mar 29 '16 at 22:56
  • Your swap code is fine. Run the for loop from my linked code and replace Exchange with your swap. – David Heffernan Mar 30 '16 at 05:44
  • So finally I came to this: https://i.gyazo.com/eece7549f87c4577d79f72f249ff1c64.png Correct? – Tallmios Mar 30 '16 at 23:37
  • No! Do the exact same as in that link. The argument pass to Random should be i+1! What's wrong with a for loop? – David Heffernan Mar 31 '16 at 05:58
  • Overlooked that, a := random(i+1) and then I swap pozice[a] and pozice[i]. – Tallmios Mar 31 '16 at 06:48
0

When there are much more free space than elements, the simplest possible method is very effective and gives correct distribution:

Pick point at random, if it's already occupied, pick it again, until you get unoccupied cell.

It is simple variation of method frequently used in Monte-Carlo, called rejection sampling (https://en.wikipedia.org/wiki/Rejection_sampling).

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
Yuriy Afanasenkov
  • 1,440
  • 8
  • 12
  • This is actually what I tried once, but I faced a problem when the algorithm did find an overlap that the second element just disappeared without getting assigned a different position, because the whole process could only happen once during the Timer's tick (developing in Delphi). – Tallmios Mar 20 '16 at 22:00
  • @Tallmios Use repeat..until loop inside your timer's event handler, so you find correct position for each new element. – Yuriy Afanasenkov Mar 20 '16 at 22:02
  • @Tallmios Clearly your code is wrong, but you didn't show it. Hence the conceptual answers. I'll bet that everything is mixed into GUI code in a TForm descendent. Properly factored code makes these problems simple. Big spaghetti balls of tangled threads does not. – David Heffernan Mar 20 '16 at 22:32
  • ISTM that this is quite nice for a large population, with little chance of overlap, but not for a population of 18. – Rudy Velthuis Mar 21 '16 at 08:20
  • Rejection sampling is perfectly reasonable here. The cleanest way to perform the rejection is probably to maintain a status array for all 18 candidates. At which point it's not much of a stretch to use a partial shuffle which is probably marginally more elegant. But either approach would be perfectly acceptable to me at least. – David Heffernan Mar 21 '16 at 11:11