Pierwszy krok w stronę pracy inżynierskiej

Postanowiłem przerwać delikatna ciszę w moich poczynaniach blogowych spowodowana sesją egzaminacyjną oraz trwającymi jeszcze upragnionymi praktykami zawodowymi o których szerzej wspomnę w najbliższym wpisie. :) Przede mną ostatni semestr studiów co powoduje, że zaczynam rozmyślać nad częścią praktyczną mojej pracy inżynierskiej. Zamierzam napisać niewielki system do tworzenia i przeprowadzania testów sprawdzających więdzę. Jakiś czas temu zainteresowałem się architekturą SOA (Service Oriented Architecture) – spróbuję niebawem głębiej przyjżeć się jej aspektom i wpleść pewne jej założenia do swojego projektu. Na dzień dzisiejszy w zgłębianiu wiedzy wspomagać mnie będzie ta oto książka:

Service Oriented Architecture For Dummies

Dodatkowo w zakamarkach mojej głowy pojawiają się takie oto technologie:

  • ADO. Entity Framework, Nhibernate
  • Web Services
  • MS SQL Server 2008
  • UML

To wszystko na początek – bez specjalnego rozwijania co i jak zostanie stworzone. Być może jakieś problemy które napotkam i rozwiążę znajdą się tutaj szczegółowo opisane. Trzymajcie kciuki drodzy czytelnicy za moje skromne inżynierskie przedsięwzięcie. ;)

Zabawa w szyfrowanie – RSA

W ramach zaliczenia laboratorium jednego z przedmiotów szkolnych miałem za zadanie napisać niewielki programik szyfrujący. Wybrałem szyfr RSA – jak podaje wikipedia jego nazwa jest akronimem utworzonym z pierwszych liter nazwisk jego twórców – Rivest, Shamir, Adleman. Szyfrowanie i odszyfrowanie tekstu z wykorzystaniem RSA nie jest procesem zbyt skomplikowanym – wymaga jednak operowania dość dużymi liczbami pierwszymi co zmusiło mnie do rezygnacji ze standardowego typu int w C# który miał za mały zakres. W bezdennych zasobach internetowych znalazłem strukturę którą wykorzystałem w swojej implementacji -> BigInt.

Etapy szyfrowania umożliwiające zakodowanie i odkodowanie tekstu:

  1. Wylosuj dwie duże liczby pierwsze p i q
  2. Oblicz n = p * q
  3. Oblicz phi = (p – 1) * (q – 1)
  4. Oblicz e -> Para (e, n) tworzy klucz publiczny wykorzystywany do szyfrowania
  5. Oblicz d -> Para (d, n) tworzy klucz prywatny wykorzystywany do odszyfrowania
  6. Szyfrowanie znaku:
    • Zamiana znaku na kod Ascii
    • Oznaczmy c jako zaszyfrowany znak c = asciiNume mod n
  7. Odszyfrowanie znaku:
    • Aby otrzymać na powrót zaszyfrowany znak: t = cd mod n

Zamiana znaku na kod Ascii i na odwrót:

          char ch = "A";
          int asciiNum = (int)ch;
          ch = (char)asciiNum;

Nie chcę jednak zagłębiać się maksymalnie w poszczególne etapy szyfrowania i odszyfrowania, a skupię się na samym specyficznym sposobie zapisu zaszyfrowanych znaków który sobie wymyśliłem i zakodziłem. ;)

Przykład 1:

Powiedzmy, że mam wyraz „Ola”, który zaszyfrowałem i chcę zapisać do pliku następnie odczytać i odszyfrować. Liczby powstałe w procesie szyfracji: 186220802, 16730814, 137205435. I tutaj pojawia się pytanie w jaki sposób mogę zapisać liczby tak aby osoba otwierająca plik nie domyśliła się, że poszczególne liczby reprezentują znaki? Jednym ze sposobów jest na przykład oddzielanie liczba przecinkami, kropkami lub innymi znakami interpunkcyjnymi. Mnie to jednak nie zadowalało wpadłem więc na pomysł, aby ustalić na stałe znaki „specjalne”, które zostaną zakodowane i „dodane” do powstałych liczb.

Przykład 2:

Ustalam, że moim znakiem specjalnym będzie przecinek następnie szyfruje jego kod i otrzymuję na przykład liczbę: 164916224.
Dodaje do każdej z wyżej powstałych liczb znak specjalny i do pliku wędruję taki oto ciąg liczb 18622080216491622416730814164916224137205435 :)

Jest jednak problem w sytuacji gdy ciąg znaków wygląda tak „O,l,a,,,”, a znakiem specjalnym jest przecinek. Odczytanie z pliku zaszyfrowanego ciągu z użyciem jako znak specjalny przecinka znacząco utrudnia odszyfrowanie – mamy długi ciąg liczb chcemy go podzielić po przecinku, ale tak naprawdę nie wiemy ile razy przecinek mógł wystąpić w danym wyrażeniu. By mieć możliwość swobodnego podziału tego typu ciągu liczb i odzyskać poszczególne liczby, aby je odkodować następnie odczytać oryginalny tekst źródłowy ustaliłem znaki specjalne spoza kodów Ascii. – Są to losowa przeze mnie wybrane trzy dziwne „krzaczki” które mi pięknie oddzielają poszczególne zakodowane znaki.

Klasę realizującą szyfrowanie i odszyfrowanie na podstawie obliczonych kluczy nazwałem Rsa. Jako, że spróbuję zmierzyć się z implementacją innych sposobów szyfrowania utworzyłem interfejs który w moim programie będzie reprezentował klasy szyfrujące i deszyfrujące.

Interfejs ICipher:

public interface ICipher
{
    StringCollection Encrypt(StringCollection sc);
    StringCollection Encrypt(string path);
    StringCollection Decrypt(StringCollection sc);
    StringCollection Decrypt(string path);
    void SaveToFile(string savePath);
}

Klasa Rsa:

/// <summary>
/// Klasa szyfrujaca RSA
/// </summary>
public class Rsa : ICipher
{
   #region Fields & Properties
   private BigInt _n;
   private BigInt _e;
   private BigInt _d;

   /// <summary>
   /// Tablica znakow specjalnych
   /// </summary>
   private readonly char[] _chars = { 'ฒ', 'ฑ', 'ฐ' };
   private StringCollection _scTemp;

   /// <summary>
   /// Zwraca tablice zawierajaca klucz prywatny i publiczny
   /// Indexy: 0 = n, 1 = e, 2 = d
   /// </summary>
   public BigInt[] Keys
   {
        get
        {
            return new BigInt[]
            {
                 _n,
                 _e,
                 _d
             };
         }
   }
   #endregion

   /// <summary>
   /// Konstruktor przyjmujacy wartosci umozliwiajace szyfrowanie
   /// </summary>
   /// <param name="n">n</param>
   /// <param name="e">e</param>
   /// <param name="d">d</param>
   public Rsa(BigInt n, BigInt e, BigInt d)
   {
            _n = n;
            _e = e;
            _d = d;
   }

    /// <summary>
    /// Konstruktor tworzacy obiekt Rsa tylko do odszyfrowywania danych
    /// Przyjmuje pare tworzaca klucz prywatny
   /// </summary>
   /// <param name="n">n</param>
   /// <param name="d">d</param>
   public Rsa(BigInt n, BigInt d)
   {
         _n = n;
         _d = d;
   }

   #region ICipher Members
   /// <summary>
   /// Szyfruje podana kolekcje stringow
   /// </summary>
   /// <param name="sc">Kolekcja stringow</param>
   /// <returns>Kolekcja zaszyfrowanych stringow</returns>
   public StringCollection Encrypt(StringCollection sc)
   {
       StringCollection cipheredText = new StringCollection();
       _scTemp = null;

       foreach (string s in sc)
       {
            //Zamiana stringa na tablice znakow
            char[] charArray = s.ToCharArray();

            int index = 0;
            string str = string.Empty;

            //Pobranie zakodowanych znakow specjalnych
            BigInt[] biArray = GetCipheredSpecialChar();

            foreach (char ch in charArray)
            {
                //Wylosowanie znaku specjalnego wykorzystanego do szyfracji
                index = Randomizer.UniqueInstance.Next(0, biArray.Count());

                //Szyfrowanie
                BigInt c = BigInt.Parse(ch.GetNumberOfChar().ToString());
                c = c.FastModuloPower(_e, _n);

                //Znak + znak specjalny
                str += c.ToString() + biArray[index].ToString();
            }

            cipheredText.Add(str);
       }
       _scTemp = cipheredText;

       return cipheredText;
   }

