MMA132: Laboration 2 Matriser i MATLAB Introduktion I den här labben skall vi lära oss hur man använder matriser och vektorer i MATLAB. Det är rekommerad att du ser till att ha laborationshandledningen för laboration 1 till hands då många av övningarna för den här laborationen liknar det ni gjorde i laboration 1. Det är enklast för dig och för läraren om du gör varje övning i denna laboration i en egen m-fil. Exempel 2: Villkor och loopar Detta är den del av laborationshandledningen till laboration 1 som hör till övning 4 som flera inte hann med. Du kan börja laboration 2 med att göra denna övning. Tidigare i kursen har vi talat om iterativa metoder för lösning av linjära ekvationer (intervallhalveringsmetoden, Newton-Raphson metoden, sekantmetoden och fixpunktsmetoden). Alla dessa metoder byggde på att göra en enkel beräkning flera gånger efter varandra. I denna del skall vi lära oss hur vi kan få MATLAB att arbeta iterativt. for-loopar Ett sätt att få MATLAB att upprepa en viss beräkning flera gånger är att använda en for-loop. En for-loop konstrueras på följande sätt: for index variabel t.ex. i = lista t.ex. list utför den beräkning som vi vill göra. Oftast så skjuts de in lite för att det ska bli tydligt vad som är inuti och vad som är utanför for-loopen. Kommandona körs en gång per element i listan list och om i dyker upp i något kommando så kommer det att vara motsvarande värde i list. Om man vill skriva ett program som utför en beräkning n gånger kan man skriva for i = 1:n som man vill köra n gånger. Låt oss testa att skriva en for-loop genom att skriva en funktion som räknar ut fakulteten av ett tal. n! = n (n 1) (n 2)... 2 1 En m-fil för detta kan se ut som följer (prova gärna och se om du kan skriva den på ett annat sätt): function f = factorial_calc(n) % factorial_calc beräknar fakulteten % av ett positivt heltal med hjälp av % en for-loop % 0! = 1 1
f = 1; % Här multipliceras värdena 1 till n % med varandra i en for-loop. % Notera att för MATLAB så betyder % 1:n och [1:1:n] samma sak for i = 1:n % i kommer att variera från 1 till n % Första varvet kommer f = 1*1 % Andra varvet kommer f = 1*2 % Tredje varvet kommer f = 2*3 % Tredje varvet kommer f = 6*4 o.s.v f = f*i; Om du byter ut i = 1:n mot i = 1:2:n kommer du att få en funktion som räknar ut produkten av alla udda tal mellan 1 och n f(n) = m (m 2)... 5 3 1 där m = n om n är udda och m = n 1 om n r jämn Om vi istället vill skriva en funktion som räknar ut produkten av alla element i en lista så kan vi enkelt ordna det function f = list_prod(list) % Beräknar produkten av alla elementen i list f = 1; for i = list f = f*i; Jämför den här funktionen med funktionen för fakultet och se till att du förstår skillnaden. I MAT- LAB finns det också färdiga funktioner för fakultet och produkt av elementen i en lista, de heter factorial respektive prod. Villkor När man utför numeriska beräkningar är det inte alltid man vet i förväg hur många gånger man vill utföra en beräkning utan man vill enkelt fortsätta tills svaret är tillräckligt bra. Man kan få MATLAB att kolla hur bra en lösning är åt oss. Först vill vi kunna jämföra tal med varandra, det finns det ett antal olika kommandon för i MATLAB a < b a < b a mindre än b a <= b a b a mindre än eller lika med b a > b a > b a större än b a >= b a b a större än eller lika med b a == b a = b a lika med b Tabell 1: Tabell över olika kommandon för att jämföra saker i MATLAB. 2
Det finns en viktig skillnad mellan a = b och a == b. När bara ett likhetstecken, =, används säger vi att vi vill spara värdet till höger i variabeln till vänster. När två likhetstecken används, ==, så jämför värdet i variabeln på höger sida med variabeln på vänster sida och ser om dom är lika. När vi skriver a == b så kommer resultatet att bli antingen 0 (om a b) eller 1 (om a = b). Prova och skriv följande i kommandofönstret: 1 < 2 15 + 7 <= 12 3*5 == 15 Blir svaren vad du förväntade dig? Om man jämför två listor med varandra så kommer jämförelserna att göras elementvis. Vi kan också använda jämförelser för att bara göra något när ett visst villkor är uppfyllt med hjälp av en if-sats. if-satser skrivs på följande vis: if jämförelse bara skall utföras om jämförelsen är sann (villkoret är uppfyllt) Det finns också en variant av if-satsen där vi kan få MATLAB att bete sig på olika sätt beroe på om villkoret är uppfyllt eller inte. if else jämförelse bara skall utföras om jämförelsen är sann (villkoret är uppfyllt) bara skall utföras om jämförelsen är falsk (villkoret är inte uppfyllt) Vi kan bygga ut funktionen för fakultetsberäkning som vi tidigare skrev: function f = factorial_calc(n) % factorial_calc beräknar fakulteten % av ett positivt heltal med hjälp av % en for-loop % 0! = 1 f = 1; % Kontrollera att n är ett positivt tal if n > 0 % Här multipliceras värdena 1 till n % med varandra i en for-loop. % Notera att för MATLAB så betyder % 1:n och [1:1:n] samma sak for i = 1:n 3
% i kommer att variera från 1 till n % Första varvet kommer f = 1*1 % Andra varvet kommer f = 1*2 % Tredje varvet kommer f = 2*3 % Tredje varvet kommer f = 6*4 o.s.v f = f*i; % Vad skall vi göra om n inte är positivt else % Kommandot disp skriver ut text i % kommandofönstret disp('n måste vara större än 0') f = 0; while-loopen Nu när vi vet hur jämförelser fungerar kan vi också få MATLAB att fortsätta göra samma sak, om och om igen tills ett visst villkor har uppfyllts. Detta görs enklast med en så kallad while-loop: while jämförelse utf ör den beräkning som vi vill göra. MATLAB kommer att utföra dessa om och om igen tills jämförelsen inte är sann längre. Med en while-loop kan man enkelt implementera de iterativa metoder som vi användes för attt lösa icke-linjära ekvationer i kapitel två och gruppuppgift 1. Nedan finns en enkel implementation av Newton-Raphson metoden för att lösa x 3 2x 2 + 1 = 0. function y = newton_raphson(x) % Funktion som numerisk löser % x^3-2*x^2+1 = 0 % med Newton-Raphsons metod och % startvärde x. % Här bestämmer vi att vi vill ha % 3 korrekta decimaler i vårt svar. eps = 0.0005; % Vi låter y vara den 'nya lösningen' % och x vara den 'gamla lösningen'. % Vi få ta till ett litet knep för att % se till att MATLAB skall räkna fram % en ny lösning minst en gång. x_new = x; x = x_new + 2*eps; % Här använder vi Newton-Raphsons metod. % Vi avgör hur bra uppskattningen är % genom att jämföra de två senaste iterationerna. while abs(x_new-x) > eps 4
% Vår 'nya lösning' blir vår 'gamla lösning' % inför nästa varv. x = x_new; % Beräkning av funktionens värde och derivata % för den gamla lösningen. f = x^3-2*x^2+1; f_p = 3*x^2-4*x; % Beräkning av ny lösning. x_new = x - f/f_p; Notera att denna implementation inte skyddar mot Newton-Raphson metodens instabilitet överhuvudtaget. Det betyder att man kan välja ett startvärde x sådant att man aldrig hittar någon lösning. Detta innebär att programmet aldrig kommer att sluta köra. Om du skulle råka ge ett sådant startvärde och MATLAB slutar svara, tryck på ctrl-c så kommer du att avbryta körningen 1. Har du tid över under laborationen så får du gärna prova att förbättra funktionen (tips: du kan använda en if-sats och kommandot return för att sätta ett maximalt antal interationer), eller skriva en ny funktion som använder sig av sekantmetoden istället. Om du tittar på kursens Blackboard-sida finns också ett par lite mer avancerade implementationer av Newton-Raphsons metod och sekantmetoden (under Laborationer Laboration 1 Intressanta m-filer) om du är intresserad. Övning 1 (övning 4, laboration 1) Skriv en MATLAB-funktion som använder sekantmetoden för att lösa icke-linjära ekvationer. Funktionen skall ta fyra argument, f, x0, x1, tol. f skall vara en funktion, x0 och x1 skall vara första gissningar och tol skall vara den tillåtna felgränsen E f. Funktionen skall kunna följande: Returnera en punkt x sådan att f(x) = 0 ± E f med sekantmetoden. Om funktionen inte hittat en tillräckligt bra lösning efter 1000 iterationer så skall den ge upp. Extra utmaning om du vill: Funktionen skall rita upp f(x) över ett lämpligt intervall och markera alla gissningar som sekantmetoden har givit. 1 Om du använder FreeMat så avbryter du skript och funktioner med ctrl-b 5
Matriser i MATLAB MATLAB är en förkortning av MATrix LABoratory. Detta namn valdes eftersom MATLAB är mycket bra på att hantera matriser. Listor i MATLAB är egentligen matriser med bara 1 rad (eller bara 1 kolonn). Prova att skriva A = [1 2 3; 0 1 6; 7 8 1] i kommandofönstret. Prova nu att skriva B = [1 2 3 0 1 6 7 8 1] i kommandofönstret. Jämför de två matriserna med varandra. Det är också enkelt att transponera och invertera matriser. Prova att skriva C = A och D = inv(a) i kommandofönstret. Fundera ut vilket kommando som gör vad. Man kan också lösa linjära ekvationssystem med MATLAB. Ekvationssystemet Ax = y där A är den matris som vi tidigare lagrade i variabel A och kan lösas genom att skriva y = [1;2;3]; x = A\y 1 y = 2 3 i kommandofönstret. Om du vill kan även använda inversen du räknade ut tidigare för att lösa systemet. Lös ekvationssystemet Övning 2 1,456x 1 + 2,846x 2 + 0,987x 3 = 1,983 2,238x 1 7,998x 2 + 3,226x 3 = 4,762 3,738x 1 + 0,937x 2 = 2,116 6
Implementering av Jacobis metod i MATLAB Om du tittar på sidan 3.14 i kompiumet så skrivs formeln för en iteration med Jacobis metod på matrisform i ekvation (3.35). Denna formel är enkel att implementera i MATLAB. Formeln är skriven sådan att du skall dela upp matrisen A i tre andra matriser A = L + D + R. Detta kan du göra på flera olika sätt. Ett sätt är att plocka ut varje element ur matrisen för sig och stoppa in i en annan matris. För att plocka ut ett specifikt element ur en matris skriver du A(i,j) där A är matrisens namn (variabeln) och i och j är rad respektive kolonn. Du kan också sätta ett specifikt element i en matris till ett visst värde, x genom att skriva A(i,j) = x. Nedan syns ett exempel: % Här definierar vi en 3 x 3 matris A = [ 1 2 3 4 5 6 7 8 9 ]; % Här kommmer: % c = 2 % d = 7 % e = 5 c = A(1,2) d = A(3,1) e = A(2,2) % Nu har vi ändrat matrisen A % så att vi på diagonalen har % värdet 10 istället för 5 A(2,2) = 10 % OBS! Notera att e = 5 fortfarande! Med följande kod kan du plocka ut diagonalen från en 2 2 matris. % Funktion som kan plocka ut diagonalen % ur vilken 2 x 2 matris som helst function D = diagonal(a) D = [ A(1,1) 0 0 A(2,2)]; Att skriva en funktion som den ovan för större matriser blir dock besvärligt. Pröva därför kommandot diag som kan göra två saker: 1) om du skriver x = diag(a) där A är en matris så blir x en lista med diagonalelementen i A. 2) om du skriver A = diag(x) där x är en lista så blir A en kvadratisk matris med diagonalelementen från x och noll överallt annars. Tips: Pröva vad som händer om du använder diag två gånger i rad. För att plocka ut L och R, testa kommandona triu och tril. Nu är vi redo att implementera iterationssteget i Jacobis metod. 7
Övning 3 Skriv en funktion som heter jacobi och som tar emot tre argument: en matris, M, en svarsvektor, y, och en ungefärlig lösning, x, och skickar tillbaka en ny gissning framräknad med Jacobis metod. För att testa er funktion kan ni skriva: M = [8 1 3; 2 10-1; 2 1 15] y = [119; 131; 167] x0 = [10; 10; 10] x1 = jacobi(m,y,x0) Om ni får resultatet x1 = 9.8750 12.1000 9.1333 så fungerar er funktion som den ska. Det går bra att anta att alla matriser som skickas in är 3 3 matriser. Eftersom Jacobis metod är en iterativ metod så vill vi naturligtvis använda den flera gånger på raken. I nästa övning skall vi skriva en version av Jacobis metod där vi matar in hur många iterationer vi vill göra. Det enklaste sättet att göra detta är med en for-loop på det sätt som demonstreras i factorial_calcexemplet på sidan 3. Övning 4 Skriv en ny funktion (hitta på ett bra namn själv) som kan ge en ungefärlig lösning på ett linjärt ekvationsystem med hjälp av Jacobis metod. Metoden skall ta en matris, en svarsvektor, en ungefärlig lösning och ett heltal, n, som inargument och skicka tillbaka en ny ungefärlig lösning framräknad med n iterationer av Jacobis metod. Tips: återanvänd lösningen till övning 3. Kom också ihåg att använda en strikt diagonaldominant matris när ni testar funktionen (Som en extra utmaning till den som vill: se till att funktionen kollar om matrisen är diagonaldominant och låter bli att räkna om den inte är det). 8