Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

C# Word Permuter-Shifter


On April 23rd I had a job phone interview. The talk was about a Software Development Engineer in Test (SDET) position at Microsoft.

I already blogged about a C# Thread Safe Circular Queue, which was one of the questions of a screen test (prior to a phone interview) I completed on January for the same Microsoft job position.

On this last interview we (me and the Microsoft employee-interviewer) would talk a little bit and then get to the coding questions. It didn't happen as expected. Firstly the Microsoft interviewer tried to call me on my cellphone. I just couldn't hear a word of what he was saying. The sound was very low. Then we decided to start with the coding questions and then on the phone land line we would talk so that he could explain more about the position, etc.

We started a meeting on Microsoft Office Live Meeting. I was asked about a simple and easy question.

The question:

Write a program that changes the position of the words of a given sentence. For example, given the sentence "How are you going" as input, the output should be "are How going you".

Simple, isn't it? I thought that too at the moment.

As you can see, the first and second words change of position and then the third and forth words change of position and so forth.

The interviewer explained that I could write any code. It didn't need to be written with the syntax of a programming language, it could be a pseudocode.

I'm so used to code using code auto-completion (IntelliSense) and the debugger that I just didn't write any good code to solve the question. It was really frustrating. All what I needed to do was clear in my mind but I just couldn't express it. Was that the case of me being nervous? Beats me.

Today I decided about writing a post with a possible solution to this simple programming question. I opened Microsoft Visual Studio C# Express and wrote some code. In just 5 minutes I had a working code that did the job.

There are things in life that are really weird. I passed more than 45 minutes trying to write a pseudocode and then with the help of IntelliSense and debugger catching my mistakes, things flowed flawlessly and rapidly. Why that faster? I don't know how to explain it, maybe because of IntelliSense and the presence of a handy debugger!

For sure the solution I present here isn't the better, but it's a solution and that's what was asked.

Bellow is the code I wrote:

namespace WordPermuter
{
  class Program
  {
    static void Main(string[] args)
    {
      List<string> sentences = new List<string>()
      {
        { "How are you going my dear?" }, // are How going you dear? my
        { "I am going fine honey." } // am I fine going honey
      };
      DoPermutation(sentences);
    }
    static void DoPermutation(List<string> sentences)
    {
      foreach(string sentence in sentences)
      {
        // Split the sentence when it reaches a white space
        var splitted = sentence.Split(' ');
        // Increment the counter on a scale of 2
        for(int i = 0; i < splitted.Length; i = i + 2)
        {
          if(i < splitted.Length - 1)
          {
            var aux = splitted[i];
            splitted[i] = splitted[i + 1];
            splitted[i + 1] = aux;
          }
        }
        foreach(var str in splitted)
          Console.Write(str + " ");
        Console.WriteLine();
      }
    }
  }
}

What's the purpose of this code? Aha... It isn't the code itself but what approach you took to get to a solution. The interviewer wants to see how is your thinking process. No matter if you coded it wrong, but at least you should show something. As the recruiters always say: "Think it loud so that we can help you."

After a solution has been presented, the interviewer will probably ask you what if questions. What if I did this way? What If I did that way? What is the breach? What are the possible bugs?... The list of possible questions is innumerable.

I think my solution is OK! If you find any bug, please tell me.

Thread Safe Circular Queue in C#


While completing a screen for a Software Development Engineer in Test (SDTE) position for Microsoft I had to implement a thread safe circular queue.

The question was the following:

Implement a circular queue of integers of user-specified size using a simple array. Provide routines to initialize(), enqueue() and dequeue() the queue. Make it thread safe. A really good observation was: Please do not to use existing class libraries for this question. Thanks!

My first attempt was obviously get more information about a circular queue and bring it back to my mind. That's because I had studied it a long time ago when I was in the beginning of the computer engineering course. If you don't use it you forget it. That's the truth.

This post aims to present an implementation of a thread safe circular queue in C#. I won't deal with theory. For theory, I advise you to read the Wikipedia article about circular queue (buffer) to get started.

I reused some code from the links I present in the references section at the end of this post.

I'll break down the code I implemented so that you can go through the functions that were asked in the problem statement.

class ThreadSafeCircularQueue
{
  private int[] queue;
  private int head;
  private int tail;
  private int length;

  static Object thisLock = new Object();

  public CircularQueue()
  {
     Initialize();
  }

  ...
}

The class ThreadSafeCircularQueue has five properties: an integer array (the queue) and the queue's head, tail and length. A static object thisLock is used to make the queue thread safe.

private void Initialize()
{
  head = tail = -1;

  Console.Write("Circular Queue size: ");

  string length = Console.ReadLine();

  this.length = int.Parse(length);

  queue = new int[this.length];
}

The function Initialize() does what its name means. The queue's head and tail are set to appropriate values and the user has the opportunity of specifying the queue's size (length) by entering an integer value. A new queue is then created and has the user specified size.

The Enqueue() and Dequeue() functions shown bellow do the hard work, that is, they control all the circular queue functionality.

The Enqueue() function receives an integer value to be enqueued. Note the use of the thisLock variable. It is used so that we get thread safety, that is, all the code section inside the lock statement can't be executed by two threads at the same time. This avoids the problem of two concurrent threads trying to access the queue data structure simultaneously. If it's not controlled in the source code level we can get invalid values in the queue what could lend to a corrupted queue. That's not what is expected. When the code section that is inside the lock statement is being executed by a thread and other thread reaches the same section of code, this second thread waits till the first thread exits such code section. This way the queue has a valid set of values and its data is maintained secure.