   /// <summary>
   /// Szyfruje wskazana zawartosc pliku
   /// </summary>
   /// <param name="path">Sciezka do pliku</param>
   /// <returns>Kolekcja zaszyfrowana</returns>
   public StringCollection Encrypt(string path)
   {
        StringCollection sc = null;
        _scTemp = null;

        try
        {
            string[] stringArray = File.ReadAllLines(path, Encoding.Default);
            sc = new StringCollection();
            sc.AddRange(stringArray);
            sc = Encrypt(sc);
        }
        catch (IOException)
        {
            throw;
        }
        catch (Exception)
        {
            throw;
        }

        return sc;
   }

   /// <summary>
   /// Deszyfracja z kolekcji stringow
   /// </summary>
   /// <param name="sc">Kolekcja stringow</param>
   /// <returns>Zwraca rozszyfrowany text</returns>
   public StringCollection Decrypt(StringCollection sc)
   {
        _scTemp = null;
        //Konwersja zakodowanych znakow specjalnych
        //i wrzucenie wszytkich do tablicy string
        string[] cipheredSpecialCharArray = GetCipheredSpecialChar().ToList()
                     .ConvertAll<string>((BigInt bi) =>
                {
                    return bi.ToString();
                }).ToArray();

        StringCollection decodedTextCollection = new StringCollection();

        foreach (string s in sc)
        {
             //Podział zakodowanych znakow metoda Split wedlug znakow specjalnych
             List<string> cStringArray = s.Split(cipheredSpecialCharArray, StringSplitOptions.RemoveEmptyEntries).ToList();

             string decodedText = string.Empty;
             //Iteracja po tablicy zawierajacej poszczegolne znaki w postaci zakodwanych liczb
             foreach (string str in cStringArray)
             {
                 BigInt de = BigInt.Parse(str).FastModuloPower(_d, _n);
                 char c = de.GetCharFromNumber();
                 decodedText += c;
             }
             //Dodanie do kolekcji utworzonej linii textu
             decodedTextCollection.Add(decodedText);
        }

        _scTemp = decodedTextCollection;

        return decodedTextCollection;
   }

   /// <summary>
   /// Deszyfruje text odczytany z pliku
   /// </summary>
   /// <param name="path">Sciezka do pliku</param>
   /// <returns>Zwraca rozszyfrowany text</returns>
   public StringCollection Decrypt(string path)
   {
        _scTemp = null;

        StringCollection sc = null;
        try
        {
             string[] stringArray = File.ReadAllLines(path, Encoding.Default);
             sc = new StringCollection();
             sc.AddRange(stringArray);
             sc = Decrypt(sc);
        }
        catch (IOException)
        {
              throw;
        }
        catch (Exception)
        {
              throw;
        }

        return sc;
   }

   /// <summary>
   /// Metoda realizujaca zapis do pliku
   /// </summary>
   /// <param name="savePath">Sciezka do zapisu</param>
   /// <param name="sc">Kolekcja do zapisu</param>
   public void SaveToFile(string savePath)
   {
         if (!String.IsNullOrEmpty(savePath) && _scTemp != null)
         {
              StreamWriter writer = null;
              try
              {
                  foreach (string s in _scTemp)
                  {
                      if (writer == null)
                      {
                          writer = new StreamWriter(savePath, false, Encoding.UTF8);
                      }

                      writer.WriteLine(s);
                 }
             }
             catch (IOException)
             {
                   throw;
             }
             catch (Exception)
             {
                  throw;
             }
             finally
             {
                  if (writer != null)
                  {
                       writer.Close();
                  }
             }
        }
   }
    #endregion

    #region Help Methods

    /// <summary>
    /// Metoda umozliwiajaca zapis kluczy do pliku
    /// </summary>
    /// <param name="savePath">Sciezka do zapisu</param>
    public void SaveKeysToFile(string savePath)
    {
         StreamWriter writer = null;
         try
         {
             writer = new StreamWriter(savePath, false, Encoding.UTF8);
             writer.WriteLine("Klucz publiczny:");
             writer.WriteLine("KP: (" + _e.ToString() + "; "
                           + _n.ToString() + ")");
             writer.WriteLine("Klucz prywatny:");
             writer.WriteLine("KPr: ( d=" + _d.ToString() + "; n="
                           + _n.ToString() + ")");
         }
         catch (IOException)
         {
              throw;
         }
         catch (Exception)
         {
              throw;
         }
         finally
         {
                if (writer != null)
                {
                    writer.Close();
                }
         }
    }

    /// <summary>
    /// Metoda pomocnicza szyfrujaca kody znakow specjalnych
    /// </summary>
    /// <returns>Tablica kodow znakow specjalnych</returns>
    private BigInt[] GetCipheredSpecialChar()
    {
         BigInt[] biArray = new BigInt[_chars.Length];
         for (int i = 0; i < _chars.Length; i++)
         {
             biArray[i] = BigInt.Parse(_chars[i].GetNumberOfChar()
                                 .ToString()).FastModuloPower(_e, _n);
         }

         return biArray;
    }
    #endregion
}

Moje spojrzenie na Code Camp

ccwawa09_140x140.jpg

Fragment „będę na” niestety mogę już zamienić na „byłem na”. Wszystko co dobre tak szybko się kończy, ale mam nadzieję, że za rok doznam powtórki w podobnym stylu.

Do Warszawy wyruszyłem dość wcześnie ponieważ miała to być moja dziewicza podróż zakamarkami stolicy obawiałem się, że mogę nieco błądzić i dlatego postanowiłem zapewnić sobie bufor czasowy. Po 2,5 godziny koszmarnej jazdy małym, ciasnym busikiem w pozycji embrionalnej około godziny 7 postawiłem swoją wielką stopę na Placu Defilad w centrum miasta. ;) Po 20 minutach błądzenia udało mi się odnaleźć odpowiedni przystanek i autobus którym bez przeszkód dotarłem pod samą siedzibę Microsoft – chyba byłem pierwszym oczekującym. :) – I tutaj podziękowania należą się organizatorom, którzy umieścili na stronie internetowej wydarzenia numery autobusów dojeżdżających na miejsce.

Zostałem przywitany przez bardzo miłą Panią, która zaprosiła mnie na skosztowanie przygotowanych specjalnie dla uczestników napojów (kawa, soczki, woda, mleko. :)) – Jestem przekonany, że tego dnia nikt spragniony, ani nawet diabelnie głodny stamtąd nie wyszedł (w połowie konferencji wchłanialiśmy pizze! :)).

„Programowanie równoległe i rozproszone „ – Pan Tomasz Kopacz

Zdecydowanie na ten temat było za mało czasu. Dużo informacji, moim zdaniem przekaz był jasny i zrozumiały. Dowiedzieliśmy się jakiego rodzaju narzędzia znajdują się w .NET 4.0 oraz jaki będzie kierunek dalszego rozwoju programowania równoległego.

Ogólną ocenę przyznaje 8/9

„Media w Silverlight – dostarczanie niezapomnianych wrażeń” – Panowie Piotr Czekała, Krzysztof Bartkowski

Generalnie o Silverlight nie mam zbyt wielkiego pojęcia. Wiem tylko, że to co zazwyczaj widzę w internecie i jest wykonane dzięki tej technologii wygląda w większości przypadków imponująco. Panowie zdecydowanie to potwierdzili. Bardzo fajny opis wykorzystywanych narzędzi i technologii – poparty faktycznie zapierającymi dech w piersiach przykładami. :) Jednak było coś co mi strasznie przeszkadzało podczas tej prelekcji – jeden z prowadzących ciągle trzymał rękę w kieszeni – odruch nerwowy który zdecydowanie powinien poprawić.

Ogólną ocenę przyznaję 7/9

„Wyjątki kontrolowane w C#” – Pan Bartłomiej Legiędź – Zwycięzca Speaker Idola – Łódź.

Ta sesja podobała mi się najbardziej. Jasno zwięźle i na temat. Nieoceniona dawka informacji w pigułce.
Projekt nad którym pracuję Pan Bartłomiej:
Exceptional Plugin
Książkę którą polecił podczas prelekcji i po którą myślę, że za jakiś czas sięgnę ;):
Framework Design Guidelines – Krzysztof Cwalina, Brad Abrams

Ogólną ocenę przyznaję 9/9 :)

„ Ile cukru w cukrze – IronPython i jego zastosowania” – Pan Michał Żyliński

