5,442,164 members and growing! (18,491 online)
Email Password   helpLost your password?
Languages » VB.NET » General     Beginner License: The Code Project Open License (CPOL)

Sudoku Algorithm: Generates a valid Sudoku in 0.0452 seconds

By The ANZAC

An article about the simple, yet often annoying to achieve, backtracking algorithm, the fastest method to generate a Sudoku.
VB (VB 7.x, VB 8.0, VB 9.0, VB)

Posted: 25 Jan 2008
Updated: 2 Feb 2008
Views: 16,006
Bookmarked: 25 times
Announcements
Want a new Job?



Search    
Advanced Search
Sitemap
5 votes for this Article.
Popularity: 2.65 Rating: 3.79 out of 5
0 votes, 0.0%
1
1 vote, 20.0%
2
0 votes, 0.0%
3
2 votes, 40.0%
4
2 votes, 40.0%
5
Note: This is an unedited contribution. If this article is inappropriate, needs attention or copies someone else's work without reference then please Report This Article

Introduction

When I designed the project this article is based on, it was much less about producing a fast algorithm for Sudoku generation. For all intents and purposes I had already achieved this in a previous project. No, this project was about testing myself that little bit further to learn new ways of dealing with things and, overall, improving my previous code with the new things I had learnt. Little did i know i'd reduce my all time best 5 second algorithm to a mere 0.07 second algorithm. Now i look at at, it seems so obvious.

Aim

The Aim of this project was to create a fast, short and reliable sudoku algorithm. To do this I used three main parts; a specialized structure, array lists and generic lists.

The result of this combination and application o the backtracking technique has resulted in exactly what i aimed for. A sudoku generator that does not make a mistake, is less than 300 lines of code and makes a sudoku in 0.07 seconds. That was impressive enough but thanks to Keith B. the generator can now produce a sudoku at an avergae of 0.0452 seconds.

The Rundown

What is Backtracking:

