Dealing with an Assumption in VB

Category: visual studio vb

Question

Light Weight on Thu, 03 Mar 2016 17:08:26


  Recently I am learning Visual Basic. When I had lessons today, I got an idea or an assumption. I saw students come into the classroom and be randomly seated around which inspired me to think if I can simulate this situation in VB (console app). I assume that the classroom has 16  rows by 14 columns of seats and I also assume that there are precisely 224 students each of whom objectively get a seat at random in this room. If I number each student as "stu001, stu002, ..., stu224", in VB Console App, how to show each student’s coordinate of seat? Maybe I could create a 3-dimension or 2-dimension array? In addition, if these students come to this classroom in the same way again and again, maybe for 10 times, how to get and list the row or column number of the seat on which a specific student each time came to sit within this 10 times?

Replies

Cor Ligthert on Thu, 03 Mar 2016 17:20:12


Wait until you are a little bit further.

Than you learn to create classes (a kind of templates) of students and classrooms. 

From those classes you instance objects for every student and every classroom. 

Those you can arrange in collections. If you have the knowledge it is very easy.

But do n't try to drive a big truck if you have done only your first theoretical lessons.

Professional truck-drivers parks their trucks driving behind while I than think, how is it possible, but that is also only handling the experience they got. 

Frank L. Smith on Thu, 03 Mar 2016 18:34:08


LW,

I wouldn't mind taking this on as weekend project (as a WinForms project though, not Console) if you'd like for me to?

Cyrille Précetti on Thu, 03 Mar 2016 20:49:38


There are many ways to logically do what you're talking about LW.

Nowadays .NET provides interesting stuff to store, retrieve, manipulate information, Array, List(Of)...

Some would say that it's all the same behind the curtain, but  one need to find a way and be done with it... It will be a matter of "personal style".

as an advanced level exercise one could set constraints like the least memory used or the fastest way or... you name it.

You could start of with a simple table to record one period arrival order:

Private periodRandomArrivals(0 To 99) as Integer

Then you can archive that order into a table with a second dimension when a new period has been recorded into periodRandomArrivals  like this (I did only a 100 students, a nice 10x10)

        'Load into RecordOfRandomArrivals at IndexRepetition while preserving the previous periods
        If IndexRepetition > 0 Then
            ReDim Preserve RecordOfRandomArrivals(0 To 99, IndexRepetition)
        End If
        Dim intIndex As Integer

        For intIndex = 0 To 99
            RecordOfRandomArrivals(intIndex, IndexRepetition) = onedRandomArrivals(intIndex)
        Next
        IndexRepetition += 1

It looks something like this for a 10x10 grid, where a new random arrival is generated and stored into the RecordOfRandomArrivals.

This was done with just using tables (old way?). I have found that using List(Of) is really more efficient speedwise.... Is that the experience of someone else?



tommytwotrain on Thu, 03 Mar 2016 22:00:42


Cy,

"This was done with just using tables (old way?). I have found that using List(Of) is really more efficient speedwise.... Is that the experience of someone else?"

I dont know I guess it depends on your list. My gut feeling is... well, do you mean a 2d grid made with a list of lists? Or what do you mean?

I would guess a datatable that is displayed with a datagridview would be fairly easy for Light Weight? Is that what you show in your example or did you draw that grid?

Also, maybe it would to 10 random sittings on a 10 x 10 grid 10 times. See if it correlates to students patterns etc. Have ways to save and display the data etc.

Cyrille Précetti on Thu, 03 Mar 2016 22:12:17


The example are picturebox dynamically created in a grid and looping thru the table to switch the picbox.Image to red.

It is just a one-D table to do the random stuff (I reused code I slapped together in another thread on unique random serie) then the copy to the new index of the 2-D table.

Maybe a stopwatch would be handy to record multiple repeats.

My feeling is that a List(of Integer) could hold the random arrivals, and a  List(of List(of integer)) could be used to hold the archive and be faster.... also the I used brute force in a While End While loop that could be done with a .Contains test....maybe for tomorrow's fun...

Here is the unique random number creation sub:

Private myRandom As New Random

Private Sub GenerateRandomArrivalSequence(ByRef myRandomArrivals() As Integer)
        Dim intIndex As Integer
        Dim tempRandom(0 To 99) As Integer
        Dim tempVal As Integer

        For intIndex = 0 To 99
            'Debug.Print(intIndex & " =" & myRandomArrivals(intIndex))
            tempVal = myRandom.Next(1, 101) - 1
            While tempRandom(tempVal) <> 0
                tempVal = myRandom.Next(1, 101) - 1
            End While
            tempRandom(tempVal) = intIndex + 1
            myRandomArrivals(intIndex) = tempVal
            'Debug.Print(intIndex & " = " & tempVal)
        Next

    End Sub

tommytwotrain on Fri, 04 Mar 2016 00:55:22


Cy,

Oh I see. Well not sure which is faster list vs tables. Sometimes one has to test it to find out?

Here is something with a datatable and datagridview to display the table. I learned this recently so I like how simple it is. It fills the grid by looping the grid and randomly determining if a seat is occupied or not.