Firstly is checked if the queue is full. It is full when it's head points to index 0 and its tail points to length - 1 or when its tail + 1 equals the same value of head. Remember that it is a circular queue.

In case the queue is full a message is shown to the user. If it still has room we check if the "end" of the queue (tail) that points to the last element has an index of length minus 1. If it is then the tail will now point to the the "start" of the queue. The other possibility is to increment the tail variable. The position referenced by the tail variable is then used to enqueue the value parameter into the queue. Then the just enqueued value is printed in the screen. A check is done to verify if the head variable has a value of -1. If it's the case then head will point to index 0.

private void Enqueue(int value)
{
  lock(thisLock)
  {
    if((head == 0 && tail == length - 1)  (tail + 1 == head))
    {
      Console.WriteLine("Circular queue is full.");

      return;
    }
    else
    {
      if(tail == length - 1)
        tail = 0;
      else
        tail++;

      queue[tail] = value;

      Console.WriteLine("In -> {0}", value);
    }

    if(head == -1)
      head = 0;
  }
}

The Dequeue() function also uses the lock statement because it must be a thread safe function.

Firstly is checked if the queue's head points to -1. Remember that in the Initialize() function when the queue is initialized its head and tail variables are set to -1. If head has a value of -1 then the queue is empty and the user is informed about that. A value of -1 is returned to conform with the function signature that needs to return an integer value.

If the queue is not empty the integer variable v receives the value that is indexed in the queue's head position. The value 0 is then assigned to the position that was just dequeued.

Now it's necessary to update the head position inside the queue. To accomplish that it's checked if head is equal tail, if it is, then head = tail = -1. This last operation states that the queue is empty after the dequeue operation. Else it's checked if head is equal length - 1. If it is the case then head receives 0. The last possible condition increments the value of head. In the end the value being dequeued (v) is printed in the screen.

Printing the values in the screen is a good way of keeping track of what is going on while the program is running.

private void Dequeue()
{
  lock(thisLock)
  {
    int value;

    if(head == -1)
    {
      Console.WriteLine("Circular queue is empty.");

      value = -1;
    }
    else
    {
      value = queue[head];
      queue[head] = 0;

      if(head == tail)
        head = tail = -1;
      else
        if(head == length - 1)
          head = 0;
        else
          head++;
    }

    Console.WriteLine("Out -> {0}", value);
  }
}

Now take a look at the Show() function bellow. Again the lock is present. Why? In an instant you'll get to the point. Hold your horses! :-)

A check is done to verify if the queue is empty. If it is the case the callee function just returns to the caller function. If it is not the case we proceed to show the values already present in the queue. If tail is less than head a for loop starting in 0 and ending in length - 1 iterates the queue and each value is written in the screen. Otherwise a for loop starting in 0 end ending in tail iterates the queue and each value is written in the screen.

private void Show()
{
  lock(thisLock)
  {
    int i;

    if(head == -1)
    {
      Console.WriteLine("Circular queue is empty.");

      return;
    }
    else
    {
      if(tail < head)
      {
        //for(i = head; i <= size - 1; i++)
        for(i = 0; i <= length - 1; i++)
          Console.Write("{0} ", queue[i]);
      }
      else
        //for(i = head; i <= tail; i++)
        for(i = 0; i <= tail; i++)
          Console.Write("{0} ", queue[i]);

      Console.WriteLine();
    }
  }
}

The following is the EnqueueDequeue() function responsible for calling the Queue(), Dequeue() and Show() functions. In this function I execute a simple test case and comment the operations being carried out.

public void EnqueueDequeue()
{
  Enqueue(1);
  Enqueue(2);
  Enqueue(3);
  Enqueue(4);
  Show();
  Enqueue(5); // Circular queue is full!
  Dequeue();
  Dequeue();
  Dequeue();
  Dequeue();
  Dequeue(); // Circular queue is empty!
  Dequeue(); // Circular queue is empty!
  Show();
  Enqueue(6);
  Show();
  Enqueue(7);
  Show();
  Dequeue();
  Dequeue();
  Enqueue(8);
  Enqueue(9);
  Show();

  // Supposing a 4 size queue:  0 ¦ 0 ¦ 0 ¦ 0
  //
  //                            1 ¦ 0 ¦ 0 ¦ 0  <- Enqueue 1
  //
  //                            1 ¦ 2 ¦ 0 ¦ 0  <- Enqueue 2
  //
  //                            1 ¦ 2 ¦ 3 ¦ 0  <- Enqueue 3
  //
  //                            1 ¦ 2 ¦ 3 ¦ 4  <- Enqueue 4
  //              
  //                            1 ¦ 2 ¦ 3 ¦ 4  <- Circular queue is full when trying to enqueue 5.
  //
  //                            0 ¦ 2 ¦ 3 ¦ 4  <- Dequeue 1
  //
  //                            0 ¦ 0 ¦ 3 ¦ 4  <- Dequeue 2
  //
  //                            0 ¦ 0 ¦ 0 ¦ 4  <- Dequeue 3
  //                   
  //                            0 ¦ 0 ¦ 0 ¦ 0  <- Dequeue 4
  //
  //                            0 ¦ 0 ¦ 0 ¦ 0  <- Circular queue is empty when trying to dequeue.
  //
  //                            0 ¦ 0 ¦ 0 ¦ 0  <- Circular queue is empty when trying to dequeue.
  //
  //                            6 ¦ 0 ¦ 0 ¦ 0  <- Enqueue 6
  //
  //                            6 ¦ 7 ¦ 0 ¦ 0  <- Enqueue 7
  //
  //                            0 ¦ 7 ¦ 0 ¦ 0  <- Dequeue 6
  //
  //                            0 ¦ 0 ¦ 0 ¦ 0  <- Dequeue 7
  //
  //                            8 ¦ 0 ¦ 0 ¦ 0  <- Enqueue 8
  //
  //                            8 ¦ 9 ¦ 0 ¦ 0  <- Enqueue 9
  //
  // * 0 is set in a position when dequeueing so that it's easier to watch the queue variable.
}