The basic principle of a backtracking algorithm, in regards to Sudoku, is to work forwards, one square at a time to produce a working Sudoku grid. When a problem occurs, the algorithm takes itself back one step and tries a different path. Essentially it's like walking through a maze with some golden thread and going back and forth down dead ends until you find the right way out. It's nearly impossible to produce a valid Sudoku by randomly plotting numbers and trying to make them fit. Likewise, backtracking with a random placement method is equally ineffective (trust me I've tried both). Backtracking best works in a linear method. It is fast, effective and reliable if done correctly. Below is a basic diagram showing the general flow of the algorithm.

AlgorithmTree.jpg

Sudoku:

Just in case you have forgotten or are unsure. There is only one rule to Sudoku, every number form 1 to 9 must be placed once, and once only, in every row, column and 3x3 region.

When to backtrack:

Ok, let's use this image as an example. We've been going fine so far for the first 17 numbers, but when it comes to finding a valid fit for the 18th number there are no valid options. every number bar 1 has been used on this line, but 1 has been used above, in the same column and the same region. Therefore; we have to backtrack, in this case back to the 6. Changing the 6 to a 1 would fix this problem.

Example1_2_.jpg

The key to backtracking:

The most important part of backtracking is, logically, keeping track of what has already been tried and where, or, in this case, what hasn't been tried where. For this example I used an arraylist of generic lists.

Dim Available(80) as List(Of Integer)

This way, each square of the Sudoku has its own individual list of values, telling the algorithm what hasn't been tried in the square. This way when another number has to be chosen, the only available numbers are the only ones tried.

The Algorithm:

You'll notice the algorithm calls a number of other functions, subs and the all important 'Structure', which I haven't yet talked about. For ease of understanding I have numbered and explained them.

    Public Sub GenerateGrid()
        Clear() '1:Clear
        Dim Squares(80) As Square '2:Square
        Dim Available(80) As List(Of Integer) 
        Dim c As Integer = 0 

        For x As Integer = 0 To Available.Length - 1
            Available(x) = New List(Of Integer)
            For i As Integer = 1 To 9
                Available(x).Add(i)
            Next
        Next

        Do Until c = 81
A:          Dim i As Integer = GetRan(0, Available(c).Count - 1) '3:GetRan
            If Not Available(c).Count = 0 Then 
                Dim z As Integer = Available(c).Item(i)
                If Conflicts(Squares, Item(c, z)) = False Then '4:Conflict
                    Squares(c) = Item(c, z) '5:Item
                    Available(c).RemoveAt(i) 
                    c += 1 
                Else
                    Available(c).RemoveAt(i) 
                    GoTo A
                End If
            Else
                For y As Integer = 1 To 9 
                    Available(c).Add(y)   
                Next
                Squares(c - 1) = Nothing 
                c -= 1                   
                GoTo A
            End If
        Loop

        Dim j As Integer 
        For j = 0 To 80
            Sudoku.Add(Squares(j))
        Next
    End Sub

1: Clear simply deletes the previously produced sudoku, if any.

    Public Sub Clear()
        Sudoku.Clear()
    End Sub

2: Square is the name I have given to the structure. Each instance of square represents an object that contains information about the value, index and relative position of each square. It contains it's region (3x3 area), row, column, index and value.

    Public Structure Square
        Dim Across As Integer
        Dim Down As Integer
        Dim Region As Integer
        Dim Value As Integer
        Dim Index As Integer
    End Structure

3: GetRan retrieves a random number between 0 and the last index of the current list, which means it grabs one of the remaining numbers indexes.

    Private Function GetRan(ByVal lower As Integer, ByVal upper As Integer) As Integer
        Dim r As New Random
        GetRan = r.Next(lower, upper - 1)
    End Function

4: Conflicts is, perhaps, the most important function in the overall algorithm. It tells us if the number we are looking at using, is going to work or not. To do this it takes the squares currently produced and compares them with an instance of a not yet produced square. This test square ('the hypothetical') is made in the 'Item' function below.

    Private Function Conflicts(ByVal CurrentValues As Square(), ByVal test As Square) As Boolean
        Dim Commons As New List(Of Square)
        For Each s As Square In CurrentValues
            Dim t As Integer = 0
            If s.Across <> 0 And s.Across = test.Across Then
                If s.Value = test.Value Then
                    Return True
                End If
            End If
        Next
        Commons.Clear()
        For Each s As Square In CurrentValues
            Dim t As Integer = 0
            If s.Down <> 0 And s.Down = test.Down Then
                If s.Value = test.Value Then
                    Return True
                End If
            End If
        Next
        Commons.Clear()
        For Each s As Square In CurrentValues
            Dim t As Integer = 0
            If s.Region <> 0 And s.Region = test.Region Then
                If s.Value = test.Value Then
                    Return True
                End If
            End If
        Next
        Return False
    End Function

5: Item takes the given value and the given index and returns a square item containing all relevant information. It does this by calling on 3 other functions to acquire the row, column and region of the square. These other functions use simple math to determine the row, column etc...

    Private Function Item(ByVal n As Integer, ByVal v As Integer) As Square
        n += 1
        Item.Across = GetAcrossFromNumber(n)
        Item.Down = GetDownFromNumber(n)
        Item.Region = GetRegionFromNumber(n)
        Item.Value = v
        Item.Index = n - 1
    End Function

    Private Function GetAcrossFromNumber(ByVal n As Integer) As Integer
        Dim k As Integer
        k = n Mod 9        
        If k = 0 Then Return 9 Else Return k
    End Function

    Private Function GetDownFromNumber(ByVal n As Integer) As Integer
        Dim k As Integer
        If GetAcrossFromNumber(n) = 9 Then
            k = n\9   
        Else
            k = n\9 + 1
        End If
        Return k
    End Function

    Private Function GetRegionFromNumber(ByVal n As Integer) As Integer
        Dim k As Integer
        Dim a As Integer = GetAcrossFromNumber(n)
        Dim d As Integer = GetDownFromNumber(n)

        If 1 <= a And a < 4 And 1 <= d And d < 4 Then
            k = 1
        ElseIf 4 <= a And a < 7 And 1 <= d And d < 4 Then
            k = 2
        ElseIf 7 <= a And a < 10 And 1 <= d And d < 4 Then
            k = 3
        ElseIf 1 <= a And a < 4 And 4 <= d And d < 7 Then
            k = 4
        ElseIf 4 <= a And a < 7 And 4 <= d And d < 7 Then
            k = 5
        ElseIf 7 <= a And a < 10 And 4 <= d And d < 7 Then
            k = 6
        ElseIf 1 <= a And a < 4 And 7 <= d And d < 10 Then
            k = 7
        ElseIf 4 <= a And a < 7 And 7 <= d And d < 10 Then
            k = 8
        ElseIf 7 <= a And a < 10 And 7 <= d And d < 10 Then
            k = 9
        End If
        Return k
    End Function

More about the structure:

If your completely new to structures like I really was when I started this two day project then here is a brief rundown of how it works. When you create an instance of a structure like I did every time I created a 'square'. The variables defined in the structure itself are created as part of the instance like properties. They can be read and written but are crucially part of the instance, like text to a textbox. in this way you can create a totally new and extremely useful/versatile object such as the Sudoku square. The beauty of having a list of these custom structures is the ability to access individual items and extract individual values. For instance;

Dim Test as Integer =  SudoGen.Sudoku.Items(0).value

The best way to learn more about them is either by reading or playing around with them a bit, until you get the hang of it. I recommend the latter, nothing beats experience.

Benefit of the list:

The most benefecial part about using list(of integer) is the way it automatically adjusts itself when an item is removed. When you take an item out, like a listbox, the next item takes on its index which means when using GetRan its not a hit and miss affair, there's always a number available in every item.

Using the code

For ease of use, the code discussed is compiled in a module called SudoGen. Easy to access, easy to use and easy to incorporate into countless projects.

To Generate a Sudoku, it's as easy as:

SudoGen.GenerateGrid

Then it's simple to access the output once the Sudoku has generated. these remain until a new grid is created or the SudoGen.Clear Function is called.

SudoGen.Sudoku 'A list(of square) : The output grid

Points of Interest

Removing the components form the module and using them within your main class may be more useful as it will allow you to do different things with the code. For example you could keep track of the progress of the generator to display to the user.

Nothing really to do with this program, but I believe some Bacteria use backtracking to find the most direct path to a source of food, probing every direction until they find a large enough source to feed the colony, then the unused/unsuccessful parts die off for the greater good of the colony. A bit similar I think. If nature thinks it's the most effective, then methinks it a wise move to agree.

History

Posted: January 26, 2008, 7:04 PM AEDST

Updated: January 26, 2008, 8:50 PM AEDST

Updated: January 27, 2008, 9:00 AM AEDST
- Increased sudoku creation speed from an average of 6-20 seconds, all the way down to 0.07 seconds

Updated: February 3rd, 2008, 11:20 AM AEDST
- Added suggestions from Keith B. which increased the generator speed from 0.07 to 0.0452

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

The ANZAC


I am a 18 year old student in Australia who has spent the vast majority of my life growing up on Australia's beautiful east coast. I am a hobby programmer and have no formal training as yet. I have just begun studying Nanotechnology at the University of Wollongong.

I am currently using Visual Studio 2005.
Occupation: Other
Location: Australia Australia

Other popular VB.NET articles:

Article Top
Sign Up to vote for this article
You must Sign In to use this message board.
FAQ FAQ Noise ToleranceSearch Search Messages 
 Layout  Per page   
 Msgs 1 to 23 of 23 (Total in Forum: 23) (Refresh)FirstPrevNext
Subject  Author Date 
Generalvery good~membersunerbun0:22 25 May '08  
GeneralRe: very good~member The ANZAC 0:52 25 May '08  
GeneralThe real trouble in producing a sudoku.memberMember 42592044:45 6 Feb '08  
GeneralRe: The real trouble in producing a sudoku.member The ANZAC 12:28 6 Feb '08  
GeneralGenerate Sudoku even fastermemberkeith.b4:26 2 Feb '08  
GeneralRe: Generate Sudoku even fastermember The ANZAC 14:08 2 Feb '08  
GeneralRe: Generate Sudoku even fastermember The ANZAC 14:22 2 Feb '08  
GeneralRe: Generate Sudoku even fastermemberkeith.b23:40 5 Feb '08  
GeneralRe: Generate Sudoku even fastermember The ANZAC 0:24 6 Feb '08  
GeneralRe: Generate Sudoku even fastermemberkeith.b2:33 6 Feb '08  
GeneralRe: Generate Sudoku even fastermember The ANZAC 12:29 6 Feb '08  
GeneralRe: Generate Sudoku even fastermemberkeith.b7:15 17 Feb '08  
GeneralRe: Generate Sudoku even fastermember The ANZAC 10:53 17 Feb '08  
GeneralRe: Generate Sudoku even fastermemberOmegaThree5:31 2 Aug '08  
GeneralSuggestion - puzzle "levels"memberJim Tomasko21:14 29 Jan '08  
GeneralRe: Suggestion - puzzle "levels" [modified]member The ANZAC 1:09 30 Jan '08  
GeneralRe: Suggestion - puzzle "levels"member The ANZAC 14:25 2 Feb '08  
GeneralRe: Suggestion - puzzle "levels"member The ANZAC 2:38 18 May '08  
QuestionC# ?member Amr Elsehemy ®22:24 26 Jan '08