'example data table with random fill
Public Class Form5
    Private WithEvents Dgv As New DataGridView With {.Parent = Me, .Location = New Point(30, 30),
        .Dock = DockStyle.Bottom, .Font = New Font("Arial", 10, FontStyle.Bold),
        .RowHeadersVisible = False, .ColumnHeadersVisible = False,
        .AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill}
    Private WithEvents FillButton1 As New Button With {.Parent = Me, .Location = New Point(10, 10), .Text = "Fill"}
    Private Rnd1 As New Random(Now.Millisecond)
    Private Dt1 As New DataTable
    Private GridCols As Integer = 10
    Private GridRows As Integer = 10

    Private Sub Form5_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Size = New Size(300, 350)

        AddDataTable()

        Dgv.DataSource = Dt1

        Form5_Resize(0, Nothing)
    End Sub

    Private Sub AddDataTable()

        For c = 0 To GridCols - 1
            Dt1.Columns.Add(c.ToString)
        Next

        Dim dr As DataRow
        For r = 0 To GridRows - 1
            dr = Dt1.NewRow
            Dt1.Rows.Add(dr)
        Next

    End Sub

    Private Sub FillButton1_Click(sender As Object, e As EventArgs) Handles FillButton1.Click
        For r = 0 To GridRows - 1
            For c = 0 To GridCols - 1
                If Rnd1.Next(0, 2) = 0 Then
                    Dt1.Rows(r)(c) = "X"
                Else
                    Dt1.Rows(r)(c) = " "
                End If
            Next
        Next
    End Sub

    Private Sub Form5_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        Dgv.Height = Me.ClientSize.Height - (FillButton1.Bottom + 20)
    End Sub
End Class

Frank L. Smith on Fri, 04 Mar 2016 19:21:15


  Recently I am learning Visual Basic. When I had lessons today, I got an idea or an assumption. I saw students come into the classroom and be randomly seated around which inspired me to think if I can simulate this situation in VB (console app). I assume that the classroom has 16  rows by 14 columns of seats and I also assume that there are precisely 224 students each of whom objectively get a seat at random in this room. If I number each student as "stu001, stu002, ..., stu224", in VB Console App, how to show each student’s coordinate of seat? Maybe I could create a 3-dimension or 2-dimension array? In addition, if these students come to this classroom in the same way again and again, maybe for 10 times, how to get and list the row or column number of the seat on which a specific student each time came to sit within this 10 times?

LW,

I've put something together - strictly in code - that I'm hoping you'll be interested in and want to learn from (and thus, ask questions about).

It's a class library and you can see the code for it on a page of my website here.

Don't let any of that throw you; it's really not *that* tough at all and I'l explain anything that you don't understand, but in essence there are two main classes that do the work in this and I'll show you by example what they do; we can talk about what's behind it later if you want.

The code that I used to test it with is shown below:

Option Strict On Option Explicit On Option Infer Off Imports ClassroomsAndStudents.SeatingArrangment Public Class Form1 Private _classroom As ClassroomSeating Private _students As Students Private Sub Form1_Load(sender As System.Object, _ e As System.EventArgs) _ Handles MyBase.Load _classroom = New ClassroomSeating _classroom.CreateSeats(14, 20) Stop _students = New Students _students.AssignStudentSeats(20, _classroom) Stop Dim info As ClassroomSeating.CollectionItems = _ _students.GetStudentSeatingInfo("Student0001", _classroom) Stop End Sub End Class


It starts with the assignment for the seats in a classroom. You can have up to 99 rows and 99 columns, so it'd be a big classroom! ;-)

Once the seats for a classroom have been established, I can now proceed to the students in that classroom and that can be from one to the maximum number of seats that the classroom has.

In the code above, you see the word "Stop" several times. When the code gets there, execution will halt (as with a breakpoint) and if you hover your mouse over any variable which is in scope, you can see the value of it.

I'll start by setting up the classroom:

_classroom = New ClassroomSeating
_classroom.CreateSeats(14, 20)

Stop

I've told it to set up the seats for 14 rows and 20 columns (280) seats. When it gets to "Stop", I hover my mouse over the class-scoped variable and I can see the results:

If I drill down a bit further, I can see the details:

So that seat has been assigned a position and the name of the seat has been given based on the row and column numbers. Now I'll set up the students for that classroom:

_students = New Students
_students.AssignStudentSeats(20, _classroom)

Stop

You can see there will be 20 students in that class so obviously most of the seats are empty. When I look at the the properties of this one, I see the following:

I've also added in a method so that you can get some more specific information for a given student:

Dim info As ClassroomSeating.CollectionItems = _
    _students.GetStudentSeatingInfo("Student0001", _classroom)

Stop

Looking at that one, I see the info that I wanted:

If you want, I'll zip up the project folder and upload it for you and post a link here. Also, if you want me to help, we can set up a UI to use this so that it's interactive.

I hope this might give you food for thought, and if you're interested, I'll explain every aspect of it.

:)

Cyrille Précetti on Fri, 04 Mar 2016 21:35:24


For what it's worth I setup a stopwatch to evaluate the difference between going old-style with creating unique random arrays or with List(Of) but I had to go to .Ticks as it breached the ms only for a 16x16 grid...so I lost interest... :)

I still believe List(Of) would be more efficient though as I had the experience with handling large datasets...

On the other hand I found an interesting side effect when trying to update 3500+ picturebox, where the update pooled after halfway and suddenly caugth up with the UI. Maybe I'll post a Thread question at some point...