Now the main start thread:

class Program
{
  static void Main(string[] args)
  {
    ThreadSafeCircularQueue circularQueue = new ThreadSafeCircularQueue();

    Thread[] threads = new Thread[10];

    for(int i = 0; i < threads.Length; i++)
    {
      threads[i] = new Thread(new ThreadStart(circularQueue.EnqueueDequeue));
    }

    for(int i = 0; i < threads.Length; i++)
    {
      threads[i].Start();
    }

    Console.ReadLine();
  }
}

As you can see above I declare an object of type ThreadSafeCircularQueue. An array of 10 objects of type Thread is then created. In a for loop I instantiate each Thread passing to it a delegate that represents the method that'll be invoked when the thread begins executing.

In the subsequent for loop I call the Start() method of each thread and they start executing simultaneously, tough they won't concur when accessing the functions Enqueue(), Dequeue() and Show().

Visual Studio C# Console Application
You can get the Microsoft Visual Studio Project and the app executable at: http://leniel.googlepages.com/ThreadSafeCircularQueueCSharp.zip

References
During the research phase of this implementation I recurred to some sites to get more information regarding the circular queue data structure. The following list can provide you a better understanding of such a data structure.

Thread Safety articles
[1] Venners, Bill. Designing for Thread Safety: Using Synchronization, Immutable Objects, and Thread-Safe Wrappers. 1998. Available at <http://www.artima.com/designtechniques/threadsafety.html>. Accessed on April 29, 2008.

[2] Suess, Michael. A Short Guide to Mastering Thread-Safety. 2006. Available at <http://www.thinkingparallel.com/2006/10/15/a-short-guide-to-mastering-thread-safety/>. Accessed on April 29, 2008.

[3] Allen, K. Scott. Statics & Thread Safety: Part II. 2004. Available at <http://www.odetocode.com/Articles/314.aspx>. Accessed on April 29, 2008.

Circular Queue sample code
[4] Kumar, Nunna Santhosh. Circular Queue Implementation using Arrays in C++. Available at <http://www.sourcecodesworld.com/source/show.asp?ScriptID=887>. Accessed on April 29, 2008.

Thread material
[5] Albahari, Joseph. Threading in C#. 2007. Available at <http://www.albahari.com/threading/>. Accessed on April 29, 2008.

Robot arm with OpenGL in CSharp


Robot arm
A robot arm or robotic arm can be classified as articulated and not articulated. It’s more autonomous than a simple mechanic arm and can be used to lift small parts with high precision and velocity. It’s generally used in tasks such as: welding, painting, assembling, packaging, storage, product inspection and test and even in spacecrafts as can be seen below:

OpenGL
OpenGL (Open Graphics Library) is a standard specification that defines an API (Application Program Interface) that is multi-language and multi-platform and that enables the codification of applications that output computerized graphics in 2D and 3D.

Computer graphics paper
I and a dear brother in faith of mine called Wellington Magalhães Leite wrote a paper titled: Construction and Simulation of a Robot Arm with OpenGL

We used the Tao Framework C# biding to OpenGL during the construction of the robot arm.

See one of the screenshots of our robot arm:

See the paper's abstract below:

The importance of projects related to the field of Computer Graphics in simulations has been growing a lot during the last years. Therefore it brings to life the necessity of mastering the concepts and techniques inherent to the process of elaboration, construction and simulation of a given graphical project.

The OpenGPL API specification tries to help us when we are programming the graphical details of a given project. In this article we’re showing the necessary steps and routines to the proper codification and simulation of a robotic arm in 3D, which is the most employed robot in the manufacturing industry and in areas that require a high precision rate.

With a simulation (virtual) model, we can have a closer vision of the object of study in contrast with reality, what make us capable of foreseeing how a determined object will look like and how it will behave after its proper construction in the physical world.

Keywords: robot arm, OpenGL, 3D simulation, computer graphics

You can get a PDF copy of the article at:

http://leniel.googlepages.com/ConstructionSimulationRobotArmOpenGL.pdf

Visual Studio C# Windows Application
You can get the Microsoft Visual Studio Project and the executables at:

http://leniel.googlepages.com/RobotArmOpenGLCSharp.zip

LINQ - Language Integrated Query


LINQ
The LINQ Project is a codename for a set of extensions to the .NET Framework that encompass language-integrated query, set, and transform operations. It extends C# and Visual Basic with native language syntax for queries and provides class libraries to take advantage of these capabilities.

My bachelor's degree graduation project
As a result of my graduation project in the computer engineering course I ended up with a concise document describing the idea behind LINQ. The document is available only in Portuguese so that I think it's a valuable source of information to people that know Portuguese given the fact that great material about LINQ is only available in English.

