Dvojrozmerné polia - matice
25.01.2010 20:53Dvojrozmerné polia – matice
Dvojrozmerné pole si môžeme predstaviť ako dvojrozmernú tabuľku s istým číslom riadku a stĺpca, v bunkách tabuľky sú samotné hodnoty.
Napríklad šachovnica je typickým príkladom štvorcovej matice (8 riadkov a 8 stĺpcov), riadky sú indexované písmenami ‘A’..’H’ a stĺpce sú indexované číslami 1..8.
Budeme sa stretávať s úlohami, kde je výhodné pracovať s „dvojrozmernou tabuľkou“ ako napríklad vzájomné zápasy tímov, šifrovanie tajných správ, rôzne hry na hracom poli, rôzne grafové úlohy a podobne.
V našich úlohách budeme často pristupovať k údajom v matici pomocou čísla riadku (najčastejšie premennou „i“) a čísla stĺpca (najčastejšie premennou „j“). Napríklad v matici K typu 3x4 (3 riadky a 4 stĺpce) je farebne vyznačený prvok K[3,2]=-12.
Deklarácia matice K v hlavičke programu bude nasledujúca:
var K: array [1..3,1..4] of integer;
Druhy matíc a ich vlastnosti
Rozmery matice a jej hodnoty zaraďujú matice do skupín s istými vlastnosťami:
Štvorcová matica typu NxN – má rovnaký počet riadkov a stĺpcov, napríklad matica A typu 3x3 bude mať prvky:
Obdĺžniková matica typu MxN – má M riadkov a N stĺpcov, napríklad matica B typu 4x2 bude mať prvky:
Hlavná diagonála matice obsahuje prvky s rovnakým číslom riadka a stĺpca (a1,1, a2,2, a3,3, ... , aN,N), napríklad prvky 1, 4 a 12 ležia na hlavnej diagonále:
Vedľajšiu diagonálu tvoria prvky, ktorých súčet čísla riadku a čísla stĺpca je N+1 (a1,N, a2,N-1, a3,N-2, ... , aN,1):
Trojuholníkový tvar matice má štvorcová matica, ktorá má pod hlavnou diagonálou nulové prvky, napríklad nasledujúca matica typu 3x3 je trojuholníková:
Lichobežníkový tvar matice má obdĺžniková matica, ktorá má pod hlavnou diagonálou nulové prvky, napríklad nasledujúca matica typu 4x3 je lichobežníková:
Jednotková matica je štvorcová matica, ktorá má na hlavnej diagonále jednotky, pod a nad hlavnou diagonálou má nuly, napríklad nasledujúca matica typu 3x3 je jednotková:
Operácie s maticami
Matice môžeme sčítavať, odčítavať, násobiť, pri operáciách platia však isté pravidlá:
- Sčítať (odčítať) môžeme len matice rovnakého typu, napríklad A(MxN) a B(MxN) tak, že sčítame (odčítame) odpovedajúce prvky matíc: ci,j=ai,j+bi,j (ci,j=ai,j-bi,j) pre každé i z 1..M a pre každé j z 1..N
- Násobenie matíc je o niečo zložitejšie. Napríklad vynásobením matice A(3x2) maticou B(2x4) je matica C(3x4) pre prvky ktorej platí , napríklad prvok c 1,1 dostaneme vynásobením prvého riadku matice A prvým stĺpcom matice B: c1,1=a1,1.b1,1+a1,2.b2,1 , prvok c 1,2 dostaneme vynásobením prvého riadku matice A druhým stĺpcom matice B: c1,2=a1,1.b1,2+a1,2.b2,2 , prvok c1,3 dostaneme vynásobením prvého riadku matice A tretím stĺpcom matice B: c1,3=a1,1.b1,3+a1,2.b2,3 a t.ď.
Všeobecne: A(MxL) . B(LxN) = C(MxN), kde
Násobiť môžeme len matice, z ktorých má prvá rovnaký počet stĺpcov, ako druhá riadkov!
Príklad
Je dané dvojrozmerné pole znakov, ktoré je už zaplnené nejakým textom. Napíšte program, ktorý presunie prvý riadok do druhého, druhý do tretieho, atď., až posledný do prvého (napr. motivácia: rolovanie obrázka). Najprv nie pekné riešenie:
var
Pole: array [1..10, 1..25] of Char;
Riadok: array [1..25] of Char;
I, J: Integer;
begin
... // pole je už zaplnené
for I := 1 to 25 do // odložíme posledný riadok do pomocného poľa
Riadok[I] := Pole[10, I];
for I := 9 downto 1 do // postupne kopírujeme všetky riadky
for J := 1 to 25 do
Pole[I+1, J] := pole[I, J];
for I := 1 to 25 do // presunieme pomocné pole do prvého riadka
Pole[1, I] := Riadok[I];
...
A teraz zapíšeme to isté, ale využijeme to, že ak sú dve polia zadeklarované identicky, môžeme ich kopírovať obyčajným priradením:
type
TRiadok = array[1..25] of Char;
TPole = array[1..10] of TRiadok;
var
Pole: TPole;
Riadok: TRiadok;
I: Integer;
begin
...
Riadok := Pole[10];
for I := 9 downto 1 do
Pole[I+1] := Pole[I];
Pole[1] := Riadok;
...
Nasledujúci príklad ukazuje spôsob prezerania prvkov dvojrozmerného poľa (zisťujeme, či matica NxN je symetrická). Najprv ukážeme nie najlepší spôsob riešenia:
const
N = 100;
type
TMatica = array [1..N, 1..N] of Integer;
function Symetricka(const M: TMatica): Boolean;
var
I, J: Integer;
begin
Result := True;
for I := 2 to N do
for J := 1 to I-1 do
if M[I, J] <> M[J, I] then
begin
Result := False;
Break;
end;
end;
Toto riešenie sa snaží použiť príkaz Break, ktorý spôsobí vyskočenie z najvnútornejšieho cyklu. To je ale nedostatočné, lebo aj tak sa budú kontrolovať ešte všetky nasledujúce riadky poľa. Keďže po skončení cyklu táto funkcia končí, v tomto prípade môžeme použiť príkaz na vyskočenie z podprogramu - Exit.
Pozrite sa ešte na riešenie, ktoré je z programátorského hľadiska správnejšie - ak potrebujeme vyskakovať z viacerých cyklov, nepoužijeme for-cyklus ale while-cyklus:
function symetricka(const M: TMatica): Boolean;
var
I, J: Integer;
begin
Result := True;
I := 2
while Result and (I <= N) do
begin
J := 1;
while Result and (J <= I-1) do
begin
if M[I, J] <> M[J, I] then
Result := False;
Inc(J);
end;
Inc(I);
end;
end;
Príklad :
Deti sa hrajú s kamienkami v obdĺžnikovom poli s okienkami, v každom okienku majú istý počet kamienkov, ktoré navzájom medzi okienkami presúvajú. Zistite, s koľkými kamienkami sa deti hrajú a vypíšte aktuálny stav hracieho poľa - počet kamienkov v okienkach.
Podstatou úlohy je súčet všetkých prvkov, uložených v štruktúre matice, ako aj jej výpis.
type
matica = array[1..100,1..100] of Integer;
var
A: matica;
M,N: Integer;
procedure TForm1.Button1Click(Sender: TObject);
procedure generuj(mat: matica; K,L: integer);
var
i,j: Integer;
begin
Randomize;
for i := 1 to K do
for j := 1 to L do A[i,j] := Random(100);
end;
procedure vypis(mat: matica; K,L: Integer);
var
i,j: Integer;
riadok: String;
begin
for i := 1 to K do
begin
riadok := '';
for j := 1 to L do
if mat[i,j] < 10 then riadok := IntToStr(mat[i,j]) + ' ' + riadok
else riadok := IntToStr(mat[i,j]) + ' ' + riadok;
Memo1.Lines.Add(riadok);
end;
end;
function scitaj(mat: matica; K,L: Integer): Integer;
var
i,j: Integer;
begin
Result := 0;
for i := 1 to K do
for j := 1 to L do Result := Result + mat[i,j];
end;
begin
Memo1.Clear;
M := SpinEdit1.Value;
N := SpinEdit2.Value;
generuj(A,M,N);
vypis(A,M,N);
Edit3.Text := IntToStr(scitaj(A,M,N));
end;
end.
Príklad :
Naprogramujme jednoduchú hru „Lodičky“, pre počítač a jedného hráča, hráč bude triafať vygenerované 3 náhodné lode zvolené počítačom v poli o veľkosti 13x13 (loď zaberá jedno políčko).
Program vyhodnotí počet úspešných a neúspešných striel. More bude predstavovať napr. nulová matica, umiestnenie lode bude číslo 1, zasiahnutá loď napr. 2.
type
matica = array[1..13,'A'..'M'] of Char;
const
zahlavie = 'ABCDEFGHIJKLM';
var
lode,skryta: matica;
riadok,k,l: Integer;
stlpec: String[1];
retazec: String;
procedure napln;
var
i,j,k: Integer;
S: Char;
begin
Randomize;
for i := 1 to 13 do
for S := 'A' to 'M' do skryta[i,S] := '0';
j := 1;
while j <= 3 do
begin
i := Random(13)+1;
k := Random(13)+1;
S := zahlavie[k];
if skryta[i,S] = '0' then
begin
skryta[i,S] := '1';
Inc(j);
end
end;
for i := 1 to 13 do
for S := 'A' to 'M' do lode[i,S] := ' ';
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
try
riadok := StrToInt(Edit1.Text);
except
ShowMessage('Zadaj číslo od 1 do 13');
Edit1.SetFocus;
end;
if (riadok < 1) OR (riadok > 13) then
begin
ShowMessage('Zadaj číslo od 1 do 13');
Edit1.SetFocus;
Exit;
end;
stlpec := Edit2.Text;
if (stlpec < 'A') OR (stlpec > 'M') then
begin
ShowMessage('Zadaj písmeno od A do M');
Edit2.SetFocus;
Exit;
end;
if skryta[riadok,stlpec[1]] = '0' then lode[riadok,stlpec[1]] := '0'
else lode[riadok,stlpec[1]] := '2';
retazec := ' ';
if riadok < 10 then
begin
retazec[1] := ' ';
retazec[2] := IntToStr(riadok)[1];
end
else
begin
retazec[1] := IntToStr(riadok)[1];
retazec[2] := IntToStr(riadok)[2];
end ;
for k := 1 to 13 do retazec[k+2] := lode[riadok,zahlavie[k]];
Memo1.Lines.Strings[riadok] := retazec;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
napln;
end;
end.
Úlohy na riešenie
Úloha 1
Pri sčítavaní prvkov na diagonálach nepotrebujeme prechádzať všetkými prvkami matice, lebo v istom i-tom riadku je prvok b[i,i] prvkom hlavnej diagonály a prvok b[i,N+1-i] prvkom vedľajšej diagonály (súčet indexov je N+1). Preto postačí prechádzať iba riadkami a vybrať len prvky na diagonálach. Maticu vytvoríme pomocou generátora náhodných čísel od 1 do 100, vygenerovanú maticu aj vypíšeme.
Úloha 2
Žiaci dostali test pozostávajúci z „N“ otázok, v každej z otázok sú 4 možné odpovede (práve 1 z nich je správna). Vyhodnoťte percentuálne správnosť odpovedí pre „M“ žiakov.
Program načíta správne odpovede testu do jednorozmerného poľa, a „M“ „N“-tíc odpovedí žiakov. Pre každého žiaka vypíše počet správnych odpovedí a percentuálnu úspešnosť, ako aj úspešnosť celej triedy.
Napríklad pre 3 žiakov a 4 testové otázky bude pri istých správnych odpovediach vyzerať nasledovne:
Zaveďme si teda 3 jednorozmerné polia – spravne(N), pocet(M) a uspesnost(M), a dvojrozmerné pole – maticu klasifikacia(MxN)a porovnajte pre každého žiaka jeho odpovede so správnymi a vyhodnoťte ich.
Úloha 3
Jednotlivé štáty získali na olympiáde istý počet zlatých, strieborných a bronzových medailí, za ktorý každý štát získa 3, 2 a 1 bod. Zistite, ktorý štát získal najviac bodov a ktorý najviac medailí. (Využite 3 – stĺpcovú maticu, pričom do prvého stĺpca je výhodné načítať počty bronzových medailí, do druhého počty strieborných a do tretieho počet zlatých, potom stačí násobiť počet medailí už len číslom stĺpca 1, 2 alebo 3).
———
Späť