1.1 Úvod Algoritmus pro generování normálních magických čtverců Naprogramoval jsem v Matlabu funkci, která dokáže vypočítat magický čtverec libovolného přípustného rozměru. Za pomocí tří algoritmů, které jsem nastudoval zejména z [1] a [2] se mi povedlo sestavit fungující funkci (a několik pomocných v ní obsažených), které by měly magický čtverec spočítat. 1.2 Teorie Normální magický čtverec řádu n je matice s navzájem různými prvky z množiny {1, 2, n 2 } taková, že existuje jedno přirozené číslo K tak, že součet prvků každého řádku, sloupce nebo jedné z hlavních diagonál je roven K. Je dokázáno, že magický čtverec řádu n lze vytvořit pro všechna přirozená n kromě případu n = 2. V tomto případě nelze sestrojit matici 2x2 tak, aby vyhovovala definici magického čtverce. Při konstrukci magického čtverce jsem využil tři algoritmy, které zde postupně uvedu. Algoritmy se liší v závislosti na tom, jaký řád čtverce dostane funkce k výpočtu. Rozdělíme všechna možná n do tří disjunktních skupin lichá čísla, čísla dělitelná čtyřmi a sudá čísla nedělitelná čtyřmi. 1. pro n liché (př. n = 7) a) Umístíme číslo 1 doprostřed první řádky b) Vyplňujeme dalšími čísly diagonálu ve směru nahoru doprava tak, že pokud se dostaneme mimo matici na pozici (i,j), tak pokračujeme na odpovídajícím políčku v původní matici tak, že přejdeme přepočtem pokud je i,j<1 pak i,j:=n; pokud je j>n pak I,j:=1; 1
c) Pokud už je diagonála zaplněná celá, postoupíme o políčko níže a vracíme se na krok b). d) Algoritmus skončí, když umístíme do čtverce právě n 2 čísel. 2. pro n dělitelné čtyřmi beze zbytku (př. n = 8) a) umístíme čísla do čtverce A po řádcích postupně b) vytvoříme pomocnou matici X stejného rozměru, kterou vytvoříme ze čtverců rozměru 4x4, které mají na hlavní i vedlejší diagonále jedničku a na ostatních prvcích číslo 0. 2
c) Postupujeme po řádcích postupně, v případě, že je v matici X na dané pozici 0, tak prohodíme symetricky prvek v matici A na dané pozici s prvkem na poslední pozici v matici, na které je 0 a ještě pro něho nebylo prohazováno. d) Algoritmus skončí, pokud přeházíme všechna čísla v matici A na nulových pozicích v matici X mezi sebou. 3. pro n sudé a nedělitelné čtyřmi (př. n = 6) a) Nejprve vytvoříme magický čtverec řádu n/2 pomocí algoritmu 1. pro lichá n. b) Označme následující metody tvoření matice A rozměru 2x2 pro koeficient k podle následujícího schématu: metoda A: ( ) metoda B: ( ) metoda C: ( ) 3
c) Vytvoříme matici pomocí matice z bodu a) tak, že vždy nahradíme dané jedno číslo k celou maticí 2x2 tak, že použijeme jednu z metod popsaných v bodě b). Metodu A použijeme na první polovinu všech řádků, kromě prostředního prvku prostředního řádku, který nahradíme maticí za pomoci metody B. Na prvky v řadě po prostřední použijeme metodu B s výjimkou prostředního prvku, který upravíme metodou A. Všechny ostatní prvky nahradíme maticí za použití metody C. Vše shrnuje následující tabulka pro n=10: 1.3 Program Výše vypsaná teoretická pravidla mají stejné značení i postup činností jako v naprogramované funkci. Metody A,B a C v posledním případě jsou v programu očíslované pro jednoduchost jako metody 1,2,3. Funkce se volá s argumentem přirozeného čísla, které určuje řád magického čtverce. function [M] = MagickyCtverec(n) M = zeros(n,n); if ((n==2) (n<1)) %pro n = 2 nemá cenu hledat magický čtverec M = []; return if (mod(n,2)==1) %případ, kdy je n liché M(1,(n+1)/2)=1; x = 1; %posouvací index řádků y = (n+1)/2; %posouvací index sloupců umistenych = 1; %počet zatím umístěných čísel while umistenych<n*n while plnadiagonala(m,x,y)~=1 %pokud již není diagonála plná if M(x,y) == 0 M(x,y) = umistenych +1; umistenych = umistenych +1; x = normovani(n,x-1); y = normovani(n,y+1); x = normovani(n,x+1); if (mod(n,4)==0) pocet = 1; for i = 1:n for j = 1:n M(i,j) = pocet; pocet = pocet+1; X = [1 0 0 1; 0 1 1 0; 0 1 1 0; 1 0 0 1]; R = []; for i = 1:n/4 R = [R X]; %pokud je na pozici (x,y) volno %musíme hlídat, abychom "neskočili" mimo matici % pro n dělitelné čtyřmi %přiřadíme čísla do matice v přirozeném pořadí 4
X = []; for i = 1:n/4 X = [X ; R]; %vytváříme matici X pomocí maticových bloků 4x4 for i=1:n/2 for j=1:n if X(i,j) == 0 cache = M(i,j); M(i,j) = M(n-i+1,n-j+1); M(n-i+1,n-j+1) = cache; %pokud je v matici X nula, můžeme prohodit %zrcadlově prvky %pokud je n sudé nedělitelné čtyřmi A = MagickyCtverec(n/2); %vytvoříme magický čtverec polovičního řádu M = []; for i =1:n/2 X = []; for j = 1:n/2 if i<=(n/2+1)/2 if i<(n/2+1)/2 %metoda A (1) pro všechny řádky nad prostředním X = [X VytvorCtverecek(1,A(i,j))]; if i == j %metoda B (2) pro prostřední prvek prostředního řádku X = [X VytvorCtverecek(2,A(i,j))]; X = [X VytvorCtverecek(1,A(i,j))]; if i ==((n/2+1)/2)+1 if j~=(n/2+1)/2 %metoda B (2) pro následující řádek po prostředním X = [X VytvorCtverecek(2,A(i,j))]; %opět s výjimkou prostředního X = [X VytvorCtverecek(1,A(i,j))]; %na všechny ostatní metoda C (3) X = [X VytvorCtverecek(3,A(i,j))]; M =[M ; X]; function [a] = normovani(n,x) %přídatná funkce, která zajistí abychom "nevyskočili" z matice v první metodě if x < 1 a = n; if x > n a = 1; a = x; 5
function [a] = plnadiagonala(m,x,y) %přídatná funkce, která zjišťuje, jestli už není diagonála v případě 1 obsazena a = 1; for i = 1:size(M,1) if M(x,y)==0 a = 0; x = normovani(size(m,1),x-1); y = normovani(size(m,2),y+1); function[a] = VytvorCtverecek(metoda,koeficient); %přídatná funkce,vytvoří čtvereček podle zadané metody (A=1,B=2,C=3)a koeficientu k switch metoda case 1 A = [4*koeficient 4*koeficient-3; 4*koeficient-2 4*koeficient-1]; case 2 A = [4*koeficient-3 4*koeficient; 4*koeficient-2 4*koeficient-1]; case 3 A = [4*koeficient-3 4*koeficient; 4*koeficient-1 4*koeficient-2]; 1.4 Výstupy na obrazovku pro různé vstupy 6
1.5 Závěr Pomocí funkce tic-toc v Matlabu jsem zjistil, že výpočet magického čtverce pro velká n pomocí mé funkce trvá bohužel déle, než pomocí funkce magic(). Je to možná způsobeno tím, že Matlab používá ještě složitější algoritmus pro generování tohoto čtverce, založený nejspíše na metodě rozděl a panuj. Tato metoda spočívá v tom, že si čtverec postupně rozdělíme na menší čtverce, které vyřešíme zvlášť a dále budeme prohazovat celé jejich bloky za pomocí metody popsané pro n liché. Použití daného řešení by však překračovalo mnohonásobně délku funkce MagickyCtverec(), která je již v tomto případě už i tak dost komplikovaná. Níže uvádím zdroje, odkud jsem dané algoritmy nastudoval a pomocí nich napsal výslednou funkci. 1.6 Reference [1] http://mks.mff.cuni.cz/library/magickectvercetr/magickectvercetr.pdf [2] http://mathworld.wolfram.com/magicsquare.html [3] http://juggle.cz/matematika.asp [4] http://www.mathworks.com/help/matlab/ref/magic.html 7