In addition to the intrinsic subjects related to the integration of the query language (SQL) into the programming language (C#), in this paper you'll also find information about the great language extensions that form the base of LINQ:

  • Generics
  • Anonymous methods
  • Iterators
  • Partial types
  • Nullable types
  • Query expressions
  • Automatically implemented properties
  • Implicitly typed local variables
  • Extension methods
  • Partial methods
  • Lambda expressions
  • Object initializers
  • Collection initializers
  • Anonymous types
  • Implicitly typed arrays
  • Expression trees

See the paper's abstract below (English/Português):

ABSTRACT

Macaferi, Leniel Braz de Oliveira. Query language integrated into the programming language. 2007. 96f. Monograph (bachelor’s degree in Computer Engineering) - Barra Mansa University Center, Barra Mansa, 2007. www.ubm.br

Data is the raw material of computation and is processed via software. Software products are generally structured in tiers, typically three, the data tier, the middle or business tier and the presentation or client tier. Each of these tiers has its own data model. These different paradigms cause the impedance mismatch problem between these three disparate models.

Instead of trying to unify at the data model level, a better approach is to unify at the level of algebraic operations that can be defined the same way over each data model. This allows us to define a single query language that can be used to query and transform any data model. All the data model need to do is to implement a small set of standard query operators, and each data model can do so in a way natural to itself.

The industry has reached a stable point in the evolution of object-oriented (OO) programming technologies. Programmers now take for granted the facilities of oriented programming languages and their features like classes, objects, methods and events. Such languages support the creation and use of higher order, functional style class libraries. The support is the result of new language extensions being developed. These extensions enable the construction of compositional application program interfaces (APIs) that have equal expressive power of query languages inside the programming language syntax. This makes it possible to implement the standard query operators. The standard query operators can be then applied to all sources of data, not just relational or XML domains.

This work aims to present and use the most important aspects of the language integrated query with special focus on the integration of the SQL query language into the C# programming language. Aspects as simplification of the way of writing queries, unification of the syntax for querying any data source, reinforcement of the connection between relational data and the object oriented world and less time spent in the software development process.

Keywords: query language, programming language, data models, SQL, C#, LINQ

RESUMO

Macaferi, Leniel Braz de Oliveira. Linguagem de pesquisa integrada à linguagem de programação. 2007. 96f. Monografia (bacharelado em Engenharia de Computação) - Centro Universitário de Barra Mansa, Barra Mansa, 2007. www.ubm.br


Dados formam a matéria prima da computação e são processados via software. Produtos de software são geralmente estruturados em camadas, tipicamente três: a camada de dados, a camada intermediária ou de lógica e a camada de apresentação ou do cliente. Cada uma destas camadas possui seu próprio modelo de dados. Estes diferentes paradigmas causam o problema da combinação mal sucedida entre estes três modelos completamente diferentes.

Ao invés de tentar unificar no nível do modelo de dados, uma melhor alternativa é unificar no nível das operações algébricas que podem ser definidas do mesmo modo sobre cada modelo de dados. Isto nos permite definir uma única linguagem de pesquisa que pode ser usada para pesquisar e transformar qualquer modelo de dados. Tudo o que os modelos de dados precisam implementar é um pequeno conjunto de operadores de pesquisa padrão, e cada modelo de dados pode fazer isto de uma maneira natural.

A indústria chegou a um ponto estável na evolução das tecnologias de programação orientada a objetos (OO). Desenvolvedores agora têm por certo as facilidades das linguagens de programação OO e seus ricos recursos iguais a classes, objetos, métodos e eventos. Tais linguagens suportam a criação e uso de bibliotecas de classe de estilo funcional de ordem maior. O suporte é o resultado das novas extensões de linguagem de programação que estão sendo desenvolvidas. Estas extensões permitem a criação de interfaces para programação de aplicativos (APIs) composicionais que possuem poderosas capacidades de pesquisa dentro da sintaxe da linguagem de programação. Isto torna viável a implementação dos operadores de pesquisa padrão. Os operadores de pesquisa padrão podem ser aplicados em todas as fontes de informação, não somente em domínios de bancos de dados relacionais ou XML.

Este trabalho visa apresentar e utilizar os aspectos mais importantes da linguagem integrada de pesquisa com foco na integração da linguagem de pesquisa SQL à linguagem de programação C#. Aspectos como a simplificação da maneira de escrever pesquisas, unificação da sintaxe para pesquisar qualquer fonte de dados, reforço da conexão entre dados relacionais e o mundo orientado a objetos e o menor tempo gasto no processo de desenvolvimento de software.

Palavras-chave: linguagem de pesquisa, linguagem de programação, modelos de dados, SQL, C#, LINQ

SUMÁRIO
1 INTRODUÇÃO 15
  1.1 Delimitação do tema 16
  1.2 Problema 16
  1.3 Enunciado das hipóteses 17
  1.4 Objetivos específicos e geral 18
  1.5 Justificativa do trabalho 18
2 FUNDAMENTAÇÃO TEÓRICA 19
  2.1 Linguagem de pesquisa 19
      2.1.1 Pesquisa 19
  2.2 Linguagem de programação 19
  2.3 Combinação mal sucedida entre as linguagens de pesquisa e de programação 20
  2.4 Programação orientada a objetos 24
      2.4.1 Classe e objeto 25
      2.4.2 Variável e tipo 25
      2.4.3 Membro 25
      2.4.4 Acessibilidade 25
      2.4.5 Método 26
      2.4.6 Parâmetro 26
      2.4.7 Troca de mensagem 26
      2.4.8 Herança 26
      2.4.9 Encapsulamento 26
      2.4.10 Abstração 27
      2.4.11 Polimorfismo 27
      2.4.12 Interface 27
      2.4.13 Delegate 27
  2.5 Banco de dados relacional 28
      2.5.1 Relação ou tabela 28
      2.5.2 Restrição 28
      2.5.3 Domínio de dado 28
      2.5.4 Chave primária 29
      2.5.5 Chave estrangeira 29
      2.5.6 Stored procedure 29
      2.5.7 View 29
      2.5.8 User defined function 30
  2.6 .NET Framework 30
      2.6.1 Principais recursos 32
      2.6.2 Arquitetura 33
      2.6.3 Infra-estrutura de linguagem comum 33
      2.6.4 Assemblies 34
      2.6.5 Metadados 35
      2.6.6 Biblioteca de classes base 35
  2.7 SQL 35
  2.8 C# 35
3 METODOLOGIA 37
  3.1 Extensões de linguagem 37
      3.1.1 Genéricos 37
      3.1.2 Métodos anônimos 38
      3.1.3 Iteradores 38
      3.1.4 Tipos parciais 40
      3.1.5 Tipos anuláveis 41
      3.1.6 Expressões de pesquisa 43
      3.1.7 Propriedades automaticamente implementadas 44
      3.1.8 Variáveis locais implicitamente tipificadas 45
      3.1.9 Métodos de extensão 46
      3.1.10 Métodos parciais 47
      3.1.11 Expressões lambda 49
      3.1.12 Inicializadores de objeto 50
      3.1.13 Inicializadores de coleção 50
      3.1.14 Tipos anônimos 51
      3.1.15 Arrays implicitamente tipificados 52
      3.1.16 Árvores de expressão 53
4 DESENVOLVIMENTO 54
  4.1 Linguagem de pesquisa integrada à linguagem de programação 54
      4.1.1 Operadores de pesquisa padrão 56
      4.1.2 Fonte de dados 61
      4.1.3 Operação de pesquisa 62
      4.1.4 Modelo de objetos 63
  4.2 Estudo de caso 64
      4.2.1 Classes do modelo de objetos 68
      4.2.2 DataContext 68
      4.2.3 Relacionamentos 69
      4.2.4 Pesquisa de dados 70
      4.2.5 Operações de insert, update e delete 71
5 CONCLUSÃO 73
  5.1 Avanços 73
  5.2 Limitações 74
  5.3 Trabalhos relacionados 74
  5.4 Trabalhos futuros 76
6 BIBLIOGRAFIA 78
ANEXOS 81

You can get a PDF copy of the full paper at:

http://leniel.googlepages.com/LingPesqIntegradaLingProg.pdf

Breadth and depth first search - part 2


As I've written in the previous post Breadth and depth first search - part 1 - I'll dive in more details and explain how to use the breadth and depth search methods. We'll execute a test case using the Romania map shown bellow, print the traveled paths in the screen, calculate the shortest path possible between two cities using Dijkstra's algorithm and print such a path in the screen.

To accomplish all the above let's start presenting the data structures used to represent the map in the C# programming language:
 

public class Node
{
  public Node(string key, object data);
  public Node(string key, object data, AdjacencyList neighbors);
 
  public virtual object Data { get; set; }
  public virtual string Key { get; }
  public virtual AdjacencyList Neighbors { get; }
  public virtual Node PathParent { get; set; }
 
  protected internal virtual void AddDirected(EdgeToNeighbor e);
  protected internal virtual void AddDirected(Node n);
  protected internal virtual void AddDirected(Node n, int cost);
}

The Node class will be used to represent each city of the map.

Each city has its name represented by the key property and some other relevant data represented by the data property. Each city also has an adjacency list implemented by a specific class called AdjacencyList. This adjacency list represents the neighbors cities of a given city. For example, in the above map the neighbors cities of Bucharest are: Urziceni, Giurgiu, Pitesti and Fagaras.

Let's see the code of another class:

public class Graph
{
  public Graph();

  public Graph(NodeList nodes);
 
  public virtual int Count { get; }

  public virtual NodeList Nodes { get; }
 
  public virtual void AddDirectedEdge(Node u, Node v);
  public virtual void AddDirectedEdge(string uKey, string vKey);
  public virtual void AddDirectedEdge(Node u, Node v, int cost);
  public virtual void AddDirectedEdge(string uKey, string vKey,
int cost);
  public virtual void AddNode(Node n);
  public virtual Node AddNode(string key, object data);
  public virtual void AddUndirectedEdge(Node u, Node v);
  public virtual void AddUndirectedEdge(string uKey, string vKey);
  public virtual void AddUndirectedEdge(Node u, Node v, int cost);
  public virtual void AddUndirectedEdge(string uKey, string vKey,
int cost);
  public virtual void Clear();
  public virtual bool Contains(Node n);
  public virtual bool Contains(string key);
}

The Graph class has a property that references a collection of nodes, that is, a collection of cities. This collection of cities is represented by the class NodeList that implements the so used interface IEnumerable.

As you can see the Graph class has methods that add directed or undirected edges to the graph. Each line that connects two cities (vertexes) in the Romania map is considered an edge.

The map above contains only undirected edges because they aren't defined just as one way paths between the cities. It's possible to go from Bucharest to Urziceni and then come back to Bucharest for example. So it's a two way path.

Above each line in the map is a value that represents the path cost between two cities. Let's consider this cost as the distance in miles between the cities. The path cost could be any other variable, for example, the time spent to traverse the distance (edge). The cost variable can vary according to the problem.

I implemented a class called Pathfinding as follows:
 

class Pathfinding
{
  private static Graph graph = new Graph();
 
  ...
 
  public static void BreadthFirstSearch(Node start, Node end)
  {
    ...
  }
  public static void DepthFirstSearch(Node start, Node end)
  {
    ...
  }

  ...
}

This class has additional properties and methods as ShortestPath and PrintPath. I won't spend time explaining its additional methods because they are already well explained in Part 5: From Trees to Graphs (article by Scott Mitchell). So, let's run a test case. For this we need to fill the graph with the Romania map data.
 

class Program
{
  static void Main(string[] args)
  {
    Pathfinding pathFinding = new Pathfinding();
 
    Node start, end;
 
    // Vertexes
    pathFinding.Graph.AddNode("Arad", null);
    pathFinding.Graph.AddNode("Bucharest", null);
    pathFinding.Graph.AddNode("Craiova", null);
    pathFinding.Graph.AddNode("Dobreta", null);
    pathFinding.Graph.AddNode("Eforie", null);
    pathFinding.Graph.AddNode("Fagaras", null);
    pathFinding.Graph.AddNode("Giurgiu", null);
    pathFinding.Graph.AddNode("Hirsova", null);
    pathFinding.Graph.AddNode("Iasi", null);
    pathFinding.Graph.AddNode("Lugoj", null);
    pathFinding.Graph.AddNode("Mehadia", null);
    pathFinding.Graph.AddNode("Neamt", null);
    pathFinding.Graph.AddNode("Oradea", null);
    pathFinding.Graph.AddNode("Pitesti", null);
    pathFinding.Graph.AddNode("Rimnicu Vilcea", null);
    pathFinding.Graph.AddNode("Sibiu", null);
    pathFinding.Graph.AddNode("Timisoara", null);
    pathFinding.Graph.AddNode("Urziceni", null);
    pathFinding.Graph.AddNode("Vaslui", null);
    pathFinding.Graph.AddNode("Zerind", null);
 
    // Edges
 
    // Arad <-> Zerind
    pathFinding.Graph.AddUndirectedEdge("Arad", "Zerind", 75);
    // Arad <-> Timisoara
    pathFinding.Graph.AddUndirectedEdge("Arad", "Timisoara", 118);
    // Arad <-> Sibiu
    pathFinding.Graph.AddUndirectedEdge("Arad", "Sibiu", 140);
 
    // Bucharest <-> Urziceni
    pathFinding.Graph.AddUndirectedEdge("Bucharest", "Urziceni",
85);
    // Bucharest <-> Giurgiu
    pathFinding.Graph.AddUndirectedEdge("Bucharest", "Giurgiu",
90);
    // Bucharest <-> Pitesti
    pathFinding.Graph.AddUndirectedEdge("Bucharest", "Pitesti",
101);
    // Bucharest <-> Fagaras
    pathFinding.Graph.AddUndirectedEdge("Bucharest", "Fagaras",
211);
 
    // Craiova <-> Dobreta
    pathFinding.Graph.AddUndirectedEdge("Craiova", "Dobreta",
120);
    // Craiova <-> Pitesti
    pathFinding.Graph.AddUndirectedEdge("Craiova", "Pitesti",
138);
    // Craiova <-> Rimnicu Vilcea
    pathFinding.Graph.AddUndirectedEdge("Craiova",
"Rimnicu Vilcea", 146);
 
    // Dobreta <-> Mehadia
    pathFinding.Graph.AddUndirectedEdge("Dobreta", "Mehadia", 75);
 
    // Eforie <-> Hirsova
    pathFinding.Graph.AddUndirectedEdge("Eforie", "Hirsova", 86);
 
    // Fagaras <-> Sibiu
    pathFinding.Graph.AddUndirectedEdge("Fagaras", "Sibiu", 99);
 
    // Hirsova <-> Urziceni
    pathFinding.Graph.AddUndirectedEdge("Hirsova", "Urziceni", 98);
 
    // Iasi <-> Neamt
    pathFinding.Graph.AddUndirectedEdge("Iasi", "Neamt", 87);
    // Iasi <-> Vaslui
    pathFinding.Graph.AddUndirectedEdge("Iasi", "Vaslui", 92);
 
    // Lugoj <-> Mehadia
    pathFinding.Graph.AddUndirectedEdge("Lugoj", "Mehadia", 70);
    // Lugoj <-> Timisoara
    pathFinding.Graph.AddUndirectedEdge("Lugoj", "Timisoara",
111);
 
    // Oradea <-> Zerind
    pathFinding.Graph.AddUndirectedEdge("Oradea", "Zerind", 71);
    // Oradea <-> Sibiu
    pathFinding.Graph.AddUndirectedEdge("Oradea", "Sibiu", 151);
 
    // Pitesti <-> Rimnicu Vilcea
    pathFinding.Graph.AddUndirectedEdge("Pitesti", "Rimnicu Vilcea", 97);
 
    // Rimnicu Vilcea <-> Sibiu
    pathFinding.Graph.AddUndirectedEdge("Rimnicu Vilcea", "Sibiu",
80);
 
    // Urziceni <-> Vaslui
    pathFinding.Graph.AddUndirectedEdge("Urziceni", "Vaslui",
142);
 
    start = pathFinding.Graph.Nodes["Oradea"];
 
    end = pathFinding.Graph.Nodes["Neamt"];
 
    Console.WriteLine("\nBreadth First Search algorithm");
 
    Pathfinding.BreadthFirstSearch(start, end);
 
    foreach(Node n in pathFinding.Graph.Nodes)
      n.Data = null;
 
    Console.WriteLine("\n\nDepth First Search algorithm");
 
    Pathfinding.DepthFirstSearch(start, end);
 
    Console.WriteLine("\n\nShortest path");
 
    Pathfinding.ShortestPath(start, end);
 
    pathFinding.Graph.Clear();
 
    Console.ReadKey();
  }
}

Firstly we create a new instance of the Pathfinding class and two instances of the Node class that will reference the start and end city respectively.

The pathfinding object has a graph property that we use to store the nodes, that is, the cities of the map. To accomplish this the method AddNode of the Graph class is used.

The key that represents the node is the name of the city in this case.

After adding the cities to the graph it's time to connect the cities by means of the edges between them. For each undirected edge of the map the fourth overload of the AddUndirectedEdge method is used. The method receives as arguments the names of the edge's vertexes and the path cost.

Supposing we want to go from Oradea to Neamt, we must set the start and end node appropriately and that is done when the start and end node are assigned the values already present in the graph.

After everything is set up we can run the the breadth and depth first search methods. To start I call the the method BreadthFirstSearch already presented in the previous post Breadth and depth first search - part 1. The method receives the start and end nodes as arguments and traverses the graph according to the breadth first search algorithm. During the traversal it prints the paths in the screen so that it's easier to visually debug the code.

We use the same graph data to run the depth first search method but to avoid a wrong behavior it's necessary to set the data property of each graph's node to null. It's because such property is used to store a value indicating if that node was already visited during the path traversal of the breadth first search method.

OK. Now that the graph data is prepared we can run the depth first search method invoking the DepthSearchMethod of the Pathfinding class. This method receives the start and end nodes as arguments and traverses the graph according to the depth first search algorithm. During the traversal it prints the paths in the screen so that it's easier to visually debug the code.

The last and so important method is the ShortestPath one. The shortest path problem can be calculated through different algorithms. In this case the algorithm used (Dijkstra's algorithm) is suitable because we don't have negative costs otherwise we should use other algorithms. The ShortestPath method of the Pathfinding class receives as arguments the start and end nodes and prints in the screen the total distance of such a path and the cities travelled.

See the screenshot of the output:

Note: if you want to see a C++ implementation of the breadth and depth first search, check the third part of this series: Breadth and depth first search - part 3.

Get the complete code and executable of this post at:

http://leniel.googlepages.com/BreadthDepthFirstSearchCSharp.rar

Breadth and depth first search


Search algorithms
The idea behind search algorithms is to simulate the exploration of a space of states or search space through the generation of successor states already explored.

Tree based search algorithms
Tree search algorithms are the heart of searching techniques. The basic principle is that a node is taken from a data structure, its successors examined and added to the data structure. By manipulating the data structure, the tree is explored in different orders for instance level by level (breadth-first search) or reaching a leaf node first and backtracking (depth-first search).

Definitions

  • State - a representation of a physical configuration.
  • Node - a data structure part of a search tree. A node has the following properties: father, sons, depth and path cost.

States don't have father, sons, depth and path cost.

Types of problems

  • Deterministic accessible - simple states problem.
  • Deterministic inaccessible - multiple states problem.
  • Non deterministic inaccessible - contingency problem (maybe) - sensors must be used during the execution process. The solution is a tree or a policy. Many times alternates between search and execution.
  • Unknown search space - exploration problem (online).

Selecting a search space
The real world is extremely complex. The search space must be abstracted from the solution process. Abstracting we have:

  • Abstract state - the set of real states.
  • Abstract operator - combination of real actions.
  • Abstract solution - set of real paths that represents a solution in the real world.

Each abstract action must be easier than the action in the real problem.

A simple algorithm

function GeneralSearch(problem, strategy)
  return a solution or failure
 
Initialize the search tree using the initial state of the problem
 
loop do
  if there are no candidates for expansion then
    return failure
 
Choose a leaf node for expansion according to strategy
 
if the node contains a goal state then
  return the corresponding solution
else
  expand the node and add the resulting nodes to the tree
 
end

Algorithm implementation

function GeneralSearch(problem, QueueingFunction)
nodes <- MakeQueue(MakeNode(InitialState[problem]))
loop do
  if nodes is empty then
    return failure
  node <- RemoveFront(nodes) a leaf node for expansion according
to strategy
  if GoalTest[problem] applied to State(node) succeeds then
    return node
  nodes <- QueueingFunction(nodes, Expand(node, Operators[problem]))
end

Search strategies
A search strategy is a choice made between the methods used to expand the nodes. Each method has its proper order of expansion. Strategies are evaluated according to following parameters:

  • Time complexity - the number of generated or expanded nodes.
  • Space complexity - maximum number of nodes presented in the memory.
  • Optimality - the solution found has the minimum cost?
Complexities in time and space are measured in terms of:
  • b - maximum ramification factor of the search tree.
  • d - depth of the solution that has the minimum cost.
  • m - maximum depth of the search space (can be ∞) infinite.

The two strategies presented in this post (breadth and depth first search) are considered uninformed strategies because they only use the information available in the problem definition.

Breadth first search
The breadth first search expands the less deep node. The data structured used to implement this search algorithm is a queue.

Breadth first search properties
Is complete? Yes, if (b is finite).

Time: 1 + b2 + b3 + … + bd = O(bd). e.g.: exponential in d

Space: O(bd) (maintains all the nodes in memory)

Optimal? Yes, if cost per step is 1; in general is not optimal.

Space is the big problem for can generate nodes at a rate of 1 MB / sec. 24 hours = 86 GB.

Depth first search
The depth first search expands the deepest node.

The data structure used to implement this search algorithm is a stack.

An important note about the depth first search is that it can execute infinite cycles so it’s mandatory to check the states already visited to avoid such infinite cycles.

Depth first search properties
Is complete? No. It is imperfect in spaces of infinite depth or in cyclic paths. Is complete only in a finite search space.

Time: O(bm). Is very bad if m is a lot greater than d. If solutions are dense can be fastest than the breadth first search.

Space: O(bm) e.g.: linear in space

Optimal? No.

Working problem: vacation in Romania
In this post let's explore a simple states problem as shown in the following picture:

Suppose we are taking a vacation in Romania. More specifically we are in the city called Arad. We want to go to Bucharest the capital city of Romania and our flight takes off tomorrow. Breaking down our problem, we have:

  • Objective - go to Bucharest.
  • Search space - several cities to get to Bucharest.
  • Operator - drive through the cities.

In this case a solution is a sequence of cities so that the last city is Bucharest.

A valid path could be: Arad, Sibiu, Fagaras, Bucharest. e.g.: Arad -> Zerind represents a complex set of possible paths, returns, pauses for resting, etc.

To guarantee the realizability, any real state in “Arad” must take us to some real state in “Zerind”.

The problem's formulation can be:

  • Initial state - Arad
  • Operator (or successor function S(x)) - eg.: Arad -> Zerind, Arad -> Sibiu, etc.
  • Goal test - can be explicit or implicit as x = Bucharest or NoDirty(x).
  • Path cost (additive) - can be the sum of distances, used operators, etc.

The solution is a sequence of operators that takes from the initial state to the goal state.

Implementing the breadth and depth first search in C#
The following methods use two classes implemented by Scott Mitchell [1]. The classes are: Node and EdgeToNeighbor.

I just added a new property called PathParent in the node class.

public static void BreadthFirstSearch(Node start, Node end)
{
  Queue<Node> queue = new Queue<Node>();
 
  queue.Enqueue(start);
 
  while(queue.Count != 0)
  {
    Node u = queue.Dequeue();
 
    // Check if node is the end
    if(u == end)
    {
      Console.Write("Path found.");
 
      break;
    }
    else
    {
      u.Data = "Visited";
 
      // Expands u's neighbors in the queue
      foreach(EdgeToNeighbor edge in u.Neighbors)
      {
        if(edge.Neighbor.Data == null)
        {
          edge.Neighbor.Data = "Visited";
 
          if(edge.Neighbor != end)
          {
            edge.Neighbor.PathParent = u;
 
            PrintPath(edge.Neighbor);
          }
          else
          {
            edge.Neighbor.PathParent = u;
 
            PrintPath(edge.Neighbor);
 
            return;
          }
 
          Console.WriteLine();
        }
        /* shows the repeated nodes
        else
        {
          Console.Write(edge.Neighbor.Key);
        } */
 
        queue.Enqueue(edge.Neighbor);
      }
    }
  }
}
public static void DepthFirstSearch(Node start, Node end)
{
  Stack<Node> stack = new Stack<Node>();
 
  stack.Push(start);
 
  while(stack.Count != 0)
  {
    Node u = stack.Pop();
 
    // Check if node is the end
    if(u == end)
    {
      Console.WriteLine("Path found");
 
      break;
    }
    else
    {
      u.Data = "Visited";
 
      // Store n's neighbors in the stack
      foreach(EdgeToNeighbor edge in u.Neighbors)
      {
        if(edge.Neighbor.Data == null)
        {
          edge.Neighbor.Data = "Visited";
 
          if(edge.Neighbor != end)
          {
            edge.Neighbor.PathParent = u;
 
            PrintPath(edge.Neighbor);
          }
          else
          {
            edge.Neighbor.PathParent = u;
 
            PrintPath(edge.Neighbor);
 
            return;
          }
 
          Console.WriteLine();
 
          stack.Push(edge.Neighbor);
        }
        /* shows the repeated nodes
        else
        {
          Console.Write(edge.Neighbor.Key);
        } */
      }
    }
  }
}

Summary
The formulation of a problem requires abstraction to avoid irrelevant details of the real world so that a search space can be defined and explored.

In a next post I'll dive in more details and explain how to use the breadth and depth search methods. We'll execute a test case using the Romania map shown in the picture above, print the traveled paths in the screen, calculate the shortest path possible between two cities using Dijkstra's algorithm and print such a path in the screen. Before that I recommend the reading of the excellent revision about data structures written by Scott Mitchell [1]. Although I only use the facts exposed in part 5 of Scott's article: From Trees to Graphs, it's worth to read starting in part 1. For now, look and think about the breadth and depth first search implementation in C#.

References
[1] Mitchell, Scott. An Extensive Examination of Data Structures. 2004. Available at <http://msdn2.microsoft.com/en-us/library/aa289152(VS.71).aspx>. Accessed on January 8, 2008.

[2] Artificial Intelligence Depot. Blind search. Available at <http://ai-depot.com/Tutorial/PathFinding-Blind.html>. Accessed on January 8, 2008.

Critical Path Method


The critical path method or just CPM is a mathematically based algorithm for scheduling a set of project activities.

The essential technique for using CPM is to construct a model of the project that includes the following:
  • A list of all activities required to complete the project (also known as