2-ulotteiset taulukot (matriisit)

Yksiulotteinen taulukko on kuin rivitalo: selvittääksemme tietyn henkilön asunnon meidän tarvitsee tietää vain asunnon (järjestys-)numero.

Voimme esimerkiksi sanoa, että Pekka asuu rivitalon asunnossa nro 3, ja Maija asunnossa 0. Huomaa, että indeksit eli taulukon alkioiden laskeminen alkaa aina nollasta.

Kaksiulotteinen taulukko sen sijaan on kuin kerrostalo. Kerrostalossa henkilön majapaikan selvittämiseksi tarvitaan ensin kerros, ja vielä asunnon numero siinä kerroksessa.

Petteri asuu kerrostalon kerroksessa 1, ja sen kerroksen asunnossa 3 -- siis "paikassa" (1, 3). Huomaa, että tässä kaksiulotteisessa tapauksessa "kerrosten", eli rivien, laskeminen tapahtuu ylhäältä alaspäin.

1. Käyttö

1.1 Taulukon alustaminen

Treenataan seuraavassa 2-ulotteisen taulukon indeksejä. Olkoon meillä taulukko:

    0  1  2  3  4
  ----------------
0 | 5  6  2  7  9|
1 |11 34 23  0 42|
2 |99  6  4 23 23|
3 |19 11  5 11 47|
4 |47  9  5 18 19|
5 |33 24 54 67 88|
  ----------------

C#:illa vastaava taulukko luotaisiin alustaen esim:

int[,] mat = {
                { 5, 6, 2, 7, 9},
                {11,34,23, 0,42},
                {99, 6, 4,23,23},
                {19,11, 5,11,47},
                {47, 9, 5,18,19},
                {33,24,54,67,88}
             };

tai tyhjäksi saman kokoiseksi, 0:ia täynnä olevaksi taulukoksi

int[,] mat = new int[6, 5];

1.2 Taulukon indeksit

Esimerkkinä olevassa 2-ulotteisessa taulukossa on siis 6 riviä ja 5 saraketta. Indeksit alkavat 0:sta samoin kuin 1-ulotteisen taulukon tapauksessa.

Voisimme esimerkiksi sijoittaa taulukossa olevat 5:t paikoilleen lauseilla:

mat[0, 0] = 5;
mat[3, 2] = 5;
mat[4, 2] = 5;

Vastaavasti voisimme tarkistaa onko meillä paikassa 2,3 luku 23:

if (mat[2,3] == 23) ...

tai ottaa paikassa 3,4 olevan alkion:

int luku = mat[3,4]; // esimerkin tapauksessa 47

1.3 taulukon koon selvittäminen

Taulukon kunkin suunnan koko saadan selville GetLength(suunta) metodilla:

int riveja = mat.GetLength(0);
int sarkkeita = mat.GetLength(1);

2. Esimerkkialiohjelmia

2.1 for-silmukalla

for-silmukkaa, tai oikeastaan sisäkkäisiä for-silmukoita on käytettävä, mikäli läpikäynnin aikana on tiedettävä jotakin riveistä tai sarakkeista.

Tehdään esimerkkialiohjelma, joka laskisi monellako rivillä tietty luku esiintyy. Aliohjelmaa kutsuttaisiin:

int lkm = MonellakoRivilla(mat,11);  // esimerkissä pitäisi palauttaa 2

Aliohjelma testeineen:

/// <summary>
/// Lasketaan monellako rivillä taulukossa esiintyy etsittävä luku.
/// Jos luku on useasti samalla rivillä, rivi lasketaan yhden kerran.
/// </summary>
/// <param name="mat">matriisi, josta lukua etsitään</param>
/// <param name="etsittava">luku jota etsitään</param>
/// <returns>monellako rivillä etsittävä oli</returns>
/// <example>
/// <pre name="test">
///  int[,] t = {{2, 1, 2}, {3, 4, 2}, {1, 0, 1}, {1, 8, 9}};
///  Matriisit.MonellakoRivilla(t, 2) === 2;
///  Matriisit.MonellakoRivilla(t, 5) === 0;
///  Matriisit.MonellakoRivilla(t, 0) === 1;
///  Matriisit.MonellakoRivilla(t, 1) === 3;
///  Matriisit.MonellakoRivilla(new int[0, 0], 1) === 0;
/// </pre>
/// </example>
public static int MonellakoRivilla(int[,] mat, int etsittava)
{
  int riveja = mat.GetLength(0);
  int sarkkeita = mat.GetLength(1);
  int lkm = 0;
  
  for (int iy = 0; iy < riveja; iy++)
    for (int ix = 0; ix < sarkkeita; ix++)
      if (mat[iy, ix] == etsittava)
      {
        lkm++;
        break;
      }
  return lkm;
}

Tässä ei voida käyttää foreach-silmukkaa, koska silloin ei tiedetä mitään riveistä.

2.2 foreach-silmukka

foreach-silmukka käy kaikki matriisin alkiot samanarvoisesti lävitse. Esimerkiksi matriisin alkioiden summan laskemiseksi tämä kävisi:

int s = Summa(mat);

ja silloin aliohjelman toteutus olisi:

/// <summary>
/// Lasketaan taulukon alkioiden summa
/// </summary>
/// <param name="mat">Taulukko jonka alkioiden summa lasketaan</param>
/// <returns>Alkioiden summa</returns>
/// <example>
/// <pre name="test">
///  int[,] t = {{2, 1, 2}, {3, 4, 2}, {1, 0, 1}, {1, 8, 9}};
///  Matriisit.Summa(t) === 34;
///  Matriisit.Summa(new int[1, 1] {{5}}) === 5;
///  Matriisit.Summa(new int[0, 0]) === 0;
/// </pre>
/// </example>
public static int Summa(int[,] mat)
{
  int summa = 0;
  foreach (int luku in mat) summa += luku;
  return summa;
}

foreach-silmukkaa ei voi käyttää tilanteissa joissa tarvitaan rivi/sarakeindeksejä. Näin on myös yksiulotteisten taulukoiden tapauksessa. Sama myös tilanteessa, jossa rivit ja sarakkeet täytyy erottaa toisistaan (esim. laivaupotus tai edellisen esimerkin MonellakoRivilla).

3. Harjoituksia

  • kirjoita missä kaikissa indekseissä mallitaulukossa on luku 11 esim tyyliin:
if (mat[2,3] == 23) ...
  • entä missä 88
  • entä 9
  • kirjoita C#-lauseet, joilla sijoitat mallitaulukossa olevien lukujen 9 tilalle luvun 88
  • tee aliohjelma, joka laskee monessako sarakkeessa on tietty luku

Attachments

  • rivitalo.png (20.1 kB) - added by anlakane on 2011-10-12 13:39:04.
  • kerrostalo.png (51.3 kB) - added by anlakane on 2011-10-12 13:45:57.