tommytwotrain on Fri, 04 Mar 2016 22:31:59


Cy,

Do you mean a basic 2d array of double say? I thought you meant a datatable vs list of lists.

I will guess a 2d array will be faster vs list of list.

I think you have to get well up there in the repititions and or size to time it. So thats part of the answer, when does it matter? Until then its conveinence and now that I have learned how to use lists a little I like them. As long as I remember to put the NEW in the declare.

Cyrille Précetti on Fri, 04 Mar 2016 22:55:38


Nah! never ever considered database.... how would I say it... clunky?

List(Of) are really awesome for the .Contains, .Sort, .Reverse, .Find methods

I did some image processing with List(Of) and it beat basic arrays... but maybe my algorithms were not as optimized as the List(Of) methods internals...

dbasnett on Sat, 05 Mar 2016 15:01:12


Maybe when you are further along something like this might interest you.

''' <summary>
''' a seat
''' </summary>
''' <remarks></remarks>
Public Class Seat
    Public WhichRow As Integer
    Public SeatInRow As Integer
    Public SeatID As Integer
    Public StudentInSeat As String = ""
End Class

Public Class ClassRoom

    Private SeatsInRoom As New List(Of Seat)
    Private SeatsOccupied As Integer = 0
    Private Shared prng As New Random

    Public Sub New(rowsCount As Integer, seatsPerRow As Integer)
        Dim ids As List(Of Integer) = Enumerable.Range(1, rowsCount * seatsPerRow).ToList
        'generate all seats as a list
        For y As Integer = 1 To rowsCount
            For x As Integer = 1 To seatsPerRow
                Dim aSeat As New Seat
                With aSeat
                    .WhichRow = y
                    .SeatInRow = x
                    .SeatID = ids(0)
                End With
                Me.SeatsInRoom.Add(aSeat)
                ids.RemoveAt(0)
            Next
        Next
    End Sub

    ''' <summary>
    ''' IEnumerable of available seats
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Iterator Function AvailableSeats() As IEnumerable(Of Seat)
        Dim seatsAvl As IEnumerable(Of Seat)
        seatsAvl = From aseat In Me.SeatsInRoom
                   Where aseat.StudentInSeat = ""
                   Select aseat

        For Each st As Seat In seatsAvl
            Yield st
        Next
    End Function

    ''' <summary>
    ''' IEnumerable of assigned seats
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Iterator Function AssignedSeats() As IEnumerable(Of Seat)
        Dim seatsAvl As IEnumerable(Of Seat)
        seatsAvl = From aseat In Me.SeatsInRoom
                   Where aseat.StudentInSeat <> ""
                   Select aseat

        For Each st As Seat In seatsAvl
            Yield st
        Next
    End Function

    ''' <summary>
    ''' 
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks>return seats in row order</remarks>
    Public Iterator Function AllSeats() As IEnumerable(Of Seat)
        Dim seatsAvl As IEnumerable(Of Seat)
        seatsAvl = From aseat In Me.SeatsInRoom
                   Select aseat

        Dim lastRow As Integer = -1
        Me._rowChanged = True
        For Each st As Seat In seatsAvl
            If st.WhichRow = lastRow Then
                Me._rowChanged = False
                Yield st
            Else
                Me._rowChanged = True
                lastRow = st.WhichRow
                Yield st
            End If
        Next
        Me._rowChanged = False
    End Function

    ''' <summary>
    ''' class is over, no students in any seat
    ''' </summary>
    ''' <remarks></remarks>
    Public Sub ClassEnded()
        'class is over, no students in any seat
        For Each st As Seat In Me.AssignedSeats
            st.StudentInSeat = ""
        Next
        Me.SeatsOccupied = 0
    End Sub

    Private stpw As New Stopwatch
    Public Sub AssignRandomSeats(numberOfStudents As Integer)
        Me.stpw.Restart()
        If Me.SeatsInRoom.Count - Me.SeatsOccupied >= numberOfStudents Then
            For Each st As Seat In Me.AvailableSeats.OrderBy(Function() prng.Next).Take(numberOfStudents)
                st.StudentInSeat = "Stu_" & st.SeatID.ToString
            Next
            Me.SeatsOccupied += numberOfStudents
        Else
            'can't seat them all
        End If
        Me.stpw.Stop()
        Debug.WriteLine((stpw.ElapsedTicks / TimeSpan.TicksPerMillisecond).ToString("n2"))
    End Sub

    Public Sub AssignAllSeatsRandom()
        Me.stpw.Restart()
        Me.SeatsOccupied = 0
        For Each st As Seat In Me.AllSeats
            If prng.Next(2) = 0 Then
                st.StudentInSeat = "Stu_" & st.SeatID.ToString
                Me.SeatsOccupied += 1
            Else
                st.StudentInSeat = ""
            End If
        Next
        Me.stpw.Stop()
        Debug.WriteLine((stpw.ElapsedTicks / TimeSpan.TicksPerMillisecond).ToString("n2"))
    End Sub

    Private _rowChanged As Boolean = False
    ''' <summary>
    ''' used by SeatingChart
    ''' </summary>
    ''' <value></value>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public ReadOnly Property RowChanged() As Boolean
        Get
            Return Me._rowChanged
        End Get
    End Property

    Public Function SeatingChart() As DataTable
        Dim rv As New DataTable
        Dim maxSeatInRow As Integer = Me.AllSeats.Max(Function(m) m.SeatInRow)
        For seatsPerRow As Integer = 1 To maxSeatInRow
            Dim dc As DataColumn = New DataColumn(seatsPerRow.ToString, GetType(System.String))
            dc.Caption = seatsPerRow.ToString
            rv.Columns.Add()
        Next

        Dim curCol As Integer = 0
        For Each st As Seat In Me.AllSeats
            If Me.RowChanged Then
                Dim curRW As DataRow = rv.NewRow
                curCol = 0
                rv.Rows.Add(curRW)
            End If
            rv(rv.Rows.Count - 1)(curCol) = st.StudentInSeat
            curCol += 1
        Next
        Return rv
    End Function

    Public Sub SeatingChart(g As Graphics)
        Dim maxSeatInRow As Integer = Me.AllSeats.Max(Function(m) m.SeatInRow) - 1

        Dim curCol As Integer = 0
        Dim boxSZ As Integer = 3

        For Each st As Seat In Me.AllSeats
            If Me.RowChanged Then
                curCol = 0
            End If
            Dim r As New Rectangle(curCol * boxSZ, (st.WhichRow - 1) * boxSZ, boxSZ, boxSZ)
            If st.StudentInSeat = "" Then
                'avail
                g.FillRectangle(Brushes.Green, r)
            Else
                'occu
                g.FillRectangle(Brushes.Red, r)
            End If
            g.DrawRectangle(Pens.Black, r)
            curCol += 1
        Next
    End Sub