Wprawdzie prowadzący nie przekonał mnie do IronPythona, ale dał dobry wstęp do programowania w tym języku. Pod koniec sesji przedstawił bardzo ciekawy program prowadzony przez Microsoft skierowany do niewielkich firm dzięki któremu mogą przyśpieszyć swój rozwój – Zachęcam do zapoznania się ze szczegółowymi informacjami na stronie internetowej: BizSpark

Ogólną ocenę przyznaję 7/9

„Designer + Programista = Produkt Problem + Pomysł = Aplikacja” – Pan Andrzej Piotrowski – Zwycięzca Speaker Idola – Warszawa

Po tej sesji kolega siedzący obok zapytał mnie jak to możliwe, że ten Pan wygrał speaker idola… Może tragedii nie było, ale najsłabsza sesja. Chwilami czułem jak by prowadzący, nie wiedział o czym mówi. Być może jak sam powiedział zaraz po sesji, był to jego zły dzień.

Ogólną ocenę przyznaję 5/9

Ogólnie całej konferencji przyznaję ocenę 8/9 :)

Pierwsza praca

Nadszedł czas kiedy obowiązki związane ze szkołą mogę odsunąć na dalszy plan. Zbliża się mój ostatni semestr studiów, a wraz z nim szereg przedmiotów którym nie będę musiał poświęcać stu procentowej uwagi. Postanowiłem, więc rozpocząć poszukiwania swojej pierwszej pracy i podzielić się tą nieodpartą chęcią właśnie tutaj. Należę do grona osób które podczas studiów skupiały się na nauce, rozwoju własnej osoby, w moim przypadku mogę jeszcze dodać setki godzin spędzonych w domu nad pogłębianiem umiejętności programistycznych (w końcu studia to tylko zarys wielu zagadnień itd.). Skoro prawie nie mam udokumentowanego doświadczenia (napisałem tutaj „prawie” ponieważ mam za sobą 1 miesiąc praktyki podczas której zajmowałem się implementacją niewielkiego programu raportującego, ale czy warto o tym wspominać w CV…) , a te które posiadam zostało zdobyte podczas trwających jeszcze studiów (czyli tzw.„doświadczenie akademickie„) to czym mam zachęcić pracodawcę, aby pozwolił mi się zmierzyć z rozmową wstępną na stanowisko programistyczne? Co może być moją rekomendacją? Te pytania dręczą mnie od dłuższego czasu.

Może?

  • setki godzin spędzonych nad rozwiązywaniem problemów implementacyjnych związanych
    z zadaniami szkolnymi, pojawiającymi się na forum codeguru.pl, csharp.pl
  • chęć wejścia na ścieżkę certyfikacyjną poprzez uczestnictwo w zajęciach „Study Group 70-536” organizowanych wspólnymi siłami wielu osób na mojej uczelni
  • CV – w którym zapewne jedyne co mogę zrobić to wymienić to z czym zetknąłem się podczas pracy nad własnym rozwojem
  • każdy wpis, każdy komentarz na tym blogu
  • wiele podziękowań od kolegów i koleżanek na mojej uczelni za pomoc w ciągłej ich walce z programowaniem
  • etc.

Jeśli odwiedzających ten oto blog (pracodawcy i nie tylko) taka rekomendacja przekonuje-zapraszam z przyjemnością zmierzę się z rozmową wstępną, z przyjemnością dam z siebie wszystko, bo jeśli już coś robię to wkładam w to maksimum energii.

Z poważaniem
Piotr Zarzycki

Dżemik z egzotycznych owoców – Abstract Factory :)

Zgodnie z zapowiedzią prezentuję własną interpretację jednego z zadań kończącego rozdział książki p.t. „C# 3.0 Design Patterns.”. Rozdział ten dotyczył wzorca projektowego „Abstract Factory”.

Delikatny zarys tego czym chcę się podzielić:

  • Krótko o tworzeniu własnych typów generycznych w C#
  • Zadanie, opis wzorca
  • Implementujemy, produkujemy (jemy i sprzedajem ;))

Typy generyczne:

W języku C# mamy możliwość tworzenia własnych typów generycznych.

class MyGenericType<T>
{
      private T _t;
}

Powyższa klasa daje możliwość zdefiniowania typu na jakim będzie mogła operować. Dodatkowo mamy możliwość nadawania ograniczeń na typy podawane podczas inicjalizacji obiektu.

 class MyGenericType<T> where T : IOperation, new()
{
        private T _t;
}

Ograniczenie to oznacza, że typ na którym będzie operowała klasa „MyGenericType” musi implementować interfejs IOperation, oraz jest zobowiazana posiadać jawnie zadeklarowany konstruktor.

Zadanie i opis wzorca:

Jeden z rozdziałów książki traktował o wzorcu projektowy „Factory Method” – przykład prezentujący działanie wzorca był opisem zdjęcia obrazującego źródło dostaw owoców awokado. Owoce te były dostarczane z różnych krajów Afryki. Zadanie kończące rozdział o wzorcu projektowym „Abstract Factory” odnosi się właśnie do tego zdjęcia – Należy wykorzystać „Abstract Factory” i opisać poniższą sytuację.

abstactfactory.png

Abstract Factory jest to wzorzec udostępniający instancje obiektów z istniejącej rodziny klas (Mam tutaj na myśli klasy dziedziczące po wspólnej klasie, wspólnym interfejsie). Obiekty te są odizolowane od klienta nimi zainteresowanego, co daje możliwość ich wymiany podmieniając tylko ich fabrykę. „Abstract Factory” to wzorzec który odpowiada za to co jest tworzone, a nie jak jest tworzone.

Implementujemy, produkujemy (jemy i sprzedajem ;))

Postanowiłem puścić wodzę fantazji i rozpocząłem budowę fabryki od zdefiniowania jej odpowiedzialności. Powstał genericsowy interfejs wraz z poniższymi ograniczeniami.

Interfejs „IAfricaFactory”:

interface IAfricaFactory<AfricaFruit>
        where AfricaFruit : IAfricaFruit, new()
{
     IInfoFruit GetNorthAfricaFruit();
     IInfoFruit GetSouthAfricaFruit();
     IOperationFruit GetROSA();
     IOperationFruit GetSudan();
}

Poszczególne metody będą zwracać instancję obiektów które implementują takie oto dwa interfejsy.

Interfejs „IInfoFruit”:

 interface IInfoFruit
 {
     string PriceFruit { get; }
     Quality QualityFruit { get; }
}

Interfejs „IOperationFruit”:

 interface IOperationFruit
 {
     string Marmalade();
     string OnSale();
}

Fabryka ma dostarczać i przetwarzać owoce nim jednak rozpocząłem ich tworzenie powstał interfejs zlecający im odpowiedzialność :)

Interfejs „IAfricaFruit”

interface IAfricaFruit
{
     string Price { get; }
     Quality QualityFruit { get; }
     string Marmelade();
     string OnSale();
}

Owoce:

Klasa „Avocado”:

class Avocado : IAfricaFruit
{
     #region IAfricaFruit Members
     public string Price
     {
        get
        {
            return "10 zl za kilogram";
        }
     }

     public Quality QualityFruit
     {
         get
         {
             return Quality.Druga;
         }
     }

     public string Marmelade()
     {
         return "Dżemik z avocado! :)";
     }

     public string OnSale()
     {
          return "Dżemik z owoców avocado sprzedajemy po: " + Price;
     }
     #endregion

     public override string ToString()
     {
          return "Avocado";
     }
}

Podobnie powstawały klasy „Banana” oraz „Lemon”. Następnie stworzyłem konkretną fabrykę(klasę) generyczną implementującą interfejs „IAfricaFactory”. Metody tworzą instancję klas implementujących intefejsy „IInfoFruit” oraz „IOperationFruit”.

Klasa „AfricaFactory”

class AfricaFactory<AfricaFruit> : IAfricaFactory<AfricaFruit>
        where AfricaFruit : IAfricaFruit, new()
{
     #region IAfricaFactory<AfricaFruit> Members
     public IInfoFruit GetNorthAfricaFruit()
     {
         return new NorthAfrica<AfricaFruit>();
     }

     public IInfoFruit GetSouthAfricaFruit()
     {
         return new SouthAfrica<AfricaFruit>();
     }

     public IOperationFruit GetROSA()
     {
         return new RepublicOfSouthAfrica<AfricaFruit>();
     }

     public IOperationFruit GetSudan()
     {
         return new Sudan<AfricaFruit>();
     }
     #endregion
}

