Home > Net >  How do you randomly iterate through a 2D array with no duplicates?
How do you randomly iterate through a 2D array with no duplicates?

Time:01-07

You would normally iterate through a 2D array with two for loops:

For i = 0 to 5
   For j = 0 to 5
      Console.Writeline(Arr(i,j))
   Next
Next

I need to iterate through a 2D array (with square edges - same lengths on both side i.e. 5x5 7x7 etc), but I need to do it completely randomly, and with no duplication of the same elements.

For Each i In iRandArr
   For Each j In jRandArr
      Console.Writeline(arr(iRandArr(j), jRandArr(i)))
   Next
Next
'note: the elements i and j are swapped round above in order to maintain the "column", "row" nomenclature

I have attempted to do this already by pushing the lengths of each side to a list, randomizing it (i.e. unsorting it, see here), then iterating through in the same double for loop fashion. Instead of iterating randomly, this method unfortunately iterates semi-randomly by selecting a random "column", and iterating through the "rows" randomly. The only issue with this is that the iteration is bounded by the element of the column, and so is not truly random (in the programmatical sense).

Does anyone have any other possible options I could try?

CodePudding user response:

Here's a fully compiling implementation of the idea from my comment:

  1. Build a one-dimensional set of point objects that represent a location in the 2D array
  2. Randomize the one-dimensional set of points
  3. Iterate the randomized set

Note that the choice of using a custom Point structure is optional; you can use whatever class/struct type you want, or even a Tuple. Same goes for the randomizing algorithm - use whatever you want. The code below is just a fancy LINQ implementation of the same logic from the link in the question.

Option Explicit On
Option Strict On
Option Infer Off
Option Compare Binary

Imports System
Imports System.Collections.Generic
Imports System.Linq

Module Module1

    Sub Main()
        'init an array
        ' this is just for testing purposes;
        ' presumably the 'real' array comes from somewhere else
        Console.WriteLine("Original Array Ordering:")
        Const arrayMaxIndex As Integer = 5
        Dim arr(arrayMaxIndex, arrayMaxIndex) As Integer
        Dim value As Integer = 0
        For row As Integer = 0 To arrayMaxIndex
            For col As Integer = 0 To arrayMaxIndex
                Console.WriteLine($"Row: {row} | Col: {col} | Value: {value}")
                arr(row, col) = value
                value  = 1
            Next
        Next

        'build a 1D list of points that refer to array locations
        ' this is written separately from initializing the 2D array on purpose,
        ' since the assumption is that it will be used on an array of unknown origin
        Dim pointList As New List(Of Point)
        For row As Integer = 0 To arr.GetUpperBound(0)
            For col As Integer = 0 To arr.GetUpperBound(1)
                pointList.Add(New Point(row, col))
            Next
        Next

        'randomize the 1D list
        ' choose whatever randomization algorithm you want; this is just one implementation
        Dim rnd As New Random
        Dim randomizedList As IEnumerable(Of Point) = pointList.OrderBy(Function() rnd.Next())

        'step through the randomized 1D list
        Console.WriteLine()
        Console.WriteLine("Randomized Array Ordering:")
        For Each p As Point In randomizedList
            Console.WriteLine($"Row: {p.Row} | Col: {p.Col} | Value: {arr(p.Row, p.Col)}")
        Next

    End Sub

End Module

'use whatever Point type you want
' Didn't want to hard-code to System.Drawing
Friend Structure Point
    Public Sub New(row As Integer, col As Integer)
        _Row = row
        _Col = col
    End Sub

    Public ReadOnly Property Row As Integer
    Public ReadOnly Property Col As Integer
End Structure

Example Output (illustrating 'randomness' without duplicates):