End Class


If you want to see it in action create a form with a Button, PictureBox, and a DataGridView.

Public Class Form1

    Dim myClassRoom As New ClassRoom(14, 24)

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Debug.WriteLine("")
        myClassRoom.AssignRandomSeats(200) '200 students walked in
        'myClassRoom.AssignRandomSeats(10) 'and then 10 more
        Dim chart As DataTable = myClassRoom.SeatingChart
        DataGridView1.SuspendLayout()
        DataGridView1.DataSource = Nothing
        DataGridView1.DataSource = chart
        DataGridView1.ResumeLayout()
        Dim bm As Bitmap = New Bitmap(PictureBox1.Width, PictureBox1.Height)
        Using g As Graphics = Graphics.FromImage(bm)
            myClassRoom.SeatingChart(g)
        End Using
        PictureBox1.Image = bm
    End Sub
End Class 'add above classes here

edited in place


"Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it." - MSDN User JohnWein    Multics - An OS ahead of its time.


tommytwotrain on Sun, 06 Mar 2016 04:43:39


Nah! never ever considered database.... how would I say it... clunky?

List(Of) are really awesome for the .Contains, .Sort, .Reverse, .Find methods

I did some image processing with List(Of) and it beat basic arrays... but maybe my algorithms were not as optimized as the List(Of) methods internals...

For fun I made a speed test of the datatable vs list of list. The example draws a grid to show the values being changed randomly.

In this test the list of list performs 4 times faster than the datatable.

So you were right Cyrille!

The test generates a random 100 x 100 grid and is run 100 times. List of list is around 1 sec and datatable 4 sec. The grid is draw on a picturebox and updates each grid loop so you can see it running. The time is in the form header.