Moja fabryka potrzebowała już tylko „kontaktu” z konkretnymi częściami bądź krajami Afryki. :)

Klasa „NorthAfrica”:

class NorthAfrica<AfricaFruit> : IInfoFruit
        where AfricaFruit : IAfricaFruit, new()
{
     private AfricaFruit _africaFruit;

     public NorthAfrica()
     {
           _africaFruit = new AfricaFruit();
     }
     #region IInfoFruit Members
     public string PriceFruit
     {
          get
          {
               return _africaFruit.Price;
          }
     }

     public Quality QualityFruit
     {
          get
          {
               return _africaFruit.QualityFruit;
          }
     }
     #endregion
}

Klasa implementuje interfejs „IInfoFruit”. Właściwości spełniające wymagania interfejsu „sięgają” do obiektu którego typ zostanie określony podczas tworzenia egzemplarza tej oto klasy, jednak jest pewne, że dostarczony typ musi implementować interfej „IAfricaFruit” oraz musi zawierać zadeklarowany jawnie konstruktor. Obiek dostarczanego typu tworzę w konstruktorze klasy „NorthAfrica”

Klasa „RepublicOfSouthAfrica”:

class RepublicOfSouthAfrica<AfricaFruit> : IOperationFruit
        where AfricaFruit : IAfricaFruit, new()
{
     private AfricaFruit _africaFruit;

     public RepublicOfSouthAfrica()
     {
          _africaFruit = new AfricaFruit();
     }

     public string Marmalade()
     {
          return _africaFruit.Marmelade();
     }

     public string OnSale()
     {
            return _africaFruit.OnSale();
     }
}

Klasa implementuje interfejs „IOperationFruit”. Tak samo jak w klasie „NorthAfrica” sięgam do zwartości typu który zostanie dostarczony podczas tworzenia egzemplarza klasy. Podobnie powstały klasy „SouthAfrica” (implementuje interfejs „IInfoFruit”) oraz „Sudan” (implementuje interfejs „IOperationFruit”). Mam już wszystko czego potrzebuję. Pozostaję tylko sprzedawać i jeść pyszny dżemik z owoców afrykańskich. ;)

Wywołanie:

//Fabryka Avocado
IAfricaFactory<Avocado> factoryOfBanana = new AfricaFactory<Avocado>();
//Dostarczam instancje obiektu "NorthAfrica"

IInfoFruit northAfrica = factoryOfBanana.GetNorthAfricaFruit();
//Wykorzystuje dostępne wlasciwosci
//aby sprawdzic cene i jakosc owocow

Console.WriteLine(northAfrica.PriceFruit);
Console.WriteLine("Jakość owoców: {0}", northAfrica.QualityFruit);
 //20zl za kilogram
//Jakosc owocow: Pierwsza

IOperationFruit RPA = factoryOfBanana.GetROSA();
//Po ile sprzedajemy :)
string p = RPA.OnSale();
Console.WriteLine(RPA.OnSale());
//Dżemik!!
string d = RPA.Marmalade();
Console.WriteLine(RPA.Marmalade());

//Dzemik z owocow avocado sprzedajemy po: 10 zl za kilogram
 //Dzemik z avocado! :)

Do fabryki możemy dostarczać kolejne owoce (klasy). Ważne jest aby każdy dostarczony typ do utworzonej przy użyciu typów generycznych fabryki implementował interfejs IAfricaFruit. :) Miłego analizowania. ;)

Konwersja liczb binarny => dziesiętny i nauka po fakcie. ;)

W swoim projekcie z algorytmów genetycznych potrzebowałem przekonwertować liczbę zapisaną w systemie binarnym na system dziesiętny.
Liczba ta była przechowywana w tablicy typu int.

Utworzyłem taką oto funkcję konwertującą liczby z systemu binarnego na dziesiętny „na piechotę” ;):

public int ConvertFromBinaryToInt(int[] binaryNum)
{
    //Wykladnik potegi
    int power = 0;
    //Aktualna wartosci
    int value = 0;
    //Czytam zawartosc tablicy od konca
    for (int i = binaryNum.Length - 1; i >= 0; i--)
    {
         //Jesli napotkam w tablicy 1
         if (binaryNum[i] == 1)
         {
             //value + 2^power
             value += (int)Math.Pow(2, power);
         }
         power++;
     }

     return value;
}

Zorientowałem się jednak po napisaniu powyższej metody że można to osiągnąć w trywialny sposób:

public int ConvertFromBinaryToInt(int[] binaryNum)
{
  object[] objArray = new object[binaryNum.Length];
  binaryNum.CopyTo(objArray, 0);
  //lacze zawartosc tablicy
  //object[] w jeden string
  string str = String.Concat(objArray);

 //Konwersja na system
 //dziesietny z dwojkowego
  return Convert.ToInt32(str, 2);
}

Chyba każdemu zdarzyło się napisać coś, a dopiero później dojść do wniosku, że można było to osiągnąć w dużo prostszy sposób. ;)

Szkoła cz. 2 – Winner Takes All

Prowadzący zajęcia ze Sztucznej Inteligencji nie pozwala odpocząć moim szarym komórkom serwując kolejne zadanie z sieci neuronowych. Tym razem jednak nie musiałem rozwiązywać konkretnego problemu, a jedynie przedstawić działanie algorytmu.

Zadanie:

Napisać program prezentujący w sposób graficzny naukę neuronów stosując proces nauki bez nauczyciela. Uczenie przeprowadzić z wykorzystaniem jednego z algorytmów WTA (Winner Takes All) lub WTM (Winner Takes Most).

Krótkie postawy teoretyczne:

Nauka bez nauczyciela (sieć samoorganizująca się) – W tym procesie nauki pożądana odpowiedź sieci nie jest znana. Sieć nie posiadając informacji o poprawności danych powstałych na wyjściu, uczy się poprzez analizę pobudzenia, w trakcie tej analizy parametry sieci podlegają zmianom.

Nauka z algorytmem WTA:

  1. inicjalizacja wag sieci
    • neuron1 (w1, w2) = (1, 3)
    • neuron2 (w1, w2) = (2, 4)
  2. wzór na obliczenie odległości wektora wejściowego do wag każdego z neuronów:
    • d = sqrt((w1 – x1)^2 + (w2 – x2)^2)
  3. wybranie neuronu zwycięzcy (wygrywającego) dla którego odległość wag od wektora wejściowego jest najmniejsza (na podstawie Euklidesowej miary odległości).
    • Zwyciężył neuron2 ponieważ ma odległość mniejszą od neuron1.
  4. zmiana wartości poszczególnych wag tego neuronu przyjmując, że szybkość nauki wynosi n = 0,6
  5. powtórzenie kroków 2-5 dla wszystkich przykładów uczących.
  • Obliczenia (d):

    dn1 = sqrt((1 – 5)^2 + (3 – 8)^2) = sqrt(41) = 6,40

    dn2 = sqrt((2 – 5)^2 + (4 – 8)^ 2) = sqrt(25) = 5

  • Obliczenia (w):

    w1’ = w1 + n * (x1 – w1) = 2 + 0,6 * (5 – 2) = 3,8

    w2’ = w2 + n * (x2 – w2) = 4 + 0,6 * (8 – 4) = 6,4

Implementacja:

Cała samoorganizująca się sieć miała składać się jakby z dwóch części:

  1. Rodzin losowo wybranych punktów
  2. Losowo lub „ręcznie” dodanych neuronów

Postanowiłem więc utworzyć takie oto klasy reprezentujące powyższą sytuację:
Klasa „Characteristics”:

Klasa abstrakcyjna zawierająca właściwość zwracającą rodziny punktów, bądź utworzone neurony.
Zawiera także metodę Prepare() przygotowująca rodziny punktów, bądź neurony.

public abstract class Characteristics
{
        protected List<DataTable> _listOfAttribute;
        public abstract void Prepare();
}

Klasa „FamiliesOfPoints”:

Klasa dziedzicząca po „Characteristics”. Losuje rodziny punktów z wyznaczonego przedziału, oraz udostępnia je w postaci listy typu DataTable.