Original Array Ordering:
Row: 0 | Col: 0 | Value: 0
Row: 0 | Col: 1 | Value: 1
Row: 0 | Col: 2 | Value: 2
Row: 0 | Col: 3 | Value: 3
Row: 0 | Col: 4 | Value: 4
Row: 0 | Col: 5 | Value: 5
Row: 1 | Col: 0 | Value: 6
Row: 1 | Col: 1 | Value: 7
Row: 1 | Col: 2 | Value: 8
Row: 1 | Col: 3 | Value: 9
Row: 1 | Col: 4 | Value: 10
Row: 1 | Col: 5 | Value: 11
Row: 2 | Col: 0 | Value: 12
Row: 2 | Col: 1 | Value: 13
Row: 2 | Col: 2 | Value: 14
Row: 2 | Col: 3 | Value: 15
Row: 2 | Col: 4 | Value: 16
Row: 2 | Col: 5 | Value: 17
Row: 3 | Col: 0 | Value: 18
Row: 3 | Col: 1 | Value: 19
Row: 3 | Col: 2 | Value: 20
Row: 3 | Col: 3 | Value: 21
Row: 3 | Col: 4 | Value: 22
Row: 3 | Col: 5 | Value: 23
Row: 4 | Col: 0 | Value: 24
Row: 4 | Col: 1 | Value: 25
Row: 4 | Col: 2 | Value: 26
Row: 4 | Col: 3 | Value: 27
Row: 4 | Col: 4 | Value: 28
Row: 4 | Col: 5 | Value: 29
Row: 5 | Col: 0 | Value: 30
Row: 5 | Col: 1 | Value: 31
Row: 5 | Col: 2 | Value: 32
Row: 5 | Col: 3 | Value: 33
Row: 5 | Col: 4 | Value: 34
Row: 5 | Col: 5 | Value: 35

Randomized Array Ordering:
Row: 2 | Col: 5 | Value: 17
Row: 3 | Col: 2 | Value: 20
Row: 1 | Col: 0 | Value: 6
Row: 2 | Col: 1 | Value: 13
Row: 4 | Col: 0 | Value: 24
Row: 3 | Col: 5 | Value: 23
Row: 4 | Col: 2 | Value: 26
Row: 5 | Col: 1 | Value: 31
Row: 1 | Col: 5 | Value: 11
Row: 0 | Col: 2 | Value: 2
Row: 4 | Col: 5 | Value: 29
Row: 0 | Col: 5 | Value: 5
Row: 4 | Col: 1 | Value: 25
Row: 3 | Col: 0 | Value: 18
Row: 0 | Col: 4 | Value: 4
Row: 0 | Col: 0 | Value: 0
Row: 1 | Col: 4 | Value: 10
Row: 4 | Col: 3 | Value: 27
Row: 5 | Col: 0 | Value: 30
Row: 2 | Col: 2 | Value: 14
Row: 1 | Col: 1 | Value: 7
Row: 3 | Col: 1 | Value: 19
Row: 2 | Col: 0 | Value: 12
Row: 2 | Col: 4 | Value: 16
Row: 5 | Col: 2 | Value: 32
Row: 0 | Col: 1 | Value: 1
Row: 4 | Col: 4 | Value: 28
Row: 5 | Col: 5 | Value: 35
Row: 1 | Col: 2 | Value: 8
Row: 5 | Col: 4 | Value: 34
Row: 5 | Col: 3 | Value: 33
Row: 1 | Col: 3 | Value: 9
Row: 2 | Col: 3 | Value: 15
Row: 0 | Col: 3 | Value: 3
Row: 3 | Col: 3 | Value: 21
Row: 3 | Col: 4 | Value: 22

P.S. I hope you don't care about performance here; this is really a brute-force implementation.

CodePudding user response:

It's quite easy:

Dim Arr As Integer(,) =
    {{ 11, 12, 13 }, { 21, 22, 23 }, { 31, 32, 33 }}

Dim random As New Random

Dim indices = _
        Enumerable _
            .Range(0, 3) _
            .SelectMany(Function(i) _
                Enumerable _
                    .Range(0, 3) _
                    .Select(Function(j) (i, j))) _
            .OrderBy(Function(x) random.Next())
            
For Each x In indices
    Console.Writeline(Arr(x.i, x.j))
Next

This simply generates all possible indices pairs, and then randomly shuffles them. That ensures no duplicates.

In this example I get:

23
32
22
13
33
12
11
31
21
  •  Tags:  
  • Related