'data table list of list comparison v3
Option Strict On
Public Class Form6
    Private WithEvents FillButton As New Button With {.Parent = Me, .Location = New Point(10, 10), .Text = "Fill"}
    Private WithEvents TestButton As New Button With {.Parent = Me, .Location = New Point(200, 10), .Text = "Test"}
    Private WithEvents DataTableRB As New RadioButton With {.Parent = Me, .Location = New Point(100, 0), .Text = "Data Table"}
    Private WithEvents ListRB As New RadioButton With {.Parent = Me, .Location = New Point(100, 22), .Text = "List of Lists"}
    Private WithEvents GridPic As New PictureBox With {.Parent = Me, .Location = New Point(0, 0), .Dock = DockStyle.Bottom}
    Private Rnd1 As New Random(Now.Millisecond)
    Private Dt1 As New DataTable
    Private GridCols As Integer = 100
    Private GridRows As Integer = 100
    Private GridList As New List(Of List(Of String))
    Private SW As New Stopwatch
    Private ReadytoPaint As Boolean

    Private Sub Form5_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Size = New Size(300, 350)
        DataTableRB.Checked = True
        Form5_Resize(0, Nothing)
    End Sub

    Private Sub SetupListOfLists()
        For r = 0 To GridRows - 1
            Dim rowlist As New List(Of String)
            For c = 0 To GridCols - 1
                rowlist.Add(" ")
            Next
            GridList.Add(rowlist)
        Next
    End Sub

    Private Sub SetupDataTable()
        If Dt1.Columns.Count = 0 Then
            For c = 0 To GridCols - 1
                Dt1.Columns.Add(c.ToString)
            Next
        End If

        Dim dr As DataRow

        For r = 0 To GridRows - 1
            dr = Dt1.NewRow
            Dt1.Rows.Add(dr)
        Next
    End Sub

    Private Sub FillGrid()
        'fill the dt or list
        For r = 0 To GridRows - 1
            Dim s As New List(Of String)

            For c = 0 To GridCols - 1
                If Rnd1.Next(0, 2) = 0 Then
                    If DataTableRB.Checked Then
                        Dt1.Rows(r)(c) = "X"
                    Else
                        s.Add("X")
                    End If
                Else
                    If DataTableRB.Checked Then
                        Dt1.Rows(r)(c) = " "
                    Else
                        s.Add(" ")
                    End If
                End If
            Next
            If Not DataTableRB.Checked Then GridList(r) = s
        Next
        GridPic.Refresh()
        Text = SW.ElapsedMilliseconds.ToString
    End Sub

    Private Sub TestButton_Click(sender As Object, e As EventArgs) Handles TestButton.Click
        ReadytoPaint = True
        SW.Reset()
        SW.Start()

        For i = 0 To 100
            FillGrid()
        Next

        SW.Stop()
        Text = SW.ElapsedMilliseconds.ToString
    End Sub

    Private Sub FillButton_Click(sender As Object, e As EventArgs) Handles FillButton.Click
        FillGrid()
        ReadytoPaint = True
    End Sub

    Private Sub Form5_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        GridPic.Height = Me.ClientSize.Height - (FillButton.Bottom + 20)
        GridPic.Invalidate()
    End Sub

    Private Sub DataTableRB_CheckedChanged(sender As Object, e As EventArgs) Handles _
            DataTableRB.CheckedChanged, ListRB.CheckedChanged

        If DataTableRB.Checked Then
            If Dt1 IsNot Nothing AndAlso Dt1.Rows.Count > 0 Then
                Dt1.Clear()
            Else
                SetupDataTable()
            End If
        Else
            GridList.Clear()
            SetupListOfLists()
        End If
    End Sub

    Private Sub GridPic_Paint(sender As Object, e As PaintEventArgs) Handles GridPic.Paint
        Dim dx As Integer = CInt(GridPic.ClientSize.Width / GridCols)
        Dim dy As Integer = CInt(GridPic.ClientSize.Height / GridRows)
        Dim rect As New Rectangle(0, 0, dx, dy)
        Dim s2 As New List(Of String)
        Dim value As String
        If Not ReadytoPaint Then Exit Sub

        If DataTableRB.Checked Then
            If Dt1 Is Nothing Or Dt1.Rows.Count = 0 Then Exit Sub
        Else
            If GridList Is Nothing Or GridList.Count = 0 Then Exit Sub
        End If

        For r = 0 To GridRows - 1
            If Not DataTableRB.Checked Then s2 = GridList(r)

            For c = 0 To GridCols - 1
                If DataTableRB.Checked Then
                    value = CType(Dt1.Rows(r)(c), String)
                Else
                    value = s2(c)
                End If
                rect.X = c * dx
                rect.Y = r * dy
                If value.ToString = "X" Then
                    e.Graphics.FillRectangle(Brushes.Blue, rect)
                Else
                    e.Graphics.FillRectangle(Brushes.White, rect)
                End If
            Next
        Next
    End Sub
End Class



dbasnett on Sun, 06 Mar 2016 13:07:40


Tommy - I edited my code above to assign all seats randomly, much as you did.  I changed the 'class' to be 100 x 100 and timed just the assignment of seats.  It varied from .5 - 1.0 ms. I guess that has something to do with my use of Iterators and Lamdas.

tommytwotrain on Sun, 06 Mar 2016 14:28:25


Tommy - I edited my code above to assign all seats randomly, much as you did.  I changed the 'class' to be 100 x 100 and timed just the assignment of seats.  It varied from .5 - 1.0 ms. I guess that has something to do with my use of Iterators and Lamdas.

"Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it." - MSDN User JohnWein    Multics - An OS ahead of its time.

Hmmm. Guess I will try it. Since I don't understand your code very much I have not tried it yet.

I looked quickly I don't see if you draw the grid you get?

In my example much of the time elapsed is for drawing the grid on the picturebox. Not the processing of the data. But the two methods tested both use the same code to draw the screen with each grid it does (100 times).

I am going to play with your example today but have to do chores first.

I also want to do a case with just the old 2d Arrays.

I guess I will remove the drawing part from my example and see.... later....

tommytwotrain on Sun, 06 Mar 2016 14:37:15


Tommy - I edited my code above to assign all seats randomly, much as you did.  I changed the 'class' to be 100 x 100 and timed just the assignment of seats.  It varied from .5 - 1.0 ms. I guess that has something to do with my use of Iterators and Lamdas.


"Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it." - MSDN User JohnWein    Multics - An OS ahead of its time.

Hmmm. Guess I will try it. Since I don't understand your code very much I have not tried it yet.

I looked quickly I don't see if you draw the grid you get?

In my example much of the time elapsed is for drawing the grid on the picturebox. Not the processing of the data. But the two methods tested both use the same code to draw the screen with each grid it does (100 times).

I am going to play with your example today but have to do chores first.

I also want to do a case with just the old 2d Arrays.

I guess I will remove the drawing part from my example and see.... later....

Yup. I removed the drawing part and the list of lists took less than 100 ms and the datatable took over 3 secs.

So I guess in my example the drawing part takes around 900 ms of the total or 9 ms each time it draws the screen (form size 300 x 300 pixels.

PS That is a factor of around 30 times faster for the list of list over data table!!!

PSS Assuming there is no error somewhere.  :)