class FamiliesOfPoints : Characteristics
{
/// <summary>
/// Ilosc rodzin
/// </summary>
private int _amount;
/// <summary>
/// Obiekt pozwalajacy na losowanie liczb
/// </summary>
private Random _random;

/// <summary>
/// Wlasciwosc udostepniajaca obiekty
/// DataTable zawierajace rodziny punktow
/// </summary>
public List<DataTable> FamiliesPoints
{
  get
  {
    return base._listOfAttribute;
  }
}
/// <summary>
/// Konstruktor klasy
/// </summary>
/// <param name="amount">Ilosc rodzin punktow</param>
public FamiliesOfPoints(int amount)
{
base._listOfAttribute = new List<DataTable>();
_amount = amount;
_random = new Random(DateTime.Now.Millisecond);
}

/// <summary>
/// Metoda przygotowujaca rodziny punktow
/// Uruchamia losowanie
/// </summary>
public override void Prepare()
{
//Wylosowane liczby sa typu double
string switchOperand = "x1";
for (int i = 0; i < _amount; i++)
{
  //Wylosowanie czesci ulamkowej
  double doubleValue = RandomDoubleNumber();

  //Wylosowanie punktu i dodanie
  //czesci ulamkowej do niego
  double x2 = RandomIntNumber() + doubleValue;
  double x1 = RandomIntNumber() + doubleValue;

  //Utworzenie DataTable przechowujacego dana rodzine punktow
  DataTable dataTable = new DataTable("Family "
                       + i.ToString());
  dataTable.Columns.Add(new DataColumn("X1"));
  dataTable.Columns.Add(new DataColumn("X2"));

//Utworzenie punktow z danej rodziny na
//podstawie pierwszego wylosowanego punktu
//Przesuwam o wyznaczona wartosc punkt x1
//dodajac nowo wylosowana czesc ulamkowa lub x2
  switch (switchOperand)
  {
    case "x1":
     dataTable.Rows.Add(new object[] {
            Math.Round(i + RandomDoubleNumber(), Math.Round(x2, 2) });
     dataTable.Rows.Add(new object[] {
          Math.Round(i + RandomDoubleNumber() + 2, 2), Math.Round(x2 - 2, 2) });
     dataTable.Rows.Add(new object[] {
         Math.Round(i + RandomDoubleNumber() + 2, 1), Math.Round(x2 + 2, 1) });
     dataTable.Rows.Add(new object[] {
         Math.Round(i + RandomDoubleNumber() + 2, 2), Math.Round(x2, 2) });
     dataTable.Rows.Add(new object[] {
         Math.Round(i + RandomDoubleNumber() + 2, 1), Math.Round(x2, 1) });
     switchOperand = "x2";
    break;
	case "x2":
     dataTable.Rows.Add(new object[] { Math.Round(x1, 2),
				   Math.Round(x2, 2) });
	 dataTable.Rows.Add(new object[] { x1, Math.Round(x2 - 2, 2) });
     dataTable.Rows.Add(new object[] { x1, Math.Round(x2 + 2, 1) });
     dataTable.Rows.Add(new object[] { Math.Round(x1 + 2, 2),
	               Math.Round(x2 + 2, 2) });
     dataTable.Rows.Add(new object[] { Math.Round(x1 - 2, 1),
	               Math.Round(x2 - 2, 1) });
     switchOperand = "x1";
    break;
}
  _listOfAttribute.Add(dataTable);
}
}

/// <summary>
/// Metoda losujaca liczbe calkowita
/// </summary>
/// <returns>Zwraca wylosowana liczbe
/// w postaci typu double</returns>
private double RandomIntNumber()
{
  return _random.Next(3, 98);
}

/// <summary>
/// Losuje liczbe z przedzialu (0, 1)
/// </summary>
/// <returns>Zwraca wartosc liczby</returns>
private double RandomDoubleNumber()
{
  return Math.Round(_random.NextDouble(), 1);
}
}
}

Klasa „Neurons”:

Klasa dziedzicząca po „Characteristics”. Wagi neuronów pobierane są z pliku .xml lub losowane.

class Neurons : Characteristics
{
/// <summary>
/// Zmienna identyfikujaca sposob
/// tworzenia neuronow
/// odczyt z pliku/losowanie
/// </summary>
private object _neuronMode;

/// <summary>
/// Wlasciwosc zwracajaca obiekt
/// DataTable zawierajacy neurony
/// </summary>
public DataTable FamilyNeurons
{
  get
  {
    return base._listOfAttribute[0];
  }
}
/// <summary>
/// Szybkosc uczenia
/// </summary>
public double N
{
  get;
  private set;
}

/// <summary>
/// Konstruktor klasy
/// </summary>
/// <param name="neuronMode">Sposob tworzenia neuronow</param>
/// <param name="n">Szybkosc nauki</param>
public Neurons(object neuronMode, double n)
{
  base._listOfAttribute = new List<DataTable>();
  _neuronMode = neuronMode;
  N = n;
}
/// <summary>
/// Metoda przygotowujaca neurony
/// Kontroluje jakiego typu
//jest zmienna _neuronMode
/// Jesli jest to zmienna typu
///string przyjmuje to za sciezke do
/// pliku i uruchamia metode ReadFromFile()
/// Jesli jest to liczba typu int uruchamia metode
/// RandomNeurons()
/// </summary>
public override void Prepare()
{
  if (_neuronMode.GetType().Equals(typeof(string)))
  {
    ReadFromFile((string)_neuronMode);
  }

  if (_neuronMode.GetType().Equals(typeof(int)))
  {
    RandomNeurons();
  }
}
/// <summary>
/// Metoda odczytujaca z pliku,
// wagi poszczegolnych neuronow
/// </summary>
/// <param name="path">Sciezka do pliku</param>
private void ReadFromFile(string path)
{
  //Utworzenie obiektu DataTable
  //przechowujacego wagi
  //oraz identyfikator danego neuronu
  DataTable neuronsTable =
	new DataTable("Neurons");
  neuronsTable.Columns.Add(new DataColumn("Id"));
  neuronsTable.Columns.Add(new DataColumn("W1"));
  neuronsTable.Columns.Add(new DataColumn("W2"));

  //Utworzenie dokumentu XPathDocument pozwalajacego odczytac
  //plik xml z neuronami
  XPathDocument xPathDocument = new XPathDocument(path);
  int idNeuron = 0;
  XPathNavigator nav = xPathDocument.CreateNavigator();
  XPathNodeIterator iter = nav.Select("/Neurons/Neuron/*");

  object[] weights = new object[3];

  while (iter.MoveNext())
  {
    idNeuron++;
    weights[0] = idNeuron;
   //Sprawdzam czy aktualny node ma nazwe W1
    if (iter.Current.Name.Equals("W1"))
    {
      weights[1] = iter.Current.ValueAsDouble;
    }
    iter.MoveNext();
   //Sprawdzam czy aktualny node ma nazwe W2
    if (iter.Current.Name.Equals("W2"))
    {
      weights[2] = iter.Current.ValueAsDouble;
    }

   neuronsTable.Rows.Add(weights);
  }

  _listOfAttribute.Add(neuronsTable);
}

/// <summary>
/// Metoda losujaca wagi neuronow
/// </summary>
private void RandomNeurons()
{
  DataTable neuronsTable =
	new DataTable("Neurons");
  neuronsTable.Columns.Add(new DataColumn("Id"));
  neuronsTable.Columns.Add(new DataColumn("W1"));
  neuronsTable.Columns.Add(new DataColumn("W2"));

  //Utworzenie obiektu umozliwiajacego losowanie
  Random random =
	new Random(DateTime.Now.Millisecond);
 for (int i = 0; i < (int)_neuronMode; i++)
 {
  int w1 = random.Next(4, 97);
  int w2 = random.Next(4, 97);

  neuronsTable.Rows.Add(new object[] {
        i + 1,
         w1,
         w2 });
 }

  _listOfAttribute.Add(neuronsTable);
}
}

Plik xml zawierający wagi neuronów:

<?xml version="1.0" encoding="utf-8" ?>
<Neurons>
  <Neuron>
    <W1>1</W1>
    <W2>2</W2>
  </Neuron>
  <Neuron>
    <W1>2</W1>
    <W2>5</W2>
  </Neuron>
  <Neuron>
    <W1>4</W1>
    <W2>1</W2>
  </Neuron>
  <Neuron>
    <W1>7</W1>
    <W2>1</W2>
  </Neuron>
  <Neuron>
    <W1>4</W1>
    <W2>6</W2>
  </Neuron>
</Neurons>

Implementując dalej algorytm WTA postanowiłem poskładać sieć w całość wykorzystując wzorzec projektowy o nazwie „Builder” Wzorzec ten pozwolił mi oddzielić sposób tworzenia składników sieci i udostępnić obiekt reprezentujący algorytm WTA – złożony z tychże składników.

