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:
- Build a one-dimensional set of point objects that represent a location in the 2D array
- Randomize the one-dimensional set of points
- 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