Cyrille Précetti on Sun, 06 Mar 2016 15:07:02


Cool test... :)

I'll have a look for the fun of it.

Oh I see a big slow down with using a tabe of radiobutton for the occupied value...hmmmIf you don't mind I'll take your code and inject my table Fill version.

Also, I had half a mind of testing with increasing grid size, and plotting it alongside the picture... to see if there is a point where the List(Of) optimization kick in...

I've read somewhere that some of the algorithm behind the sort/find and such change depending on the size...we'll see...

Later

dbasnett on Sun, 06 Mar 2016 15:16:25


I am going to play with your example today but have to do chores first.

Let me know if I can answer any questions.  It isn't really complicated.

tommytwotrain on Sun, 06 Mar 2016 15:19:14


Cool test... :)

I'll have a look for the fun of it.

Oh I see a big slow down with using a tabe of radiobutton for the occupied value...hmmmIf you don't mind I'll take your code and inject my table Fill version.

Also, I had half a mind of testing with increasing grid size, and plotting it alongside the picture... to see if there is a point where the List(Of) optimization kick in...

I've read somewhere that some of the algorithm behind the sort/find and such change depending on the size...we'll see...

Later

No I don't mind. That's why I made it.

Just remove these lines from sub fillgrid to take out the graphics part.

        'GridPic.Refresh()
        'Text = SW.ElapsedMilliseconds.ToString

PS In fact in the example I had to use .refresh instead of .invalidate to slow it down enough to see the screen updates somewhat.

tommytwotrain on Sun, 06 Mar 2016 18:11:36


I am going to play with your example today but have to do chores first.

Let me know if I can answer any questions.  It isn't really complicated.

"Those who use Application.DoEvents() have no idea what it does and those who know what it does never use it." - MSDN User JohnWein    Multics - An OS ahead of its time.

db,

I have it working now. Very nice.

Frank L. Smith on Sun, 06 Mar 2016 19:55:08


I really thought that this is more of what he had in mind (barring the fact that he wanted a console application):

I'll put some data in it and fill it using the button:

I could work on making it look better, but you get the idea. Now I'll set it to not have as many students as their are seats:

Then I'll fill it again using the button (but the same data so that it gets the random seating):

Form's Code:

Option Strict On Option Explicit On Option Infer Off Imports ClassroomsAndStudents.SeatingArrangment Public Class Form1 Private _classroom As ClassroomSeating Private _students As Students Private Sub Form1_Load(sender As System.Object, _ e As System.EventArgs) _ Handles MyBase.Load btn_Create.Enabled = False With nud_Columns .Minimum = 1 .Maximum = 99 .Value = 1 End With With nud_Rows .Minimum = 1 .Maximum = 99 .Value = 1 End With With nud_Students .Minimum = 0 .Maximum = nud_Columns.Value * nud_Rows.Value .Value = 0 End With With TableLayoutPanel1 .Visible = False .AutoScroll = True .GrowStyle = TableLayoutPanelGrowStyle.AddRows .CellBorderStyle = TableLayoutPanelCellBorderStyle.Single End With End Sub Private Sub _ nud_ValueChanged(sender As System.Object, _ e As System.EventArgs) _ Handles nud_Columns.ValueChanged, _ nud_Rows.ValueChanged, _ nud_Students.ValueChanged With TableLayoutPanel1 .Visible = False .RowCount = 0 .ColumnCount = 0 End With _classroom = Nothing _students = Nothing Dim nud As NumericUpDown = DirectCast(sender, NumericUpDown) If nud.Name = "nud_Columns" OrElse nud.Name = "nud_Rows" Then With nud_Students .Value = 0 .Maximum = nud_Columns.Value * nud_Rows.Value End With End If If nud_Students.Value > 0 Then btn_Create.Enabled = True Else btn_Create.Enabled = False End If End Sub Private Sub _ btn_Create_Click(sender As System.Object, _ e As System.EventArgs) _ Handles btn_Create.Click _classroom = New ClassroomSeating _classroom.CreateSeats(CInt(nud_Rows.Value), CInt(nud_Columns.Value)) _students = New Students _students.AssignStudentSeats(CInt(nud_Students.Value), _classroom) With TableLayoutPanel1 .Visible = False .Controls.Clear() .RowCount = 0 .ColumnCount = 0 End With With TableLayoutPanel1 .RowCount = CInt(nud_Rows.Value) .ColumnCount = CInt(nud_Columns.Value) End With For r As Integer = 0 To TableLayoutPanel1.RowCount - 1 For c As Integer = 0 To TableLayoutPanel1.ColumnCount - 1 Dim lbl As New Label _ With {.Tag = String.Format("Seat R{0:00}C{1:00}", r + 1, c + 1), _ .Dock = DockStyle.Fill, _ .Text = "Unoccupied", _ .TextAlign = ContentAlignment.MiddleCenter} TableLayoutPanel1.Controls.Add(lbl, c, r) Next Next Dim columnStyles As TableLayoutColumnStyleCollection = _ TableLayoutPanel1.ColumnStyles Dim rowStyles As TableLayoutRowStyleCollection = _ TableLayoutPanel1.RowStyles For Each style As ColumnStyle In columnStyles With style .SizeType = SizeType.Absolute .Width = 75 End With Next For Each style As RowStyle In rowStyles With style .SizeType = SizeType.Absolute .Height = 20 End With Next For Each student As Students.CollectionItems In _students For Each ctrl As Control In TableLayoutPanel1.Controls If ctrl.Tag IsNot Nothing Then If CStr(ctrl.Tag) = student.SeatName Then Dim lbl As Label = DirectCast(ctrl, Label) lbl.Text = student.StudentName Exit For End If End If Next Next TableLayoutPanel1.Visible = True End Sub End Class