Interfejs „INetworkBuilder”:

W pierwszym kroku utworzyłem interfejs zlecający budowę konkretnych części danego algorytmu i zwracający utworzony obiekt z tych części.

interface INetworkBuilder
    {
        void BuildPointsClass(int amount);
        void BuildNeurons(object neuronMode, double n);
        Algorithm GetAlgorithm();
    }

Klasa „AlgorithmBuilderWTA”:

Następnie stworzyłem klasę implementująca ten interfejs – budującą zgodnie z wytycznymi interfejsu wybrany algorytm. (W tym przypadku algorytm WTA)

class AlgorithmBuilderWTA : INetworkBuilder
{
   private Algorithm _algorithmWTA = new AlgorithmWTA();

#region IBuilder Members
/// <summary>
/// Metoda uruchamiajac proces losowania punktow
/// </summary>
/// <param name="amount">Ilosc rodzin punktow</param>
public void BuildPointsClass(int amount)
{
  Characteristics familiesOfPoints =
  	new FamiliesOfPoints(amount);
  familiesOfPoints.Prepare();
  (_algorithmWTA as AlgorithmWTA).AddPartAlgorithm(familiesOfPoints);
}

/// <summary>
/// Metoda uruchamiajaca tworzenie neuronow
/// </summary>
/// <param name="neuronMode">Sposob tworzenie: odczyt z pliku/losowanie</param>
/// <param name="n">Szybkosc nauki</param>
public void BuildNeurons(object neuronMode, double n)
{
  Characteristics neurons =
		new Neurons(neuronMode, n);
  neurons.Prepare();
  (_algorithmWTA as AlgorithmWTA).AddPartAlgorithm(neurons);
}

/// <summary>
/// Metoda zwracajaca obiekt utworzonego algorytmu WTA
/// </summary>
/// <returns>Algorithm obiekt</returns>
public Algorithm GetAlgorithm()
{
	return _algorithmWTA;
}

#endregion
}
}

Kolejnym krokiem było stworzenie dwóch klas. Algorithm – niejako „spina” w jedną rodzinę klasy reprezentujące algorytmy nauki sieci (w moim przypadku mam tylko jeden algorytm, klasa nie zawiera żadnych składowych), oraz klasę AlgorithmWTA – oto ich kody.

Klasa „Algorithm”:

public abstract class Algorithm
{
}

Klasa „AlgorithmWTA”:

class AlgorithmWTA : Algorithm
{
#region Fields &amp;amp; Properties
/// <summary>
/// Obiekt rodziny punktow
/// </summary>
private FamiliesOfPoints _familiesPoints;
/// <summary>
/// Obiekt neuronow
/// </summary>
private Neurons _neurons;
/// <summary>
/// Wlasciwosc zwracajaca Neurony
/// </summary>
public DataTable FamilyNeurons
{
  get
  {
    return _neurons.FamilyNeurons;
  }
}
/// <summary>
/// Wlasciwosc zwracajaca rodziny punktow
/// </summary>
public List<DataTable> FamilyPoints
{
  get
  {
    return _familiesPoints.FamiliesPoints;
  }
}
/// <summary>
/// Wlasciwosc zwracajaca neurony nauczone
/// </summary>
public DataTable ChangedNeurons
{
  get;
  private set;
}
#endregion

#region Public Methods
/// <summary>
/// Metoda dodajaca obiekty: Kolejne skladowe
/// niezbedne do uruchomienia
/// procesu nauki algorytmem WTA
/// </summary>
/// <param name="part">Skladowe</param>
public void AddPartAlgorithm(Characteristics part)
{
  if (part.GetType().Equals(
           typeof(FamiliesOfPoints)))
  {
    _familiesPoints = (FamiliesOfPoints)part;
  }

  if (part.GetType().Equals(
           typeof(Neurons)))
  {
    _neurons = (Neurons)part;
  }
}
/// <summary>
/// Metoda uruchamiajaca proces nauki
/// </summary>
public void TeachNeurons()
{
if (_familiesPoints != null
		&amp;amp;&amp;amp; _neurons != null)
{
  int max = _familiesPoints.FamiliesPoints[0].Rows.Count;

  ChangedNeurons = GetNewNeuronsDataTable();

 foreach (DataRow row in _neurons.FamilyNeurons.Rows)
 {
   ChangedNeurons.Rows.Add(new object[] {
		row["Id"],
		row["W1"],
		row["W2"]});
 }

   DataRow winner = null;
 //Rozpoczecie procesu obliczania kolejnych
 //zwyciezcow dla danych wektorow uczacych
 //Petla zewnetrzna jest wykonywana tyle razy
 //ile wynosi ilosc najwiekszej rodziny punktow
 //W tym wypadku rodziny maja stala wielkosc 5 punktow
 for (int i = 0; i < max; i++)
 {
  //Petla wewnetrzna wybiera po jednym
  // punkcie z kazdej rodziny
  //Kolejno wyliczani sa zwyciezkie
  //neurony dla danego punktu
   for (int j = 0; j < _familiesPoints.FamiliesPoints.Count; j++)
   {
     DataTable dataTable = _familiesPoints.FamiliesPoints[j];

     if (max >= dataTable.Rows.Count)
     {
       max = dataTable.Rows.Count;
     }
   //Metoda ElementAtOrDefault(i) wyciaga po
   //jednym punkcie z kazdej rodziny
     DataRow point =
	    dataTable.Rows.OfType<DataRow>().ElementAtOrDefault(i);

     if (point != null)
     {
      //Obliczany jest zwyciezca
       winner = CalculateWinner(point);
     }

     if (winner != null)
     {
       CalculateWeights(point,
	         winner["Id"].ToString());
     }
   }
 }
}
}
#endregion

#region Help Methods
/// <summary>
/// Metoda obliczajaca zwyciezce dla
/// danego punku v i neuronow
/// </summary>
/// <param name="v">Punkt</param>
/// <param name="neurons">Neurony z aktualnymi wagami</param>
/// <returns>Zwyciezki neuron</returns>
private DataRow CalculateWinner(DataRow v)
{
  DataRow winner = null;
 if (v != null)
 {
    List<double> neuronsList =
		new List<double>();

    double valueOne;
    double valueTwo;

  for (int i = 0; i < ChangedNeurons.Rows.Count; i++)
  {
    valueOne = double.Parse(ChangedNeurons.Rows[i]["W1"].ToString())
	- double.Parse(v["X1"].ToString());
    valueTwo = double.Parse(ChangedNeurons.Rows[i]["W2"].ToString())
	- double.Parse(v["X2"].ToString());
    neuronsList.Add(Math.Round(Math.Sqrt(Math.Pow(valueOne, 2)
	+ Math.Pow(valueTwo, 2)), 2));
  }
  winner = ChangedNeurons.Rows.OfType<DataRow>()
     .ElementAtOrDefault(
		neuronsList.IndexOf(neuronsList.Min())
	);
 }

  return winner;
}
/// <summary>
/// Oblieczenie nowych wag dla danego neuronu
/// </summary>
/// <param name="neurons">Aktualne neurony</param>
/// <param name="v">Punkt</param>
/// <param name="idNeuron">Identyfikator neuronu
/// zapisany w DataTable</param>
private void CalculateWeights(DataRow v, string idNeuron)
{
  //Wyszukanie pierwszego neuronu zgodnego
  // z podanym Id w DataTable neurons
  DataRow neuron = ChangedNeurons.Rows.OfType<DataRow>()
	.FirstOrDefault(dr =>
  {
    return dr["Id"].Equals(idNeuron);
  });

 //Obliczenie nowych wag dla neuronow
 if (neuron != null)
 {
   neuron["W1"] = double.Parse(neuron["W1"].ToString())
   + _neurons.N * (double.Parse(v["X1"].ToString())
   - double.Parse(neuron["W1"].ToString()));

   neuron["W2"] = double.Parse(neuron["W2"].ToString())
   + _neurons.N *(double.Parse(v["X2"].ToString())
   - double.Parse(neuron["W2"].ToString()));
 }
}
/// <summary>
/// Utworzenie nowej tabeli dla neuronow
/// </summary>
/// <returns>Tabela</returns>
private DataTable GetNewNeuronsDataTable()
{
  var columnCollection = from colName
    in _neurons.FamilyNeurons.Columns.OfType<DataColumn>().AsEnumerable()
    select new DataColumn(colName.ColumnName);

  DataTable neurons = new DataTable();
  foreach (DataColumn column in columnCollection)
  {
    neurons.Columns.Add(column);
  }
    return neurons;
}
#endregion
}