tommytwotrain on Sun, 06 Mar 2016 21:15:51


Here I added a basic 2d array and removed the graphics from the test. Now it is doing a 1000 grids per test.

 array 400 ms

 list of list 480 ms

 dt 35000 ms

 
'data table - list of list - 2d array comparison v4
Option Strict On
Public Class Form9
    Private WithEvents FillButton As New Button With {.Parent = Me, .Location = New Point(10, 10), .Text = "Fill"}
    Private WithEvents TestButton As New Button With {.Parent = Me, .Location = New Point(200, 10), .Text = "Test"}
    Private WithEvents DataTableRB As New RadioButton With {.Parent = Me, .Location = New Point(100, 0), .Text = "Data Table"}
    Private WithEvents ListRB As New RadioButton With {.Parent = Me, .Location = New Point(100, 22), .Text = "List of Lists"}
    Private WithEvents ArrayRB As New RadioButton With {.Parent = Me, .Location = New Point(100, 42), .Text = "2d Array"}
    Private WithEvents GridPic As New PictureBox With {.Parent = Me, .Location = New Point(0, 0), .Dock = DockStyle.Bottom}
    Private Rnd1 As New Random(Now.Millisecond)
    Private Dt1 As New DataTable
    Private GridCols As Integer = 100
    Private GridRows As Integer = 100
    Private GridList As New List(Of List(Of String))
    Private SW As New Stopwatch
    Private ReadytoPaint As Boolean
    Private GridArray(GridCols, GridRows) As String

    Private Sub Form5_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Size = New Size(300, 320)
        DataTableRB.Checked = True
        Form5_Resize(0, Nothing)
    End Sub

    Private Sub SetupListOfLists()
        For r = 0 To GridRows - 1
            Dim rowlist As New List(Of String)
            For c = 0 To GridCols - 1
                rowlist.Add(" ")
            Next
            GridList.Add(rowlist)
        Next
    End Sub

    Private Sub SetupDataTable()
        If Dt1.Columns.Count = 0 Then
            For c = 0 To GridCols - 1
                Dt1.Columns.Add(c.ToString)
            Next
        End If

        Dim dr As DataRow

        For r = 0 To GridRows - 1
            dr = Dt1.NewRow
            Dt1.Rows.Add(dr)
        Next
    End Sub

    Private Sub FillGrid()
        Dim char1 As String

        'fill the grid
        For r = 0 To GridRows - 1
            Dim s As New List(Of String)

            For c = 0 To GridCols - 1
                If Rnd1.Next(0, 2) = 0 Then char1 = "X" Else char1 = " "
                If DataTableRB.Checked Then
                    Dt1.Rows(r)(c) = char1
                ElseIf ListRB.Checked Then
                    s.Add(char1)
                Else
                    GridArray(r, c) = char1
                End If
            Next
            If Not DataTableRB.Checked Then GridList(r) = s
        Next
        'GridPic.Refresh()    'activate this refresh to draw the grids  - slows results
        Text = SW.ElapsedMilliseconds.ToString
    End Sub

    Private Sub TestButton_Click(sender As Object, e As EventArgs) Handles TestButton.Click
        ReadytoPaint = True
        SW.Reset()
        SW.Start()

        For i = 0 To 1000     'change this for number of tests
            FillGrid()
        Next

        SW.Stop()
        Text = SW.ElapsedMilliseconds.ToString
    End Sub

    Private Sub FillButton_Click(sender As Object, e As EventArgs) Handles FillButton.Click
        FillGrid()
        ReadytoPaint = True
        GridPic.Invalidate()
    End Sub

    Private Sub Form5_Resize(sender As Object, e As EventArgs) Handles Me.Resize
        GridPic.Height = Me.ClientSize.Height - (ArrayRB.Bottom + 5)
        GridPic.Invalidate()
    End Sub

    Private Sub DataTableRB_CheckedChanged(sender As Object, e As EventArgs) Handles _
                DataTableRB.CheckedChanged, ListRB.CheckedChanged

        If DataTableRB.Checked Then
            If Dt1 IsNot Nothing AndAlso Dt1.Rows.Count > 0 Then
                Dt1.Clear()
            Else
                SetupDataTable()
            End If
        Else
            GridList.Clear()
            SetupListOfLists()
        End If
    End Sub

    Private Sub GridPic_Paint(sender As Object, e As PaintEventArgs) Handles GridPic.Paint
        If Not ReadytoPaint Then Exit Sub

        Dim dx As Integer = CInt(GridPic.ClientSize.Width / GridCols)
        Dim dy As Integer = CInt(GridPic.ClientSize.Height / GridRows)
        Dim rect As New Rectangle(0, 0, dx, dy)
        Dim s2 As New List(Of String)
        Dim value As String

        If DataTableRB.Checked Then
            If Dt1 Is Nothing Or Dt1.Rows.Count = 0 Then Exit Sub
        Else
            If GridList Is Nothing Or GridList.Count = 0 Then Exit Sub
        End If

        For r = 0 To GridRows - 1
            If Not DataTableRB.Checked Then s2 = GridList(r)

            For c = 0 To GridCols - 1
                If DataTableRB.Checked Then
                    value = CType(Dt1.Rows(r)(c), String)
                ElseIf ListRB.Checked Then
                    value = s2(c)
                Else
                    value = GridArray(r, c)
                End If

                rect.X = c * dx
                rect.Y = r * dy

                If value.ToString = "X" Then
                    e.Graphics.FillRectangle(Brushes.Blue, rect)
                Else
                    e.Graphics.FillRectangle(Brushes.White, rect)
                End If
            Next
        Next
    End Sub
End Class


Cyrille Précetti on Sun, 06 Mar 2016 22:09:55


I did a comparison of a Table and List(Of) versions.

I had hopes for the List(Of) .Contains method, but it failed miserably against the straigth array brute search. So I read the msdn List(Of ).Contains and it does say it's a linear search algorithm that's O(n).... so no optimization...

Here is a screenshot of what I wrote:

It's all in Ticks because it way under the ms for the 100x100 grid.

The increment does 100 reps of a nxn grid with n=2 to 100.

Here is a capture of the List(Of incremental test:

It looks really linear...



dbasnett on Sun, 06 Mar 2016 23:00:59


So it sounds like a List is the best approach to this problem, if you take into consideration the ease of use.  My quad core, 32. GHz machine randomly filled 11,200 seats in

0.45
0.81
0.46
0.45
0.48
0.45

ms. (just seat filling time, no display component in the times)

My gut told me in the beginning that the decent performers would be an array, a 2D array, List, and List(Of Lists).  I didn't even think about using a datatable to store the data.  Since I chose List I am glad to know that it performed decently.

Did anyone else expect different results?

Cyrille Précetti on Sun, 06 Mar 2016 23:16:43


I agree about the List(Of) being easier to code.

BUT I had a look again at Knuth... and the Fisher-Yates algorithm.

And here is the result comparing table or List(Of) with brute While/End While with the Fisher-Yates:

Brute Array (100 x 100) seats = 3.4ms

Brute List(Of) (100 x 100) seats = 3.5ms

FisherYates List(of) (100 x 100) seats = 0.53 ms

It's an order magnitude better... So use array or List(Of) but remember to check your prefered book and use the appropriate algorithm ....

For reference here are the Random generator for Array :

Private Sub GenerateRandomArrivalSequence(ByRef myRandomArrivals() As Integer, ByVal intNumberSeats As Integer)
        Dim intIndex As Integer
        Dim tempRandom(0 To intNumberSeats - 1) As Integer
        Dim tempVal As Integer

        For intIndex = 0 To intNumberSeats - 1            
            tempVal = myRandom.Next(1, intNumberSeats + 1) - 1
            While tempRandom(tempVal) <> 0
                tempVal = myRandom.Next(1, intNumberSeats + 1) - 1
            End While
            tempRandom(tempVal) = intIndex + 1
            myRandomArrivals(intIndex) = tempVal            
        Next

    End Sub

For List:

Private Sub GenerateRandomArrivalList(ByRef myListOf As List(Of Integer), ByVal intnumberSeats As Integer)
        Dim intIndex As Integer
        'Dim tempRandom(0 To intnumberSeats - 1) As Integer
        Dim tempList As New List(Of Integer)
        'Init the list
        For intIndex = 0 To intnumberSeats - 1
            tempList.Add(0)
        Next
        Dim tempVal As Integer

        For intIndex = 0 To intnumberSeats - 1            
            tempVal = myRandom.Next(1, intnumberSeats + 1) - 1
            While tempList(tempVal) <> 0
                tempVal = myRandom.Next(1, intnumberSeats + 1) - 1
            End While
            tempList(tempVal) = intIndex + 1            
            myListOf.Add(tempVal)
        Next
    End Sub

And from some posts way back remember this .Contains version is a disaster (on which I had hope until I found out on msdn it is linear...)

    While myListOf.Contains(tempVal) = True
        tempVal = myRandom.Next(1, intnumberSeats + 1) - 1
    End While
    myListOf.Add(tempVal) 

And the Fisher Yates:

 Private Sub GenerateRandomArrivalList2(ByRef myListOf As List(Of Integer), ByVal intnumberSeats As Integer)
        Dim intIndex As Integer        
        Dim tempList As New List(Of Integer)

        Try
            'Init the list
            myListOf.Clear()
            For intIndex = 0 To intnumberSeats - 1
                myListOf.Add(intIndex)
            Next
            Dim tempVal As Integer                        
            'Fisher-Yates shuffle
            Dim totalN As Integer = myListOf.Count
            Dim switch1 As Integer
            For intIndex = intnumberSeats - 1 To 0 Step -1
                tempVal = myRandom.Next(0, totalN)
                switch1 = myListOf(tempVal)
                myListOf(tempVal) = myListOf(intIndex)
                myListOf(intIndex) = switch1
            Next            
        Catch ex As Exception
            Debug.Print("Erreur : " & ex.Message)
        End Try        
    End Sub