Ostatnim etapem było utworzenie klasy uruchamiającej cały proces tworzenia.

Klasa „AlgorithmManager”:

class AlgorithmManager
{
/// <summary>
/// Metoda tworzaca algorytm WTA,
///przyjmujaca klase budujaca algorytm
/// oraz poszczegolne skladowe niezbedne
/// do utworznie algorytmu
/// </summary>
/// <param name="algorithmBuilder">Klasa budujaca algorytm</param>
/// <param name="amountOfPoints">Ilosc rodzin punktow</param>
/// <param name="neuronMode">Sposob tworzenia neuronow</param>
/// <param name="n">Szybkosc nauki</param>
public void BuildAlgorithm(INetworkBuilder algorithmBuilder, int amountOfPoints,
		object neuronMode, double n)
{
  algorithmBuilder.BuildPointsClass(amountOfPoints);
  algorithmBuilder.BuildNeurons(neuronMode, n);
}
}

Dodanie kolejnego algorytmu nauki sieci jest dość proste dzięki zastosowanemu wzorcowi – sprowadza się tylko do implementacji jednej klasy reprezentującej dany algorytm.

Przykładowe uruchomienie procesu nauki:
Ilość rodzin punktów: 5;
Neurony (Losowane) – ilość: 6;
Szybkość nauki n: 0,6.

INetworkBuilder algorithmBuilder = new AlgorithmBuilderWTA();

AlgorithmManager manager = new AlgorithmManager();
manager.BuildAlgorithm(algorithmBuilder, 5, 6, 0,6);
Algorithm algorithm = algorithmBuilder.GetAlgorithm();

AlgorithmWTA wta = (AlgorithmWTA)algorithm;
wta.TeachNeurons();

Na koniec zamieszczam screeny obrazujące wyniki działania algorytmu WTA.

Neurony pobrane z wpisanych wag w pliku xml:

xmlneurons.jpg

Neurony wylosowane:

losneurons.jpg

Wzorce projektowe w C# – Intro.

W ostatnim czasie zgłębiam tajniki wzorców projektowych wspomagając się tą oto pozycją książkową:

designpatternsbook.jpg

Książka w przemawiający do mnie sposób opisuję implementację dość dużej liczby wzorców projektowych opierając się na nowej wersji języka C#. Rozdziały kończą się ćwiczeniami sprawdzającymi poziom zrozumienia kolejnych wzorców. Niebawem spróbuje zaprezentować własną implementację niektórych ćwiczeń – wcześniej jednak na dobry początek przykład wzorca projektowego „Singleton” w połączeniu z wzorcem projektowym „Facade”.

Utworzyłem dwa Systemy których funkcjonalność jest udostępniania przez klasę Facade.

Klasa SystemA:

internal class SystemA
    {
        internal void OperationA()
        {
            Console.WriteLine("Operacja SystemA.");
        }
    }

Klasa SystemB:

internal class SystemB
    {
        internal void OperationB()
        {
            Console.WriteLine("Operacja SystemB.");
        }
    }

Klasa Facade:

/// <summary>
    /// Wzorzec projektowy Singleton
    /// Klasa bedaca jednoczesnie reprezentacja
    /// wzorca projektowego Facade
    /// udostepnia operacja poszczegolnych systemow
    /// </summary>
    public sealed class Facade
    {
        /// <summary>
        /// Obiekt pierwszego systemu
        /// </summary>
        private SystemA _systemA;
        /// <summary>
        /// Obiekt drugiego systemu
        /// </summary>
        private SystemB _systemB;

        /// <summary>
        /// Prywatny konstruktor
        /// </summary>
        private Facade()
        {
            //Utworzenie obiektow
            _systemA = new SystemA();
            _systemB = new SystemB();
        }

        /// <summary>
        /// Zmienna statczyna klasy Facade
        /// Jest utworzona w pierwszej kolejnosci
        /// </summary>
        private static readonly Facade _uniqueInstance = new Facade();

        /// <summary>
        /// Wlasciwosc zwracajaca unikalna instancje klasy
        /// </summary>
        public static Facade UniqueInstance
        {
            get
            {
                return _uniqueInstance;
            }
        }

        /// <summary>
        /// Operacja wykonywana przez jeden z Systemow
        /// SystemA
        /// </summary>
        public void Operation1()
        {
            _systemA.OperationA();
        }

        /// <summary>
        /// Operacja wykonywana przez jeden z Systemow
        /// SystemB
        /// </summary>
        public void Operation2()
        {
            _systemB.OperationB();
        }
    }

Tak oto wygląda wykorzystanie powyższej funkcjonalności:

//Utworzenie fasady systemow
Facade facade = Facade.UniqueInstance;
//Uruchomienie funkcjonalnosci
facade.Operation1();
facade.Operation2();

I to było by na tyle, kończę małą dawką humoru: „Każdego eksperta da się zastąpić skończoną liczbą studentów.” ;)

Szkoła cz.1

Kolejny semestr nauki przyniósł ze sobą dość interesujący przedmiot „Sztuczna Inteligencja”. Na pierwszym laboratorium otrzymałem proste zadanie rozgrzewkowe którego treść i rozwiązanie chciałbym zaprezentować. Pominę tutaj podstawy teoretyczne tego zadania, skupię się na implementacji – opisie klas które utworzyłem, pokaże także w jaki sposób narysowałem wymagany przez prowadzącego prosty wykres rozwiązania.

Na dobry początek treść zadania:

W ćwiczeniu tym będzie wykorzystany neuron do klasyfikacji samochodów. Neuron ”potrafi” ocenić dwie cechy samochodu: czy jest on „nowy” i czy jest „ładny”. Taki neuron będzie miał dwa wejścia
i jedno wyjście.

Sygnały wejściowe neuronu (podawane przez użytkownika programu, charakteryzują samochód):

  • wejście x1 – wiek samochodu
  • wejście x2 – wygląd samochodu

Informacje podawane na wejście neuronu (sygnały wejściowe) będą „mówiły” neuronowi o tym jaki samochód ”właśnie przejeżdża obok” – neuron wyrazi w odpowiedzi swoją o nim opinię. Wartości na wejściach będą oznaczały, że samochód jest: brzydki i stary (-4, -5), taki sobie (-1, 1), lub nowy i ładny (4, 5).

Wagi neuronu: w1, w2 (podawane przez użytkownika programu, należy przyjąć że ich wartość charakteryzuje komis).

Wagi neuronów to preferencje komisów wynikające m.in. stąd, że klienci danego komisu szukają w nim specyficznych aut. Np. komis sprzedawał do tej pory głównie samochody bardzo stare, przy czym analiza sprzedaży nie wykazała wpływu wyglądu na popyt auta w tym komisie. Komis ten jest reprezentowany przez neuron o wagach w1 =3, w2 = 0.

Schemat neuronu:

obliczenia.jpg

Zakładamy że wejścia oraz wagi są w przedziale: <-5; 5>
Odpowiedź neuronu jest poziomem zadowolenia komisu
z dostarczonego auta. Im wartość jest większa tym poziom zadowolenia jest wyższy.

Polecenia:

  1. Napisz program który generuje określoną przez użytkownika liczbe komisów od 2-5 (z możliwością określenia preferencji komisów „wag” ) oraz automatycznie przydziela auta do komisu
    po wpisaniu x1 i x2.
  2. Wygeneruj samochody o wszystkich możliwych x1 i x2
    z przedziału <-5,5> (jest ich łącznie 121) i przydziel wygenerowane samochody do komisów.
    (narysuj wykres prezentujący przydział samochodów do poszczególnych komisów)

Moje rozwiązanie

Przykład:
Charakterystyka samochodu:

  • x1 = -4 (wiek samochodu)
  • x2 = -5 (wygląd samochodu)

Generuje dwa komisy o wagach:

Komis nr 1:

  • w1 = 3
  • w2 = 0

Komis nr 2:

  • w1 = – 3
  • w2 = 1

Obliczenie poziomu zadowolenia jednego z komisów (Komis nr 1):

neuron.jpg

Poziom zadowolenia Komisu nr 1 wynosi: -12, analogicznie możemy obliczyć poziom zadowolenia z dostarczonego auta Komisu nr 2 który wynosi: 7. Komis o wyższym poziomie zadowolenia przyjmuje auto w tym wypadku jest to Komis nr 2.

Teraz to co Tygryski lubią najbardziej ;) – Implementacja:

Ograniczę się tutaj do pokazania kodu najważniejszych klas. W swoim programie wykorzystałem wzorzec projektowy „Decorator”. Samochód jaki i komis charakteryzują po dwie właściwości, więc w pierwszej kolejności utworzyłem klasę abstrakcyjną uogólniająca ten fakt:

public abstract class Characteristic
    {
        #region Abstract
        /// <summary>
        /// Preferencja pierwsza
        /// </summary>
        public abstract int PreferenceOne { get; protected set; }
        /// <summary>
        /// Preferencja druga
        /// </summary>
        public abstract int PreferenceTwo { get; protected set; }
        #endregion
    }

Następnie utworzyłem klasy: Car oraz CarCommission dziedziczące po klasie abstrakcyjnej Characteristic. Klasa Car jest w tym momencie komponentem który będzie „dekorowany” przez klase CarCommission. Dodatkowo klasa Car dziedziczy po interfejsie IEnumerable<Car> co umożliwia mi utworzenie kolekcji obiektów klasy Car i iteracji po niej. Wewnątrz klasy znajduje się lista List&ltCar&gt która przechowuję wszystkie kombinację aut typu wiek/wygląd. (Polecenie II). Natomiast klasa CarCommission jest odzwierciedleniem komisu samochodowego i określa poziom zadowolenia z dostarczonego auta dzięki metodzie Satisfaction().

Klasa Car:

///Komponent
public class Car : Characteristic, IEnumerable<Car>
    {
        private List<Car> _cars;
        /// <summary>
        /// Konstruktor klasy okreslajacy wiek i wyglad samochodu
        /// </summary>
        /// <param name="x1">Wiek samochodu</param>
        /// <param name="x2">Wyglad samochodu</param>
        public Car(int x1, int x2)
        {
            PreferenceOne = x1;
            PreferenceTwo = x2;
        }

        /// <summary>
        /// Konstruktor tworzacy liste ze wszystkimi
        /// kombinacjami opisujacymi samochod wyglad/wiek
        /// </summary>
        public Car()
        {
            _cars = new List<Car>();
        }

        #region Override region
        /// <summary>
        /// Wlasciwosc zwracajaca wiek samochodu
        /// </summary>
        public override int CharacterOne
        {
            get;
            protected set;
        }

        /// <summary>
        /// Wlasciwosc zwracajaca wyglad samochodu
        /// </summary>
        public override int PreferenceTwo
        {
            get;
            protected set;
        }

        public override string ToString()
        {
            return "(" + PreferenceOne.ToString() + ", "
                     + PreferenceTwo.ToString() + ")";
        }
        #endregion

        #region IEnumerable<Car> Members

        public IEnumerator<Car> GetEnumerator()
        {
            return _cars.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator
                System.Collections.IEnumerable.GetEnumerator()
        {
            return _cars.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Dodaje samochod do listy
        /// </summary>
        /// <param name="car">Obiekt samochodu</param>
        public void AddCar(Car car)
        {
            if (_cars != null)
            {
                _cars.Add(car);
            }
        }
    }

Klasa CarCommission

 public class CarCommission : Characteristic
    {
        private Characteristic _car;
        private string _commissionName;

        /// <summary>
        /// Konstruktor klasy przyjmujacy samochod do komisu
        /// Okreslajacy wagi
        /// Nazwe komisu
        /// </summary>
        /// <param name="car">Samochod</param>
        /// <param name="w1">Waga pierwsza</param>
        /// <param name="w2">Waga druga</param>
        /// <param name="commissonName">Nazwa komisu</param>
        public CarCommission(Characteristic car, int w1,
                     int w2, string commissionName)
        {
            _car = car;
            PreferenceOne = w1;
            PreferenceTwo = w2;
            _commissionName = commissionName;
        }

        #region Override region
        /// <summary>
        /// Waga pierwsza W1
        /// </summary>
        public override int CharacterOne
        {
            get;
            protected set;
        }

        /// <summary>
        /// Waga druga W2
        /// </summary>
        public override int PreferenceTwo
        {
            get;
            protected set;
        }

        public override string ToString()
        {
            return _commissionName + ": " + Satisfaction().ToString();
        }
        #endregion

        /// <summary>
        /// Poziom zadowolenia z pozyskanego samochodu
        /// </summary>
        /// <returns>Zadowolenie komisu z samochodu</returns>
        public int Satisfaction()
        {
            return _car.PreferenceOne * PreferenceOne +
                     _car.PreferenceTwo * PreferenceTwo;
        }
    }

Przykład oblczenia poziomu zadowolenia jednego z komisow z dostarczonego auta (pseudo kod):

int x1 = -4;
int x2 = -5;
///Tworze obiekt samochodu i charakteryzuje
///go wykorzystujac odpowiedni konstruktor klasy Car
Car car = new Car(x1, x2);
int w1 = 3;
int w2 = 0;
///Dostarczam samochod do komisu,
///charakteryzuje komis podajac jego wagi i nazwe
CarCommission carCommission =
          new CarCommission(car, w1, w2, "Komis 1");
//Komis okresla swoj poziom zadowolenia z dostarczonego auta
int satisfaction = carCommission.Satisfaction();

Samochód trafia do komisu o najwyższym poziomie zadowolenia. Druga część zadania opiera się na tych kilku wyżej napisanych linijkach, krótki opis:
Dostarczam do obiektu klasy Car wszystkie auta z przedziału <-5; 5> (np. (-5, 4), (-5, 4) itd.) jest ich dokładnie 121. Następnie iteruję po obiekcie tej klasy (umożliwia mi to implementacja interfejsu IEnumerable<Car> (patrz kod klasy Car) dostaczając kolejne auta do wygenerowanych komisów. Spradzam który z komisów jest najbardziej zadowolony i przypisuję mu auto.

Ostatnim etapem zadania było narysowanie wykresu. Wykorzystałem tutaj darmową bibliotekę ZedGraph dostępną tutaj. Ściągnięty plik .dll najlepiej dodać do Toolbox-a wybierając menu Tools —> Choose Toolbox Items —-> Browse (wskazać ZedGraph.dll)
dodaną kontrolke przenieść na formularz. Kod rysujący wykres do powyższego zadania przedstawia się następująco:

            PointPairList pointPairList = new PointPairList();
            //samochod (-1, 5)
            pointPairList.Add(-1, 5);
            //samochod (-1, 4)
            pointPairList.Add(-1, 4);
            //samochod (-2, 4)
            pointPairList.Add(-2, 4);

            //pobieramy obiekt GraphPane na ktorym
            //zostanie narysowany wykres z kontrolki ZedGraph
            GraphPane graphPane = zedGraphControl.GraphPane;
            //Ustawiamy tytul wykresu
            zedGraphControl.GraphPane.Title.Text = "Wykres";
            //Opis osi x
            graphPane.XAxis.Title.Text = "Wygląd samochodu.";
            //Opis osi y
            graphPane.YAxis.Title.Text = "Wiek samochodu.";

            //Rysujemy wykres, dostarczajac do metody kolejno:
            //Nazwe komisu, pary punktow(samochody), kolor linii
            //laczacej punkty, ksztalt punktow
            graphPane.AddCurve("Komis 1", pointPairList,
                  Color.Red, SymbolType.Circle);

            //Wywolujemy metode wykonujaca rysowanie
            zedGraphControl.AxisChange();

Zrzut ekranu prezentujący otrzymane wykresy dla dwóch komisów o wyżej wymienionych wagach:

wykres.jpg

Moje zmagania z programem zakończyły się pełnym sukcesem. ;)

Hello .Net World. ;)

Witam.

Postanowiłem przełamać swoją wrodzoną nieśmiałość i od dziś dawać upust wielu kłębiącym się myślom w mojej początkującej programistycznej głowie. Tytuł tego pierwszego wpisu mówi w sporej części czym postaram się dzielić z każdym kto odwiedzi moje skromne blogowe progi. Zapewniam jednak że od czasu do czasu będę odbiegał od technologii związanych z platformą .NET. Dołożę wszelkich starań, aby to czym uraczę wszystkich gości bloga – krótko mówiąc nie było jakąś programistyczną herezją, gdybym jednak popełnił takową – konstruktywna krytyka jest czymś co motywuję mnie do poprawy błędów i nauki.

Chyba każdy od tego zaczynał :)

namespace HellowNetWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hellow .NET World!");
            Console.ReadLine();
        }
    }
}

Pozdrawiam wszystkich odwiedzających i życzę miłej lektury.
Piotr Zarzycki / Hellix