Department of Physics Umeå University 18 november 2014 MATLAB Guide Marina Wallin Martin Hansson Per Sundholm INNEHÅLL 1 Innehåll 1 Introduktion till MATLAB 1.1 Vad är MATLAB och varför använder vi 1.2 En första bekantskap med MATLAB . . 1.3 Hjälp . . . . . . . . . . . . . . . . . . . . 1.4 Enkla MATLAB operationer . . . . . . 1.4.1 Matematiska funktioner . . . . . 1.4.2 Vektorer . . . . . . . . . . . . . . 1.4.3 Logiska uttryck . . . . . . . . . . 1.5 Enkla script i MATLAB . . . . . . . . . 1.5.1 If, For, While . . . . . . . . . . . 1.6 Bilder i MATLAB . . . . . . . . . . . . 1.7 Regressionsanalys . . . . . . . . . . . . . 1.7.1 Linjäranpassning . . . . . . . . . 1.7.2 Konfidensintervall . . . . . . . . 1.7.3 Polynomanpassning . . . . . . . 1.7.4 Linjärisering av potensfunktion . 1.7.5 Värdering av resultat . . . . . . det? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3 4 7 9 9 10 11 12 13 14 19 19 20 21 22 22 2 Programmering med MATLAB 2.1 Introduktion till vektorer och matriser 2.1.1 Vektorer i MATLAB . . . . . . 2.1.2 Matriser i MATLAB . . . . . . 2.1.3 Vektor- och matrisoperationer . 2.2 Mer om loopar . . . . . . . . . . . . . 2.3 Script i MATLAB . . . . . . . . . . . 2.4 Läsa in data i MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 24 24 27 29 32 34 35 3 Matematisk analys med MATLAB 3.1 Funktioner och derivator . . . . . . . . . . . . 3.2 Eulers metod . . . . . . . . . . . . . . . . . . 3.3 MATLAB:s ode-lösare . . . . . . . . . . . . . 3.4 Numerisk integrallösning . . . . . . . . . . . . 3.4.1 Riemannsumman . . . . . . . . . . . . 3.4.2 Trapetsregeln . . . . . . . . . . . . . . 3.4.3 Simpsons regel . . . . . . . . . . . . . 3.4.4 MATLAB:s numeriska integralräknare 3.5 Symbolhantering i MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 38 40 42 43 43 44 46 47 48 4 Linjära system i MATLAB 4.1 Vektorer och matriser . . . . . . . . . . . . . 4.2 Matrisoperationer . . . . . . . . . . . . . . . . 4.2.1 Matrisaddition och subtraktion . . . . 4.2.2 Matrismultiplikation . . . . . . . . . . 4.2.3 Transponering av matriser i MATLAB 4.2.4 Matrisinvers i MATLAB . . . . . . . . 4.3 Linjär algebra i MATLAB . . . . . . . . . . . 4.3.1 Determinanter i MATLAB . . . . . . 4.3.2 Lösning av linjära system i MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 49 49 50 50 51 52 52 52 53 . . . . . . . . . . . . . . INNEHÅLL 4.3.3 2 Egenvärden och egensystem i MATLAB . . . . . . . . . . 56 5 Statistik i MATLAB 5.1 Slumpvariabler och fördelningar i MATLAB . . . . . . 5.1.1 Slumptalsgenerering . . . . . . . . . . . . . . . 5.1.2 Slumpfördelningar . . . . . . . . . . . . . . . . 5.2 Sammanfattande statistik . . . . . . . . . . . . . . . . 5.2.1 Skattningar av väntevärde och spridningsmått 5.2.2 Statistiska plottar . . . . . . . . . . . . . . . . 5.3 Statistiska test och konfidensintervall . . . . . . . . . . 5.3.1 Normalitetstest . . . . . . . . . . . . . . . . . . 5.3.2 Test med normalfördelningsantagande . . . . . 5.3.3 Exempel . . . . . . . . . . . . . . . . . . . . . . 5.3.4 ANOVA . . . . . . . . . . . . . . . . . . . . . . 5.3.5 Oparametriska test . . . . . . . . . . . . . . . . 5.3.6 Konfidensintervall . . . . . . . . . . . . . . . . 5.4 Programspråket R . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 59 59 60 61 61 63 67 67 68 68 71 72 72 73 6 Klassisk mekanik med MATLAB 6.1 Newtons rörelseekvationer med numeriska metoder 6.1.1 Rörelse i en dimension . . . . . . . . . . . . 6.1.2 Rörelse i flera dimensioner . . . . . . . . . . 6.2 Verlet-integration . . . . . . . . . . . . . . . . . . . 6.2.1 Velocity-Verlet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 74 74 77 78 79 A Appendix: Rapportskrivning med A.1 Bilder i MATLAB . . . . . . . . A.1.1 Plot-kommandot . . . . . A.1.2 Sub-plottar . . . . . . . . A.1.3 Symboler . . . . . . . . . A.2 MATLAB med andra program . A.2.1 MATLAB2tikZ . . . . . . A.2.2 MATLAB med COMSOL A.3 Feluppskattningar . . . . . . . . Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 81 81 82 83 84 84 85 86 B Appendix: Vanliga fel B.1 Dimensionsfel . . . . . . . . . B.2 Indexeringsfel . . . . . . . . . B.3 Syntaxfel . . . . . . . . . . . B.4 Övriga fel . . . . . . . . . . . B.5 Felsökning i MATLAB . . . . B.5.1 Felsökning i MATLAB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 87 88 89 90 91 91 C Appendix: Tips och Trix C.1 Småtips att alltid följa . . . . C.2 Publish . . . . . . . . . . . . C.3 Multi-dimensionella matriser C.4 Cell-arrayer . . . . . . . . . . C.5 Struct . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 93 95 96 97 98 1 INTRODUKTION TILL MATLAB 1 3 Introduktion till MATLAB Något man som Teknisk fysiker kommer att använda genom hela sin utbildning och förmodligen även i det riktiga livet är MATLAB, se figur 1. MATLAB är ett programspråk från företaget MathWorks som används av många civilingenjörer. Den här handboken är skriven av studenter på Teknisk fysik för att hjälpa er och göra den första bekantskapen med MATLAB enklare. För är det något man kan vara säker på så är det att man inte kan fuska sig igenom MATLABmomenten i kurserna. Det kommer att återkomma i stort sett i varenda kurs och en bra relation till MATLAB kommer därmed hjälpa avsevärt. MATLAB kommer ibland vara ens värsta fiende och man kommer känna att man inte har tid att lära sig det, då man måste räkna uppgifter och göra teoretiska labbar hela tiden. Ett tips är att ta er tiden till att lära er MATLAB ordentligt så ni vet när och hur man bör använda det. Ni kommer tjäna på det i längden. Figur 1: MATLABs logga. Det här är som sagt en handbok som är till för att hjälpa er. Vi påstår inte på något sätt att den är världsbäst, men vi tror ändå att den är bättre än att inte ha någonting alls. Ett tips är att samtidigt som ni läser detta dokument, se till att testa att göra samma exempel som vi gör för bättre förståelse. Kom ihåg att det alltid finns äldrekursare i datasalen som kan hjälpa er då ni kör fast. Lycka till! 1.1 Vad är MATLAB och varför använder vi det? Namnet MATLAB kommer från matrix laboratory och syftar till programmets förmåga att hantera matriser. MATLAB är ett högnivåspråk som lämpar sig för matematiska och tekniska beräkningar, vilket vi som civilingenjörer sysslar en hel del med. I MATLAB kan man skriva algoritmer, beräkna matematiska uttryck, analysera data, visualisera resultat och mycket, mycket mer. I detta kapitel kommer vi börja med att förklara hur man kan använda MATLAB på ett enkelt sätt för att sedan öka svårighetsgraden i senare kapitel. 1 INTRODUKTION TILL MATLAB 4 Om ni någonsin tvivlar på MATLABs styrka kan ni alltid testa att skriva why i kommandofönstret. MATLAB kommer då ge er svar på tal. 1.2 En första bekantskap med MATLAB När man öppnar MATLAB kommer det se ut ungefär som i figur 2. Notera att man själv kan flytta och arrangera om som man själv vill. Det betyder att bilden inte alltid stämmer exakt men oftast ser det ut som i figur 2. Längst upp i fönstret finns flikar och ikoner som underlättar arbetet. Det stora fönstret i mitten kallas för kommandofönstret, Command window, och det är där man skriver in vad man vill göra. Figur 2: Det här är ett exempel på hur MATLAB ser ut då man startar det. Symbolen >> kallas för MATLAB-prompten och indikerar att man kan skriva. Om vi till exempel skriver >> 12 + 3*(4−5) + 10/5 och trycker enter kommer MATLAB att svara >> ans = 11 vilket är precis det vi vill. MATLAB har räknat ut vårt tal och sparat det i en variabel som heter ans. Ibland kan det vara skönt att MATLAB skriver ut vad den gör så man vet att allting blir rätt, men med för många utskrifter blir det snabbt oöverskådligt. Då använder man sig av ett semikolon efter uttrycket, >> 35*12−100; vilket gör att ans får värdet 320 men det skrivs inte ut i kommandofönstret. Om man skriver in 1 INTRODUKTION TILL MATLAB 5 >> a = 4; >> b = 3; >> a+b i kommandofönstret kommer MATLAB se ut som i figur 3. Figur 3: Så här kan MATLAB se ut då man utfört en del kommandon. Som man ser har bilden förändrats genom att det tillkommit nya saker. Längst ner till höger har vi en ruta som kallas kommandohistorik, Command History, där man ser vilka kommandon som senast gjorts. Längst upp till höger finns en ruta som heter arbetsyta,Workspace, där alla aktuella variabler sparas tillsammans med deras värde. Där a för tillfället är 4, b är 3 och ans är 7. ans är en temporär variabel, vilket betyder att variabeln bara kommer sparas tills en ny uträkning görs och den gamla uträkningen inte längre är aktuell. Om man skriver in >> a−b kommer ans byta värde till 1. Om man vill spara olika variabler en längre tid måste man själv ge dem egna namn. Detta görs genom att skriva tex >> sum = a+b >> diff = a−b vilket sparar 7 i variabeln sum och 1 i variabeln diff. Beskrivande variabelnamn gör koden enklare att förstå för utomstående. Om man vill spara många värden i samma variabel kan man göra det genom att skapa en lista av tal: >> numbers = [1 2 3 4 5] Detta kallas även för en vektor och kommer förklaras ytterligare i avsnitt 2.1. 1 INTRODUKTION TILL MATLAB 6 Nu har vi endast skrivit kortare rader men när man gör många uträkningar på samma gång kan det vara bra att spara det på något sätt. I MATLAB görs detta genom att skapa en M-fil. Det kan man antingen göra genom att klicka New → Script i huvudpanelen eller skriva edit i kommandofönstret. Då öppnas en editor där man kan skriva sin kod. För att köra klickar man på den gröna triangeln eller genom att skriva in m-filens namn i kommandofönstret. Till exempel kan vi skriva >> edit hej då skapas en M-fil med namnet hej.m och en editor öppnas. Editorn är som ett tomt textblad där man kan man skriva exakt samma sak som vi skrivit tidigare men M-filen kommer sparas så att man kan fortsätta en annan dag. Det man skrivit i kommandofönstret kommer raderas när man stänger ner MATLAB. I figur 4 ser man hur MATLAB ser ut efter att vi skapat en M-fil. M-filen hej.m ligger i en ruta till vänster där alla filer i samma mapp sparas. Sökvägen till den mappen kan man se under huvudpanelen. Mer om M-filer och dess fördelar kan man läsa i kapitel 1.5 som handlar om enkla script i MATLAB. Figur 4: Så här kan MATLAB se ut då man skapat en m-fil och utfört en del kommandon. Resultaten man får från koden i sin M-fil kommer hamna i editorn. T.ex. om man skriver one = 1 i M-filen, kommer MATLAB svara >> one = 1 i editorn. 1 INTRODUKTION TILL MATLAB 1.3 7 Hjälp Det kommer alltid finnas tillfällen då man inte är helt säker på hur man ska skriva för att MATLAB ska göra som man vill. Det man då kan göra är att använda sig av olika hjälpfunktioner som är inbyggda i MATLAB eller använda internet. Det första och kanske enklaste kommandot man ska prova är kommandot help. Man skriver helt enkelt >> help sqrt och får då fram en kortare text om hur man använder kommandot sqrt, se figur 5. Figur 5: En hjälpande text om sqrt som MATLAB returnerar efter kommandot help sqrt. Om man behöver mer dokumentation om kommandot eller inte är helt säker på vad kommandot heter kan man använda sig av >> doc plot som öppnar ett nytt fönster med dokumentation om kommandot plot, se figur 6. Fördelen med detta sätt är att man längst upp på sidan kan söka efter kommandon och få olika förslag om man inte är helt säker på vad man söker efter. 1 INTRODUKTION TILL MATLAB 8 Figur 6: Ett nytt fönster i MATLAB med dokumentation om kommandot plot. Ett tredje alternativ som man kan testa är >> demo matlab då får man upp ett bibliotek med kortare exempel och guider med exempelkod till MATLAB, se figur 7. 1 INTRODUKTION TILL MATLAB 9 Figur 7: Inbyggda exempel och guider till MATLAB. Om man inte riktigt vet vad man ska söka på är det såklart alltid enklast att leta på internet, där Google har många svar. En otroligt bra sida är mathworks.se, där man kan hitta väldigt mycket bra exempel och förklaringar som MathWorks, MATLABs skapare, själva skrivit. 1.4 Enkla MATLAB operationer Nu när vi har lärt känna MATLAB lite är det dags att gå vidare. Vi kommer i detta avsnitt gå igenom en del enkla MATLAB operationer som kan vara bra att ha koll på för att kunna skriva mindre program i MATLAB. 1.4.1 Matematiska funktioner I MATLAB går det att anropa alla de matematiska funktioner som man brukar hitta i en avancerad miniräknare. Nedan, i tabell 1, följer ett par kommandon som är lämpliga att kunna samt deras översättning till matematisk notation. Tabell 1: Matematiska funktioner och hur man skriver dem i MATLAB. MATLAB Matematisk motsvarighet exp(x) ex sin(x),cos(x), tan(x) sin(x), cos(x), tan(x) asin(x),acos(x), atan(x) arcsin(x), arccos(x), arctan(x) log(x), log10(x) ln(x),√log10 (x) sqrt(x) x abs(x) |x| Det går alltså att beräkna exempelvis √ 4 med MATLAB genom att skriva 1 INTRODUKTION TILL MATLAB 10 >> sqrt(4) ans = 2 1.4.2 Vektorer Begreppet vektorer kommer förklaras tydligare i senare kurser, just nu kan ni tänka på en vektor som en lista med tal. För att skapa en vektor i MATLAB kan man använda sig av klammeroperatorn [ ]. Med den kan man spara flera värden till samma variabel. numbers=[ 4 8 15 16 23 42 ] Ett annat väldigt användbart verktyg är kolonoperatorn. Skriver man x=0:2:10 i MATLAB kommer en lista skapas som börjar på 0, har ett avstånd på 2 mellan talen och slutar med 10. >> x=0:2:10 x = 0 2 4 6 8 10 När man skapar vektorer är det ibland ovärderligt att snabbt kunna ta reda på längden av den. Längden av en vektor är hur många tal som är sparad i vektorn. Det kan man göra genom att skriva: >> length(x) ans = 6 Vanliga tal är i grund och botten vektorer med längden 1. Att hantera vektorerna som skapats är enkelt. För att plocka fram till exempel det tredje elementet ur en vektor skriver man >> x(3) ans = 4 och för att ändra specifika element ur en vektor skriver man >> numbers(3)=100 numbers = 4 8 100 16 23 42 Det går att addera och subtrahera vektorer med andra vektorer med samma längd, men det går inte att multiplicera, upphöja eller dividera. För att göra det måste man sätta en punkt framför räknesymbolen. Genom att skriva en punkt framför operatorn berättar man för MATLAB att man vill utföra operationen elementvis. Om man inte skriver det kommer MATLAB utföra matrisoperationer, vilket förklaras mer ingående i avsnitt 2.1.3. Nedan följer två exempel: >> numbers−x ans = 4 6 >> x.*2 ans = 0 4 96 8 10 12 15 16 32 20 Som man ser har det första talet i x subtraherats från det första talet i numbers, det andra från det andra, osv. Det går också att använda de matematiska funktioner som vi nämnt tidigare på vektorer: 1 INTRODUKTION TILL MATLAB >> x=0:2:10 x = 0 2 4 6 >> sqrt(x) ans = 0 1.4142 8 11 10 2.0000 2.4495 2.8284 3.1623 Här har vi alltså tagit roten ur vektorn x. Svaret har blivit avrundat vid den fjärde decimalen. Om vi skulle vilja se fler decimaler kan vi skriva >> format long >> sqrt(x) ans = 0 1.414213562373095 2.000000000000000 2.828427124746190 3.162277660168380 2.449489742783178 För att byta tillbaka skriver man format short. Det finns flera olika format att välja mellan. 1.4.3 Logiska uttryck I MATLAB går det att undersöka diverse logiska samband som likheter och olikheter. I tabell 2 finns exempel på logiska operatorer som MATLAB kan hantera. Tabell 2: Logiska uttryck. MATLAB Förklaring >, < Större än, mindre än >=, <= Större/mindre än eller lika med == Lika med ∼= Inte lika med & Logiskt och | Logiskt eller Att använda logiska operatorer fungerar som att använda vilket annat räknesätt som helst. Skillnaden är att resultatet antingen blir 0 eller 1, där 0 står för falskt och 1 står för sant. För att exemplifiera följer några olikheter nedan: >> 1<2 ans = 1 >> 1>2 ans = 0 >> 5<10 & 5>8 ans = 0 >> 5<10 | 5>8 ans = 1 Det tredje exemplet returnerar falskt eftersom 5 inte är både mindre än 10 och större än 8. I det fjärde exemplet returneras sant eftersom 5 är mindre än 10. Ett logiskt och innebär att båda villkoren måste vara uppfyllda medans logiskt eller innebär att endast de ena villkoret måste vara uppfyllt. Det går även att använda logiska uttryck på vektorer >> x=0:2:10 x = 0 2 >> x<=4 ans = 1 4 1 6 8 1 10 0 0 0 1 INTRODUKTION TILL MATLAB 12 Om man använder logiska uttryck på vektorer returneras flera olika svar eftersom talen jämförs var för sig. De tre första talen i vektorn x är större eller lika med 4 och därför returneras en etta på dessa positioner. 1.5 Enkla script i MATLAB Vi har i kapitel 1.2 sett hur man skapar och öppnar en M-fil. Låt oss nu skrida till verket och skriva vårt första script. Ett script är ett dokument som kör koden rad för rad, precis på samma sätt som om vi hade skrivit rad för rad i kommandofönstret. Men nu skriver vi klart hela koden och kör allt när vi är färdiga. På detta sätt kan vi spara allt vi skriver i ett dokument. Säg att vi vill beräkna arean av ett rätblock, d.v.s. en tegelstensformad geometrisk kropp med sidolängderna 1, 2 och 3 cm. Då kommer vi ha två sidor med arean 1·2, två med arean 1·3 och två med arean 2·3, och totalarean kommer bli summan av dessa, alltså 22 cm2 . Denna beräkning går alldeles utmärkt att utföra i MATLAB genom att skriva a=1; b=2; c=3; area=2*(a*b + a*c + b*c) och spara uträkningen som ett script med namnet rArea1.m. Testkör filen genom att sedan skriva rArea1 i MATLABs kommandoprompt. >> rArea1 area = 22 Ett annat sätt att åstadkomma samma sak är att göra rätblocksarean till en funktion av sidolängderna a, b och c. I MATLAB kan man skriva funktioner med hjälp av M-filer. Variabler som skapas inuti sådana funktioner är lokala och tas automatiskt bort när funktionen avslutas. För att definiera en funktion skapar vi en ny M-fil med samma namn som funktionen vi tänker skriva. Det är viktigt att ha samma namn på funktionen och M-filen annars kommer MATLAB inte att hitta funktionen. Funktioner börjar med kommandot function som utgör ett funktionshuvud som beskriver vilka parametrar funktionen tar och vilka returvärden den ger tillbaka. Funktionen avslutas med kommandot end. Nedan följer samma exempel som tidigare men den här gången är rätblocksarean en funktion, med sidolängderna som inparametrar: function area=rArea2(a,b,c) area=2*(a*b + a*c + b*c); end Testkör funktionen genom att skriva: >>area = rArea2(1,2,3) area = 22 Det är viktigt att man skickar in parametrarna i rätt ordning, i detta fall har vi a = 1, b = 2 och c = 3. Prova också att köra rArea2(1,1,1) som räknar ut arean av en kub med sidolängden 1 cm. Fördelen med funktioner är alltså 1 INTRODUKTION TILL MATLAB 13 att man kan skicka in olika parametrar till funktionen. I det här fallet är det sidolängderna. Förklaringar till filen rArea2 Rad 1. Första raden i M-filen ovan deklarerar att en funktion ska skapas med namnet rArea2. Funktionen anropas med tre argument och kommer att returnera värdet på area. I M-filen har dessa namnet a, b och c men när funktionen anropas från MATLAB kan dessa vara vad som helst. Rad 2. Beräkningen av rätblocksarean utförs och resultatet tilldelas namnet area. Det är samma area som förekom i funktionsdeklarationen på rad ett. Detta är det sista som görs i M-filen och det är det värde som area har just nu som kommer att returneras av funktionen. Notera att area bara är ett namn och kan bytas ut mot vilket annat godtagbart namn som helst bara det förekommer både i funktionsdeklarationen på rad ett och på sista raden. Rad 3. Kommandot end avslutar funktionen. 1.5.1 If, For, While Ibland kan det vara väldigt användbart att kunna upprepa eller sätta något slags villkor på när en slinga kod ska utföras. Därför finns kontrollstrukturerna if, for och while implementerade i MATLAB. Med dessa kan man på ett snabbt och enkelt sätt göra program som inte varit möjligt med en miniräknare. If Om vi skriver if följt av ett logiskt uttryck, kommer koden inramad av if och end att utföras om villkoret uppfylls. Om man önskar kan man lägga till ett else i satsen. Då kommer det efter else att utföras om det logiska uttrycket inte är uppfyllt. Nedan följer ett exempel: if A==5 disp('Sant, A = 5'); else disp('Falskt, A inte = 5'); end Om A är lika med 5 kommer ”Sant, A = 5” att skrivas ut. Om A är något helt annat kommer ”Falskt, A inte = 5” att skrivas ut. For Med en for-loop kan man få kommandoraderna i slingan att upprepas ett önskat antal gånger. Det som sker inom slingan kan varieras med hjälp av en räknare, n i exemplet nedan, vars värde ändras en gång varje varv. for n=1:10 n2=nˆ2; sprintf('%d i kvadrat aer lika med %d',n,n2) end I denna kodsnutt är n först 1 och kvadraten räknas ut till 1. Nästa varv är n lika med 2 och kvadraten räknas ut till 4, osv. Sista gången kommer n vara lika med 10 och kvadraten lika med 100. 1 INTRODUKTION TILL MATLAB 14 While Om vi skriver while följt av ett logiskt uttryck, så kommer koden inramad av while och end upprepas så länge det logiska uttrycket är uppfyllt. Nedan följer ett exempel: i=1; s=0; while(s<20) s=s+i i=i+1; end Först är s mindre än 20 och vi går in i loopen. Där ändras s till 1 och variabeln i ökas på till 2. s ändras sedan till 3, 6, 10, 15 och slutligen 21. Loopen avslutas då s är 21 och alltså inte längre mindre än 20, vilket sker då i är lika med 7. 1.6 Bilder i MATLAB För att kunna analysera och presentera sitt mätdata ordentligt kan det vara bra att visulisera det. MATLAB har ett omfattande plot-kommando till detta ändamål som kommer att introduceras här. Plottar och grafer är ett sätt att visualisera ett förhållandet mellan två mängder av tal genom att sätta ut punkter i ett koordinatsystem som beskriver detta förhållande. För att kunna plotta sina mätvärden måste vi först skapa två listor, vi döper dem till x och y. x=[0 1 2 3 4 5 6 7 8 9 10]; y=[1 3 5 7 9 11 13 15 17 19 21]; För att sedan plotta dessa skriver vi plot(x,y) och resultatet kan vi se i figur 8 nedan. För att kunna plotta två listor mot varandra på det här sättet måste vektorerna ha samma längd. 1 INTRODUKTION TILL MATLAB 15 Figur 8: Listan y plottad mot listan x. Som vi ser i figuren så drar MATLAB automatisk linjer mellan mätpunkterna. För att bara plotta punkterna så skriver vi plot(x,y,'ro') och resultat blir enligt figur 9. 1 INTRODUKTION TILL MATLAB 16 Figur 9: Listan y plottad mot listan x som punktvärden. Där o specificerar att mätpunkterna ska visualiseras med en ring och r bestämmer färgen på plotten. Om man vill visualisera mätpunkterna och samtidigt ha linjer mellan dessa så skriver man den sista biten som 'ro−'. Använd gärna hjälpfunktionen för fler alternativ om hur man kan ändra utseendet på sin plot. För att göra sina plottar i MATLAB ännu tydligare bör man inkludera titel och axelnamn i sina plottar. Genom att addera raderna nedan utökar vi vårt tidigare exempel: plot(x,y,'ro−') title('Titel') xlabel('x−axelns namn') ylabel('y−axelns namn') Där xlabel namnger den horisontella axeln och ylabel den vertikala. Detta ger oss figur 10 nedan. 1 INTRODUKTION TILL MATLAB 17 Figur 10: Figur med titel och namn på axlarna. Ibland så vill man plotta två grafer samtidigt, antingen i samma fönster eller i två separata. För att göra det behöver vi två nya listor, vi döper dessa till f och g. f=[0 1 2 3 4 5 6 7 8 9 10]; g=[0 3 4 5 8 11 12 13 16 19 20]; För att plotta dem i samma fönster använder vi kommandot hold on: plot(x,y,'ro−') hold on plot(f,g,'gx') title('Titel') xlabel('x−axelns namn') ylabel('y−axelns namn') legend('1:a plottens namn','2:a plottens namn') Funktionen legend() beskriver vad varje kurva representerar som man kan se i figur 11. 1 INTRODUKTION TILL MATLAB 18 Figur 11: Figur med två olika grafer plottade i samma fönster tillsammans med en förklarande ruta. Avslutningsvis vill vi nu plotta de två listorna i var sitt fönster, detta görs genom att specificera det som: figure(1) plot(x,y,'ro−') title('Titel1') xlabel('x−axelns namn') ylabel('y−axelns namn') figure(2) plot(f,g,'gx') title('Titel2') xlabel('x−axelns namn') ylabel('y−axelns namn') Detta ger de två figurerna i figur 12. 1 INTRODUKTION TILL MATLAB 19 Figur 12: Två olika figurer skapade i samma MATLAB-script. Ett avslutande tips är att MATLAB som standard sparar sina figurer som .fig-filer vilket vissa program inte helt stödjer. Använd hellre de vanligare formaten .jpg eller .png när man sparar sina figurer. 1.7 Regressionsanalys När man gör en statistisk undersökning, det skulle kunna vara t.ex. att man har mätt ett tryck i en gas med avseende på temperaturen eller spänningen över ett motstånd med avseende på strömmen, så vill man att dessa mätvärden ska följa någon exakt matematiskt funktion så att man kan testa sina teorier. Tyvärr är detta sällan fallet, mätvärdena hamnar oftast lite i oordning och man måste göra ”det bästa av situationen”, eller som det också kallas regressionsanalys. Det innebär att man använder olika matematiska hjälpmedel för att anpassa sina mätvärden till just en matematisk funktion så att man kan testa sina teorier. 1.7.1 Linjäranpassning En vanlig anpassning är en linjär funktion, alltså en funktion på formen y = kx + m. Ett exempel där man använder linjär regression är Hookes lag som beskriver kraften i en fjäder med avseende på förskjutningen från jämviktsläget som F = Kx, där K är fjäderkonstanten och x är förskjutningen. Det finns många sätt att göra detta i MATLAB, ett av dem är att använda funktionen polyfit(x,y). Säg att vi har en mätserie för kraften hos en fjäder, F, mot dess förskjutning, x. För att linjäranpassa dessa värden skriver vi p = polyfit(x,F,1) Vi får då tillbaka en lista innehållande värdena för linjäranpassningen. Vi återfår dessa genom att skriva k=p(1) och m=p(2). I vårt exempel står k för fjäderkonstanten i Hookes lag och m ligger nära 0. För att kunna visualisera vår linjäranpassning använder vi funktionen polyval(p,x) som skapar en lista med värden för linjäranpassningen skapad av polyfit(x,y). Koden för detta skulle kunna se ut som nedan och det resulterar i figur 13. Notera att linjäranpassningen ibland ligger ovanför mätpunkterna och ibland under. F=[0 0.49 0.98 1.47 1.96 2.45 2.94 3.43 3.92 4.41]; x=[0 0.016 0.037 0.066 0.092 0.116 0.143 0.154 0.176 0.188]; 1 INTRODUKTION TILL MATLAB 20 p=polyfit(x,F,1); F lfit=polyval(p,x) plot(x,F,'rx'); hold on plot(x,F lfit) title('Hookes lag') xlabel('Forskjutning') ylabel('Kraft') legend('Matdata','Linjaranpassning') Figur 13: Mätdata plottat tillsammans med dess linjäranpassning. 1.7.2 Konfidensintervall Ett konfidensintervall anger det intervall omkring medelvärdet inom vilket det sanna värdet finns med en viss sannolikhet. De vanligaste sannolikhetsnivåerna är 95% och 99%. För detta ändamål måste vi först göra en linjäranpassning som i avsnitt 1.7.1 men nu skapar vi även parametern S som innehåller feluppskattningar m.m. [p S]=polyfit(x,F,1); För att skapa konfidensintervallet använder vi funktionen polyconf(p,x,S,alpha). Den skapar samma lista som funktionen polyval(p,x) samt en lista med konfidensintervall för varje punkt. Vilken sannolikhetsnivå man vill ha bestäms av 1−alpha t.ex. alpha=0.05 ger en 95-procentig nivå. Vi utvecklar exemplet i avsnitt 1.7.1 med ett 95-procentig konfidensintervall och resultatet kan ses i figur 14. F=[0 0.49 0.98 1.47 1.96 2.45 2.94 3.43 3.92 4.41]; 1 INTRODUKTION TILL MATLAB 21 x=[0 0.016 0.037 0.066 0.092 0.116 0.143 0.154 0.176 0.188]; [p S]=polyfit(x,F,1); [F lfit delta] = polyconf(p,x,S,0.05); plot(x,F,'rx'); hold on plot(x,F lfit) plot(x,F lfit+delta,'r−−') plot(x,F lfit−delta,'r−−') title('Hookes lag') xlabel('Forskjutning') ylabel('Kraft') legend('Matdata','Linjaranpassning','Konfidensintervall') Figur 14: Mätdata plottat tillsammans med dess linjäranpassning och ett 95procentigt konfidensintervall. 1.7.3 Polynomanpassning Ibland följer sina mätvärden inte någon rät linje utan de har något mer komplicerat samband. Då kan man använda sig utav ett polynom av högre ordning t.ex. y = a1 x3 + a2 x2 + a3 x + a4 som är ett polynom av ordning 3. För att göra en anpassning av högre ordning gör vi som i avsitt 1.7.1 med skillnaden p=polyfit(x,y,n) där n är ordningen på polynomet som man vill anpassa med. I vårt exempel skulle vi sätta n = 3. Det vi får tillbaka från p=polyfit(x,y,n) är värdena på konstanterna som ska stå före varje variabel (a1 , a2 , a3 och a4 ovan). Vi hittar dessa genom att skriva: a1=p(1) a2=p(2) a3=p(3) 1 INTRODUKTION TILL MATLAB 22 . . o.s.v. 1.7.4 Linjärisering av potensfunktion Ibland vet man inte vilken ordning sitt polynom har och då är det användbart att använda logaritmfunktioner. Säg att vi vill undersöka ett objekts bakomlagda sträcka, s, under fritt fall. Vi har gjort flera mätningar av objektets position efter olika tider t och vi vet att sträckan beskrivs som s = atb där a och b är konstanter. Detta ger att vi kan räkna ut g, tyngdaccelerationen, ur sambandet s = 12 g · t2 . För att lösa ut dessa konstanter börjar vi med att logaritmera funktionerna. s=[0.05 0.1 0.15 0.2 0.25 0.3 0.35 0.4 0.45]; t=[0.009 0.04 0.096 0.175 0.278 0.404 0.556 0.728 0.926]; lns=log(s); lnt=log(t); Här är funktionen log() den naturliga logaritmen. Vi får nu ett uttryck på formen ln(s) = ln(a) + b ln(t). Nu gör vi en linjäranpassning på våra logaritmerade värden som i avsnitt 1.7.1 och får ut att våra värden blir: ln(a) = 1.63 =⇒ a = e1.63 ≈ 5.10 b = 2.11 ≈ 2. Vi får då alltså s = 5.10 · t2 =⇒ 1 s = 10.2 · t2 2 Det vill säga att g = 10.2 enligt detta experiment. 1.7.5 Värdering av resultat Efter att man har gjort sina mätningar och fått sina resultat ska man bestämma osäkerheten i resultaten. Det är viktigt för att det ger ett mått på hur korrekt och tillförlitliga de är. Ett vanligt osäkerhetsmått vid en linjäranpassning är R2 som visar hur bra sin anpassningen är. Det är ett värde mellan 0 och 1 och ju närmare 1 det är desto bättre är anpassningen. Tyvärr är detta värde lite krånligt att ta fram i MATLAB så vi beskriver hur vi tar fram det utan att lägga för stor vikt vid att förklara nya begrepp. Vi fortsätter kolla på exemplet i avsnitt 1.7.1. För att få fram R2 -värdet så måste vi beräkna s.k. residualer som sparas i variablen Fresid: Fresid=F−F lfit; Sedan behöver vi räkna ut summan av residualerna i kvadrat samt summan av mätvärdena i kvadrat. Det sistnämnda gör vi med hjälp av variansen genom kommandot var: 1 INTRODUKTION TILL MATLAB 23 SSresid = sum(Fresid.ˆ2); SStotal = (length(F)−1)*var(F); Vi finner sedan värdet R2 genom r sqr = 1 − SSresid/SStotal; I vårt exempel får vi r sqr=0.9998. Ett annat osäkerhetsmått vid linjäranpassning är adj.R2 . Det är alltid mindre än eller lika med R2 -värdet och ger en mer pålitlig uppskattning av hur bra linjäranpassningen är. Vi skapar den genom rsq adj = 1 − SSresid/SStotal * (length(F)−1)/(length(F)−length(p)) Om man tyckte att denna del var lite komplicerad kan man vara lugn, det krävs nämligen betydligt mer kött på benen för att förstå allt detta fullt ut. Om man vill läsa mer om R2 finns det en bra Wikipedia-artikel som heter ”Coefficient of determination” som kan vara värd att kolla igenom för lite fördjupning. 2 PROGRAMMERING MED MATLAB 2 24 Programmering med MATLAB Att programmera i MATLAB har många fördelar. För det första är det relativt enkelt att implementera sin kod, men det är också bra för att man kan hantera stora mängder data då MATLABs specialitet är matriser. När man programmerar är det viktigt, oberoende av vilket språk man sitter i, att man förstår problemet innan man kastar sig på datorn för att skriva massa kod. Tänk efter vad det är som ska lösas och dela upp problemet i mindre småproblem. Då kan man lösa ett delproblem i taget och kontinuerligt testa om den delen fungerar innan man går vidare till nästa. För att testa om koden fungerar som man har tänkt sig kan man bland annat använda sig av utskrifter, som man sen tar bort, för att se om variablerna har samma värde som man tänkt sig. Andra bra tips är att kommentera sina koder och att indentera, det gör det lättare både för dig och eventuell hjälp att förstå koden. Fler programmeringstips finns under appendix C Tips och Trix. I det här kapitlet kommer vi avancera till att använda MATLAB för att programmera. Det gör vi genom att först prata mer om vektorer, för att sedan introducera matriser, titta på lite mer avancerade loopar och script samt hur man läser in data till MATLAB. 2.1 Introduktion till vektorer och matriser I introduktionskapitlet beskrevs vektorer som en lista av tal och begrepp som klammeroperatorn och kolonoperatorn introducerades. I det här kapitlet kommer vi tydligare förklara vad en vektor och en matris är, hur man kan skapa dem och hur de fungerar. 2.1.1 Vektorer i MATLAB Det finns två olika sorters vektorer, radvektorer och kolumnvektorer. Väldigt logiskt så har radvektorn sina tal sparade som en rad och kolumnvektorn som en kolumn. Skillnaden mellan dess sätt att spara sina vektorer är viktigt i linjär algebra för att matematiken ska stämma, mer om det kommer i kapitlet 4 Linjära system med MATLAB. Just nu räcker det att vi vet att det finns olika sorters vektorer. För att skapa en radvektor, med namnet row, skriver vi: row = [1 2 3 4 5] row = 1 2 3 4 5 Man kan även skapa en radvektor genom att sätta ett komma mellan siffrorna: row = [1, 2, 3, 4, 5]; För att skapa en kolumnvektor separerar man siffrorna med semikolon: column = [1; 2; 3; 4; 5] column = 2 PROGRAMMERING MED MATLAB 25 1 2 3 4 5 Om man råkar skapa en radvektor när man egentligen vill ha en kolumnvektor kan det lätt åtgärdas genom att ta transponatet av radvektorn: row = [2 4 6 8 10]; column = row' column = 2 4 6 8 10 Transponatet av en vektor kan man ta antingen med 0 eller med kommandot transpose. Vektorer i MATLAB indexeras från 1, vilket betyder att om man vill ha ut det första värdet i en vektor skriver man: prime = [2, 3, 5, 7, 11, 13, 17, 19]; prime(1) ans = 2 Om man vill ha ut det sista värdet i en vektor kan man använda sig utav kommandot end: prime(end) ans = 19 När vi har pratat om vektorers index har vi hittills endast använt en siffra i som representerat det i:te talet i vektorn. Men för att kunna skilja på rad- och kolumnvektorer måste man använda sig utav två index. Ett index som motsvarar raden och ett som motsvarar kolumnen. Det tredje talet i en radvektor skrivs alltså row(1,3) vilket betyder att vi vill ha talet som är i första raden och i tredje kolumnen. Det tredje talet i en kolumnvektor hittas genom column(3,1). Det kan tyckas onödigt då row(3) och column(3) kommer returnera samma värde som row(1,3) och column(3,1) men sättet med två index är tydligare och kommer behövas då vi introducerar matriser i kapitel 2.1.2. Matriser fungerar litegrann som 2-dimensionella vektorer. En radvektor har alltid 1 som radindex och en kolumnvektor har alltid 1 som kolumnindex. Kolonoperatorn, som vi tidigare i kapitel 1.4.2 använt för att skapa vektorer, kan även användas för att ta ut värden ur en vektor: prime(4:end) 2 PROGRAMMERING MED MATLAB 26 prime = 7 11 13 17 19 I ovanstående exempel har vi plockat ut det fjärde elementet till och med det sista genom att använda end. Man kan även ta ut varannat tal i en vektor: prime(1:2:end) prime = 2 5 11 17 På liknande sätt kan man ta ut vart n:te tal genom prime(1:n:end) där n kan vara vilket heltal som helst. Om man vill ändra värden i en vektor kan man också använda sig av kolonoperatorn och end: prime(1:2:end)= 1 prime = 1 3 1 7 1 13 1 19 En vektor kan skapas på många olika sätt. I kapitel 1.4.2 har vi nämnt klammeroperatorn och kolonoperatorn, men MATLAB har även inbyggda kommandon som är väldigt användbara när man lärt sig dem. Bland annat kan man om man vet vilken längd man vill ha på sin vektor skapa en vektor med den längden, antingen bestående av nollor eller ettor med hjälp av kommandona zeros och ones: zeros = zeros(3,1) ans = 0 0 0 ones = ones(1,3) ans = 1 1 1 I det här exemplet får vi en kolumnvektor zeros bestående av 3 nollor och en radvektor ones bestående av 3 ettor. Om vet vilken storlek sin vektor kommer att ha är det bra att allokera minne genom att först skapa en vektor med nollor eller ettor. Storleken på vektorer har annars en tendens att växa obehindrat i t.ex. loopar, vilket är krävande. Att ändra värdena är enklare än att låta vektorn växa då man lägger till något. Om man vill ha många värden i sin vektor, t.ex. om man behöver lagra en vektor med 100 sekunder kan man använda sig av linspace: 2 PROGRAMMERING MED MATLAB 27 seconds = linspace(1,100); Genom att använda linspace på detta sättet skapar man en vektor från 1 till 100 med 100 värden. Vill man ha fler, eller färre, värden i sin vektor kan man ange antalet värden i sin vektor genom att lägga till en inparameter: seconds = linspace(1,100,50); Nu får vi en vektor från 1 till 100 med 50 värden. Detta sätt att skapa en vektor ger ofta punkter på decimalform eftersom intervallet ska delas upp i ett givet antal delintervall. Notera att linspace alltid skapar en radvektor så behöver man en kolumnvektor får man ta transponatet av radvektorn som linspace returnerar. Om man vill skapa en vektor med slumpade tal kan man använda sig av rand för slumpade tal mellan 0 och 1 eller randi för slumpartade heltal där första inparametern representerar maxvärdet. random = rand(1,4) random = 0.6948 0.8344 0.6096 0.5747 random2 = randi(15, 4, 1) random2 = 5 7 11 15 I det övre exemplet får vi en radvektor med 4 slumpade värden mellan 0 och 1. I det undre exemplet får vi en kolumnvektor med 5 slumpade heltal med maxvärdet 15. För att kombinera dessa två funktioner och få slumpade tal på decimalform som är större än ett: random + random2' ans = 5.6948 2.1.2 7.8344 11.6096 15.5747 Matriser i MATLAB I inledningen av detta avsnitt nämnde vi att MATLABs styrka ligger i dess sätt att hantera matriser och i detta delkapitel kommer vi gå igenom vad en matris är, hur vi skapar matriser och vad vi kan göra med dem. En matris är en samling av rad- och kolumnvektorer som bildar ett rutnät av värden. Alla vektorer som bygger upp matrisen måste vara lika långa men radvektorerna behöver nödvändigtvis inte vara lika långa som kolumnvektorerna. Oftast har man 2-dimensionella matriser, alltså som ett schackbräde med värden, men man kan även ha flerdimensionella matriser men det kommer vi inte titta närmare på här. 2 PROGRAMMERING MED MATLAB 28 För att skapa en matris gör man på samma sätt som då vi skapade rad- och kolumnvektorer men kombinerat: matrix = [1, 2, 3; 4, 5, 6; 7, 8, 9] matrix = 1 4 7 2 5 8 3 6 9 Man kan även skapa en matris genom att sätta ihop radvektorer: row1 = [10 11 12]; row2 = [20 21 22]; matrix = [row1;row2] matrix = 10 20 11 21 12 22 Många av de sätt vi nyss lärde oss att använda för att skapa vektorer kan vi även använda då vi vill skapa matriser, så som zeros, ones, rand och randi. Notera att man inte kan använda linspace för att skapa matriser. Precis som tidigare anger vi hur många rader och kolumner som vi vill ha. zeros = zeros(2,3) zeros = 0 0 0 0 0 0 Om man vill ha en matris med lika många rader som kolumner behöver man endast ange ett värde: random = rand(3) random = 0.3017 0.0117 0.5399 0.0954 0.1465 0.6311 0.8593 0.9742 0.5708 För att ändra ett värde eller plocka ut från matrisen gör man på liknande sätt som för vektorer. Det man måste komma ihåg är att ange både vilken rad och kolumn som man vill använda. matrix = [1, 2, 3; 4, 5, 6; 7, 8, 9] matrix(2,3) ans = 6 2 PROGRAMMERING MED MATLAB 29 Om man vill ändra alla värden på en viss rad kan man använda sig av kolonoperatorn. Uttrycket matrix(1,:) betyder första raden, alla kolumner: matrix = [1, 2, 3; 4, 5, 6; 7, 8, 9] matrix(1,:) ans = 1 4 7 Man kan även använda sig av end för att ändra vissa värden i en matris. matrix(:,2:end) = 10 ans = 1 4 7 10 10 10 10 10 10 Översta raden i ovanstående exempel betyder att på alla rader, från och med andra kolumnen till sista, ska värdet ändras till 10. 2.1.3 Vektor- och matrisoperationer När man arbetar med vektorer och matriser är det viktigt att hålla koll på de vektor- och matrisoperationer som finns och skillnaden mellan dem. Som nämndes i introduktionskapitlet, avsnitt 1.4.2, så kan man multiplicera en vektor elementvis med sig själv men det finns även andra sätt som vi kommer gå igenom i detta delkapitel. Både vektorer och matriser kan hantera addition och subtraktion genom att operationen utförs elementvis: matrix1 = [4, 5, 32; 3, 7, 14]; matrix2 = 4*ones(2,3); matrix1−matrix2 ans = 0 −1 1 3 28 10 matrix1 + matrix2 ans = 8 7 9 11 36 18 I det övre exemplet har vi t.ex. 32 − 4 = 28 och i det undre 32 + 4 = 36 i den första raden och tredje kolumnen. Man kan även multiplicera, dividera och upphöja vektorer och matriser elementvis: 2 PROGRAMMERING MED MATLAB 30 matrix1.*matrix2 ans = 16 12 20 28 128 56 matrix1./matrix2 ans = 1.0000 0.7500 1.2500 1.7500 8.0000 3.5000 matrix1.ˆ2 ans = 16 9 25 49 1024 196 Notera punkten framför operationen som symboliserar att operationen utförs elementvis. Det som är viktigt att tänka på när man utför dessa elementvisa operationer är att de vektorer och matriser som används måste ha samma dimensioner, alltså samma antal rader och kolumner, annars kommer MATLAB inte förstå vad den ska göra och ge ett felmeddelande. När man jobbar med matriser i MATLAB kommer man ofta få felmeddelanden, så det gäller att förstå dem när de dyker upp. Exempel på vanliga felmeddelanden och hur man kan åtgärda dem finns i appendix B Vanliga fel. Addition, subtraktion, multiplikation, division och upphöjt på det sätt som nu förklarats kallas för array-operationer och det viktiga med detta sätt är punkten framför operationen som man vill utföra. Den betyder att operationen kommer utföras element för element i vektorn eller matrisen och det är därför viktigt med samma dimension. Nu ska vi gå vidare och introducera ett nytt begrepp, matrisoperationer. Matrisoperationerna skiljer sig från array-operationerna på det sättet att man inte tar multiplikation och division elementvis. Istället följer man de matematiska reglerna som används i linjär algebra. I den här guiden kommer dessa regler förklaras ytterliggare i kapitel 4 Linjära system med MATLAB. Just nu räcker det att vi vet att det finns olika sätt att multiplicera och dividera matriser och att resultaten blir annorlunda. För att matrismultiplicera två matriser krävs det att den första matrisens kolumner ska vara lika många som den andra matrisens rader. Detta kan kännas en aning rörigt så vi tittar på ett exempel: matrix1 = [4,5;7,4;9 0] matrix1 = 4 7 9 5 4 0 matrix2 = [3,5,11;7,9,12] matrix2 = 2 PROGRAMMERING MED MATLAB 3 7 5 9 31 11 12 matrix1*matrix2 ans = 47 49 27 65 71 45 104 125 99 matrix2*matrix1 ans = 146 199 35 71 Notera att den nya matrisen som skapas när vi tar matrix1 matrismultiplicerat med matrix2 har fått en ny dimension med 3 rader och 3 kolumner. Tar vi matrismultiplikationen åt andra hållet får vi en matris med 2 rader och 2 kolumner. Ordningen av multiplikationen är alltså av betydelse. Om man vill ta samma matris multiplicerat med sig själv måste vi använda oss av transponat-operatorn för att få dimensionerna att stämma: matrix1 = [4,5;7,4;9 0] matrix1 = 4 7 9 5 4 0 matrix1*matrix1' ans = 41 48 36 48 65 63 36 63 81 Om detta var svårt att förstå så är det ingen större fara. Man kommer lära sig mycket mer om vektorer och matriser i kursen om linjär algebra. Huvudsaken just nu är att vetskapen om att det finns olika sätt att räkna på då det gäller matriser finns. Det är viktigt att man kontinuerligt, då man programmerar, kontrollerar att resultatet blev som man tänkte sig. Så länge man har koll på det är det lugnt. Vi kan avsluta detta delkapitlen med att reda ut några begrepp. Här har vi pratat om vektorer och matriser men ni har säkert hört talas om arrayer, eller så kommer ni att göra det i framtiden och då kan det vara bra att veta skillnaden. I MATLAB är en vektor en rad eller kolumn med värden. En array är en 2-dimensionell vektor med värden som klarar array-operationer. En matris är också en 2-dimensionell vektor men med matrisoperationer. Skillnaden mellan array-operationer och matrisoperationer är, som vi tidigare nämnt att array-operationerna utförs elementvis medan matrisoperationerna följer de matematiska reglerna i linjär algebra. 2 PROGRAMMERING MED MATLAB 2.2 32 Mer om loopar Ett väldigt viktigt verktyg när man programmerar är att kunna hantera loopar av olika slag. När man har en översiktlig koll på de olika looparna som finns och hur de fungerar spelar det inte så stor roll i vilket programmeringsspråk som man sitter i. Det enda som skiljer är nämligen syntaxen, alltså hur man skriver koden, och den kan man kolla upp. Som tidigare nämnts är det viktigt att förstå problemet innan man börjar koda och att dela upp problemet i små delproblem. Om man gör det kommer man se att det finns väldigt många delproblem som kan lösas med hjälp av loopar. Vi har tidigare nämnt if, for och while vilket är de tre loopar som är mest grundläggande. Om man lär sig att kombinera dessa på ett bra sätt kan man lösa mycket. En loop inuti en annan loop kallas för nästlade loopar och kan vara mycket effektivt, speciellt när man har med matriser att göra. Ett vanligt exempel på nästlade loopar är nästlade for-loopar. På varje ställe i matrisen area vill vi samla arean av en rektangel med sidan som radens index och höjden som kolumnens index. for i=1:3 for j=1:3 area(i,j) = k; end end area = 1 2 3 2 4 6 3 6 9 Den yttersta for-loopen räknar upp matrisens radindex i och den inre räknar upp matrisens kolumnindex j. Först är i lika med 1 och j lika med 1, 2 och 3 innan i ökas till 2, o.s.v. Genom att göra en sån här loop ökar storleken på matrisen för varje varv i loopen. Det man kan göra då för att tjäna datortid är att allokera minne för matrisen med matris = zeros(3). Anledningen till att man skapar en matris med rätt dimensioner men fel element och sedan definierar om elementen så att de stämmer, är att man sparar beräkningstid. Gör man inte en matris i förväg, så kallad förallokering, definierar MATLAB en ny matris för varje iteration, vilket är en betydligt större ansträngning än att bara ändra ett element i matrisen. Ha därför för vana att alltid allokera minne i förväg när det ska skapas stora matriser eller vektorer. Man kan också kombinera de olika looparna med varandra, t.ex. en if utanför en for. Detta kan vara väldigt effektivt som validering av data då man bara vill utföra for-loopen om ett visst villkor är uppfyllt: index = 0; last = 4; if (index > 0) for i=index:last vector(i) = i end end 2 PROGRAMMERING MED MATLAB 33 Om index är 0 som i detta fallet går vi aldrig ens in i for-loopen. Om index hade varit 0 och vi inte hade haft if utanför for hade vi fått ett felmeddelande som talar om att vi försöker komma åt index 0, vilket inte existerar i MATLAB: Attempted to access vector(0); index must be a positive integer or logical. MATLAB returnerar felmeddelanden som detta när MATLAB inte förstår vad man menar. Då gäller det att försöka tyda utskrifterna, vilka är mer eller mindre standardiserade. Exempel på vanliga felmeddelanden finns under appendix B Vanliga fel. Att lägga en if-sats utanför en annan loop på detta sätt gör att man kan kontrollera fel som annars kan uppstå, tex undvika att dela med noll, negativa tal på längder o.s.v. I introduktionskapitlet, avsnitt 1.5, nämnde vi if och else men det finns även ett tredje alternativ, elseif. När man bygger en if-sats med många olika alternativ ska man tänka på att ta det vanligaste alternativet först, eftersom om man går in i if-satsen inte kommer gå in i elseif eller else. if kombineras vanligtvis med de logiska operationerna som vi gick igenom i introduktionen, se avsnitt 1.4.3. Tänk på att man även kan ha flera olika logiska uttryck i en if. Att kombinera många olika logiska uttryck med if och elseif kan göra programmet effektivare så det är viktigt att tänka igenom vilka alternativ man kan tänka sig få och hur man kan kombinera dem. weather = 1; %1 means sun, 0 means rain temperature = 22; if(weather>0) if(temperature<0) disp('Soligt, minusgrader') elseif(temperature==0) disp('Soligt, nollgradigt ') elseif(temperature>0 && temperature<21) disp('Soligt, plusgrader') else disp('Soligt, oever 20 grader') end else disp('Regn') end I det här exemplet har vi förutsatt att man endast är intresserad av temperaturen om det är soligt och att det oftare är kallt än varmt. När man bygger en if-sats behöver man inte ha med varken elseif eller else utan ibland behöver man endast en if-rad t.ex. i början av sin kod. I nedanstående exempel vill vi kontrollera att numret vi skickar in till while-loopen är positivt för att kunna räkna ut fakulteten. number = 4; factorial = 1; if number>0 while (number>1) factorial= factorial*number; number = number−1; end end 2 PROGRAMMERING MED MATLAB 34 En ny variant som vi inte tidigare nämnt är switch-satsen: mynumber = 1; switch mynumber case −1 disp('Negativ etta'); case 0 disp('Noll'); case 1 disp('Positiv etta'); otherwise disp('Annan siffra'); end En switch fungerar som så att man har ett switch-uttryck som antingen är ett nummer eller en textsträng som man sedan evaluerar mot ett case-uttryck. När switch-uttrycket och case-uttrycket överensstämmer utförs raden under. I ovanstående exempel är mynumber 1 och case 1 evalueras och skriver ut ”Positiv etta”. I en switch-sats evalueras endast ett case och satsen avbryts efter blocket är utfört. Om inget av de olika alternativen stämmer körs otherwise vilket kan jämföras med else. Notera att otherwise är valbart precis som else. 2.3 Script i MATLAB I introduktionen, avsnitt 1.5, nämnde vi att ett script är ett dokument i MATLAB som kör koden i filen rad för rad. Vi introducerade även funktioner med funktionshuvud bestående av inparametrar och returvärden. I båda dessa fallen använde vi oss av MATLABs M-filer, vilket man ska ha som vana att alltid använda då M-filerna måste sparas för att kunna köras. Med smarta namngivningar kan man därför lätt hålla koll på sina olika koder. Frågan är nu när man ska använda sig utav script och funktioner. Generellt sett ska man skapa funktioner då man inser att man upprepar samma kod flera gånger. T.ex. då man på flera ställen i sin kod räknar ut en area kan man skapa en funktion som gör detta istället och endast anropa funktionen på flera ställen med olika inparametrar. Tänk på att när man har funktioner så skapas lokala variabler som inte senare kan användas. Script ska man som sagt alltid ta för vana att använda sig av om man skriver lite längre kod som man vill spara, ska man endast testa mindre saker kan script vara lite överdrivet. Ett nytt begrepp som vi inte tidigare pratat om är anonyma funktioner. När vi pratar om vanliga funktioner skapar vi funktionen i en extern M-fil och har en annan M-fil med själva huvudprogrammet. Men ibland kan det behövas en mindre funktion, då kan det kännas onödigt att skapa en ny fil bara för det. Den anonyma funktionen skrivs alltså i samma M-fil som huvudprogrammet, förslagsvis samlade på samma ställe. Ett exempel kan vara om man på många ställen vill kvadrera tal: sqr = @(x) x.ˆ2; Variabeln sqr är ett funktionshandtag, @-operatorn skapar själva handtaget och berättar att det är en anonym funktion, x:et inom parantesen beskriver att funktionen tar ett x-värde som inparameter. Denna funktion returnerar ett värde för varje inparameter x, så två inparametrar ger två returvärden: 2 PROGRAMMERING MED MATLAB 35 number = [2 3]; sqr(number) sqr = 4 9 Man kan även skapa anonyma funktioner med inga eller flera inparametrar. constant = @() 10; constant() + 1 ans = 11 myfunction = @(x,y) (3*x + 12*y.ˆ2 − x*y); myfunction(3,4) ans = 189 2.4 Läsa in data i MATLAB I detta delkapitel ska vi lära oss hur man läser in data till MATLAB. Vad som är viktigt att komma ihåg är att filen man vill läsa in ifrån måste ligga i samma mapp som sin M-fil för att MATLAB ska hitta det. Man kan läsa in många olika filer i MATLAB, t.ex. filer från andra program. Men hur man gör det kommer senare i A Appendix: Rapportskrivning med MATLAB, det vi ska lära oss nu handlar om hur man läser in data från textfiler. Det finns många olika sätt att läsa in data i MATLAB och efter en snabb anblick kan det verka som att alla sätt fungerar exakt likadant. Ofta kan man läsa in en fil på många olika sätt och många har säkert sitt favoritsätt som man alltid använder. Men självklart är inte alla sätt identiska och därför kan det vara bra att titta på några olika så man vet vilka som finns och när dom är bra att använda. Vi böjar med hur man läser in data med hjälp av MATLABs grafiska interface. Det kan man göra på två olika sätt beroende på om man vill läsa in data från en fil eller från ett urklipp. För att importera från en fil klickar man Home → Import data, dubbelklickar på filen man vill läsa in ifrån och ett nytt fönster öppnas, se figur 15. Här har vi läst in en textfil med endast siffror där ena kolumnen motsvaras av längder och den andra kolumnen av skostolekar. Man kan välja att läsa in textfilen som vektorer eller en matris. Om man läser in som en matris kommer matrisen ha samma namn som textfilen om man inte själv ändrar det. Om man väljer att läsa in två kolumnvektorer kommer de ha namnen VarName1 och VarName2 som initialnamn men det kan man ändra genom at dubbelklicka på namnet och ändra till t.ex. length och size. När man är redo att importera sin data klickar man på ikonen import längst upp till höger. 2 PROGRAMMERING MED MATLAB 36 Figur 15: Det nya fönstret som öppnas i MATLAB då man läser in datafiler. För att importera data från urklippet klickar man på den lilla triangeln i Workspace och klickar sedan klistra in. För att använda denna metod krävs det att det man har kopierat kan tydas av MATLAB. Jämför att kopiera siffersekvensen 3 4 5 med four = 4. Om man ska ändra ofta i en textfil men man hela tiden vill läsa in datat för att t.ex. plotta skostorleken mot längden kan det vara bra att läsa in textfilen i ett script. Så länge filen inte byter namn kan man då köra samma program men få olika figurer beroende på hur textfilen senast är sparad. importdata: Det enklaste sättet att göra detta på är att använda sig utav importdata: A = importdata('numbers.txt'); length=A.data(:,1); size=A.data(:,2); Det här sättet är ett väldigt enkelt sätt att läsa in data till en matris om man bara har siffror och vet hur filen är uppbyggd, vilket man väldigt ofta vet då man själv skapat textfilen. Textfilen som vi använt i detta exempel är alltså samma som i tidigare exempel. Ibland kan man ha textfiler med en rad med text ovanför som beskriver vad datapunkterna motsvarar. T.ex. skulle det kunna stå length och size överst i filen. Det man kan göra då för att bara ta ut datapunkterna är att använda sig av delimiterIn och headerlinesIn som beskriver hur datat är separerat från varandra och på vilken rad man ska börja läsa in datat. filename = 'numbers.txt'; delimiterIn = ' '; headerlinesIn = 1; A = importdata(filename,delimiterIn,headerlinesIn); Ovanstående exempel är alltså bra om man har en rad med text ovanför sina datapunkter. load: Ett annat kommando som man kan använda sig av för att läsa in data från filer till en matris är load. Utan textrad överst skriver man: 2 PROGRAMMERING MED MATLAB 37 filename = 'numbers.txt'; A = load(filename); dmlread: Det sista sättet vi går igenom här är dmlread. Med detta kommando kan man endast läsa in filer som består utav numeriska data. En fördel med detta sätt är att man enkelt kan bestämma på vilken rad och kolumn man vill börja läsa in filename = 'numbers.txt'; row = 1; column =2; A = dmlread(filename, row, column); 3 MATEMATISK ANALYS MED MATLAB 3 38 Matematisk analys med MATLAB MATLAB kan vara ett användbart verktyg när man ska arbeta med analytiska problem i en eller flera variabler. I det här avsnittet ska vi se hur MATLAB kan hjälpa oss att derivera, integrera och lösa differentialekvationer. Utöver detta material så finns även ett betydligt längre och mer genomgående kompendium gjord av Institutionen för Matematik och Matematik Statistik där matematiken är mer grundligt förklarad. 3.1 Funktioner och derivator Vi börjar med ett exempel som tillämpar den kunskap som har behandlats i det inledande avsnittet 1 och avsnitt 2 som rör programmering i MATLAB. Om nedanstående exempel känns svårt bör man repetera innan man går vidare. Låt säga att man har en funktion f (x) = ln(x). Ett bra sätt att förstå funktionen bättre vore att rita upp den. Med MATLAB kan detta göras genom att skriva: x=0:0.1:3; f=@(x) log(x); plot(x,f(x)) xlabel('x') ylabel('ln(x)') Då erhålls en graf som ser ut som den i figur 16. Figur 16: En graf av ln(x) mot x. För att förstå funktionen ännu bättre skulle det kunna vara vettigt att rita upp dess derivata. Ett sätt att göra detta är att i MATLAB implementera 3 MATEMATISK ANALYS MED MATLAB 39 derivatans definition f 0 (x) = lim h→0 f (x + h) − f (x) . h (1) Utefter definitionen i ekvation (1) skapar vi en MATLAB-funktion som numeriskt gör deriverandet åt oss: function f prime=differential operator(f,x,h) f prime = (f(x+h)−f(x))/h; end Funktionen differential operator returnerar f prime som helt enkelt är derivatan av f som är den funktionen som vi vill derivera. För att utvärdera och rita ut derivatan, vilken står att finna i figur 17, skriver man sedan: h=0.00001; f prime=differential operator(f,x,h); plot(x,f prime) xlabel('x') ylabel('d/dx(ln(x))') Här har vi nöjt oss med att låta h anta värdet 0.00001 istället för att närma sig oändligt nära noll. Det är så man hanterar gränsvärden i MATLAB. Var dock försiktiga och använd inte allt för små tal eftersom MATLAB då kan avrunda dem till noll. Det är speciellt vanligt med avrundningsfel då något stort tal adderas med något litet. Man utvärderar funktionsuttryck genom att helt enkelt skriva numeriska värden som är mycket nära gränsvärdet man söker. Att välja ett mindre h ger ett mer korrekt värde medan ett större h ger ett mindre korrekt värde. 3 MATEMATISK ANALYS MED MATLAB 40 Figur 17: En graf av derivatan av ln(x) mot x. 3.2 Eulers metod För att lösa differentialekvationer kan man använda sig av Eulers metod. Tillvägagångssättet är att man delar in intervallet man är intresserad utav i diskreta tidssteg och sedan beräknar yn+1 = yn + yn0 · ∆t (2) där ∆t är tidsstegets längd och y är funktionen vid ett visst tidssteg. För att få ett slags intuition bakom metoden kan man visualisera att om man skulle följa en funktions tangent en kort sträcka skulle funktionen inte hinna ändra sig särskilt mycket gentemot tangenten. Man kan också se det som att man får ut f genom att integrera dess derivata f 0 med en Riemannsumma. Ännu ett sätt att se det är att man approximerar funktionen med en Taylorserie fast man har ∆t så litet att alla termer högre än ett anses vara försumbara. Låt oss implementera denna metod i MATLAB på genom ett exempel. Säg att vi skjuter ut en kanonkula, se figur 18, med massa m ur en kanon, på plats r0 = (0, 0), som kan accelerera kanonkulan till en initial hastighet av v0 = (vx0 , vy0 ). Givet att luftmotståndet går att försumma, hur långt kan då kanonkulan flyga? 3 MATEMATISK ANALYS MED MATLAB 41 Figur 18: En schematisk bild av banan av en kanonkula som från origo skjuts iväg med en initial hastighet v0 = (vx0 , vy0 ). Innan vi kan använda Eulers metod måste vi såklart ställa upp ett uttryck för problemet. Positionens derivata med avseende på tiden är hastigheten och hastighetens derivata med avseende på tiden är accelerationen vilket ger den kopplade differentialekvationen: r0 = v 0 v =a (3) (4) Dessutom vet vi att den enda kraften som påverkar systemet är gravitationskrafF = (0, − mg ten −mg, så accelerationen bör vara F = ma ⇒ a = m m ) = (0, −g). Följaktligen bör Eulers uppdateringsschema definierat i ekvation (2) bli rn+1 = rn + vn · ∆t (5) vn+1 = vn + a · ∆t (6) Implementeringen av detta exempel i MATLAB resulterar i följande script: r0=[0,0]; %Initial position v0=[75,50]; %Initial velocity deltat=0.001; %Time step g=9.82; a=[0,−g]; %Acceleration r(1,:)=r0; v(1,:)=v0; r(2,:)=r(1,:)+v(1,:)*deltat; %Updating position v(2,:)=v(1,:)+a*deltat; %Updating velocity i=2; while(r(i,2)>=0) %Finished when the canon ball hits the ground r tmp=r(i,:)+v(i,:).*deltat; %Updating position v tmp=v(i,:)+a*deltat; %Updating velocity i=i+1; r(i,:)=r tmp; v(i,:)=v tmp; end plot(r(:,1),r(:,2)) 3 MATEMATISK ANALYS MED MATLAB 42 Viktigt att tänka på när man implementerar sin lösning är hur man väljer att definiera det som ska beräknas. I vårt exempel använder vi exempelvis av två stycken tvådimensionella vektorer för hastighet respektive position, v och r. Men det vore också möjligt att definiera systemet som fyra enkelvariabler motsvarande x, y, v x och v y. Eller kan man hantera en vektor på formen vec = [x,y,v x,v y]. Alternativen är många och det är därför viktigt att välja en form som returnerar ett resultat på en form som sedan fungerar i nästa steg. En annan, kanske ännu viktigare aspekt är att vid implementeringen se till att ha rätt tidssteg. Med ett för litet tidssteg kommer det behövas så många beräkningar att programmet blir hopplöst långsamt och om det är för stort tidssteg får uträkningen dålig precision. Om tidssteget, eller intervallet som ska beräknas, är allt för långt kommer lösningen divergera mot oändligheten. Begreppet för detta fenomen kallas stabilitet och Eulermetodens stabilitet är erkänt dålig. Den är faktiskt så dålig att den ofta avråds från att användas. Anledningen till att man ofta tar upp den ändå är främst för att det är ett illustrativt exempel och att man utifrån samma princip kan skapa mycket bättre differentialekvationslösare som exempelvis Verlet integration som används i mekanikdelen. 3.3 MATLAB:s ode-lösare MATLAB har egna förimplementerade ordinära differentialekvationslösare. De brukar heta något på formen odeXX där XX är två siffror som benämner vilken Runge-Kuttametod som används. Exakt vad en Runge-Kuttametod är och hur ode-lösaren är uppbyggt förväntas inte läsaren veta här. Allt vi för tillfället behöver veta är att de löser system av ordinära differentialekvationer på formen y 0 (x) = f (x, y) (7) I det här avsnittet kommer vi använda oss av funktionen ode45, vilken brukar vara en stabil och bra metod att använda i de flesta lägen. Som inparameter till funktionen ska man specificera: ode45(f, [a, b], x0) där f är givet av ekvation (7), vektorn [a, b] är intervallet där lösningen ska beräknas och x0 är initialvillkoret. Om man till exempel skulle vilja finna lösningen för differentialekvationssystemet dA = 2A − AB dt dB AB = −B dt 2 på intervallet [0, 1] med initialvillkoren A(0) = 1, B(0) = 2, skrivs detta i MATLAB som: f=@(t,y) [2*y(1)−y(1)*y(2);0.5*y(1)*y(2)−y(2)]; a=0;,b=1; x0=[1,2]; Solution = ode45(f,[a,b],x0) 3 MATEMATISK ANALYS MED MATLAB 43 Som svar får vi en strukt fylld med information om lösningen till differentialekvationen som i det här fallet heter solution. Det brukar vara mest intressant att veta vad tidsstegen och lösningen är, och för att hämta den informationen skriver man ändelsen .x och .y för tidsstegen respektive lösningen vid tidsstegen. Skriver man Solution.x kommer man alltså få en vektor med tidsstegen, tidstegen är inte alltid lika långa, och skriver man Solution.y får man en matris med lösningarna, som har samma antal kolumner som längden på tidsstegsvektorn och samma antal rader som antalet rader i ekvationssystemet den löste, i det här fallet två. Det går även att lösa differentialekvationer av högre ordningar genom att formulera differentialekvationen som ett system av ode:er. Som exempel kan vi åter igen pröva att lösa exemplet i avsnitt 3.2. Genom formuleringen i ekvation (3) och (4) kan vi i MATLAB skriva: %y(1)=x y(2)=vx y(3)=0 y(4)=vy %t=[0,10] f=@(t,y) [y(2);0;y(4);−9.82]; Solution = ode45(f,[0,10],[0,75,0,50]) 3.4 Numerisk integrallösning Vissa integraler går inte att lösa analytiskt. Då är det bra att kunna lösa dem numeriskt i t.ex. MATLAB. Nedan följer tre integralapproximationer och en kort diskussion om precisionen hos dessa. 3.4.1 Riemannsumman Med en Riemannsumma approximeras integralen av en kontinuerlig funktion genom att dela upp intervallet som ska beräknas i ett antal rektanglar vars ytor sedan summeras ihop. Antag att intervallet av intresse är [a, b], rektanglarnas bredd är h = b−a h och n är antalet rektanglar. Då har vi att integralen blir Z b f (x)dx ≈ a n−1 X f (a + ih) · h. (8) i=0 I illustrativt syfte finns en bild av ett exempel på en Riemannintegral i figur 19. Implementeringen i MATLAB är förhållandevis enkel: function I=riemann(fun,a,b,n) % interval=[a,b], n=numbers of triangles x=linspace(a,b,n+1); h=x(2)−x(1); %distance between two points y=fun(x); %function evaluation I=0; %initializing for(i=1:n) I=I+y(i)*h; %sum of each rectangle end end När man gör en numerisk metod är det mycket viktigt att tänka på dess precision. Detta kan göras med en jämförelse med en Taylorserie. Då alla steg är lika borde första steget vara talande för hela integrationsmetoden. Vi har att första 3 MATEMATISK ANALYS MED MATLAB 44 steget är lika med hf (a) enligt ekvation (8). Om vi integrerar en Taylorserie av f (a + h) får man Z h Z h f (a + t)dt = 0 f (a) + f 0 (a)t + f 00 (a) 0 t2 + ...dt = 2 h2 h3 + f 00 (a) + ... 2 6 Jämför termerna märker vi att bara den första termen överensstämmer. Därför kallar man Riemannsummor för en integrallösare med första ordningens precision. f (a)h + f 0 (a) Figur 19: En Riemannintegral där kurvan är funktionen som integreras och den turkosa ytan är ytan som tas i beaktning i den approximativa integralen. 3.4.2 Trapetsregeln Med trapetsregeln approximeras integralen av en kontinuerlig funktion genom att dela upp intervallet som ska beräknas i ett antal trapetser vars ytor sedan summeras ihop. Antag att intervallet av intresse är [a, b], trapetsernas vänster respektive högersida har höjden f (xn ) och f (xn+1 ) där f (xn ) = a+nh. Bredden hos trapetsen är h = b−a h och n är antalet trapetser. Då har vi att integralen kan approximeras som Z b f (x)dx ≈ a n−1 X i=0 (f (xn ) + f (xi+1 )) · h . 2 (9) Integrallösningen kommer grafiskt likna den i figur 20. Implementeringen i MATLAB blir här mycket likt Riemannfallet: 3 MATEMATISK ANALYS MED MATLAB 45 function I=trapets(fun,a,b,n) %interval=[a,b], n=number of trapezoids x=linspace(a,b,n+1); h=x(2)−x(1); %distance between two points y=fun(x); %function evaluation I=0; %initializing for i=1:n I=I+(y(i)+y(i+1))*h/2; end end %sum of each trapezoid Återigen undersöker vi vilken precision vår metod har. Liksom tidigare är alla steg lika, vilket borde betyda att första steget är representativt för hela integrationsmetoden. Vi har att första steget är lika med (f (a) + f (a + h)) h2 h h = (f (a) + f (a) + f 0 (a)h + f 00 (a) + ...) = 2 2 2 h3 h2 + f 00 + ... 2 4 Jämför man detta med integralen av Taylorserien av f (a + h) f (a)h + f 0(a) f (a)h + f 0 (a) h2 h3 + f 00 (a) + ... 2 6 ser man att de två första termerna överensstämmer. Därmed är denna metod en metod av andra ordningen. Figur 20: En trapetsintegral där kurvan är funktionen som integreras och den turkosa ytan är ytan som tas i beaktning i den approximativa integralen. 3 MATEMATISK ANALYS MED MATLAB 3.4.3 46 Simpsons regel Med Simpsons regel approximeras integralen av en kontinuerlig funktion genom att dela upp intervallet som ska beräknas i ett antal andragradspolynom vars integraler sedan summeras ihop. Antag att intervallet av intresse är [a, b] och att vi delar intervallet i n bitar med avstånd h ifrån varandra. Andragradspolynom skapas för varje delinterval och är givna av Pi (t) = 2f (xi ) t(t − xi+1/2 ) (xi+1/2 − t)(xi+1 − t) t(xi+1 − t) +4f (xi+1/2 ) +2f (xi+1 ) h2 h2 h2 där xi = a + hi. Polynomet ser kanske avskräckande ut men grundtanken är helt enkelt att polynomet ska överensstämma med funktionen i början, i mitten och i slutet av delintervallet, d.v.s. Pi (xi ) = f (xi ), Pi (xi+1/2 ) = f (xi+1/2 ), Pi (xi+1 ) = f (xi+1 ) För att visualisera hur detta ser ut kan ni ta hjälp av figur 21. Integrerar man polynomen Pi får man att den approximativa integralen blir Z b f (x)dx ≈ a n−1 X i=0 h f (hi ) + 4f (hi+1/2 ) + f (hi+1 ) 6 Implementeringen i MATLAB blir som följer: function I=simpson(fun,a,b,n) %interval=[a,b], n=number of polynomials x=linspace(a,b,n+1); h=x(2)−x(1); %distance between two points y=fun(x); %function evaluation yhalf=fun(x+h/2); %function evaluation at half time step I=0; %initializing for i=1:n I=I+(y(i)+4*yhalf(i)+y(i+1))*h/6; %sum of each polynomial end end Då det är relativt krävande att motivera, påstår vi utan motivering att Simpsons regel är en metod av ordning tre. Den är med andra ord en stark och pålitlig metod att integrera med. 3 MATEMATISK ANALYS MED MATLAB 47 Figur 21: En simpsonintegral där kurvan är funktionen som integreras och den turkosa ytan är ytan som tas i beaktning i den approximativa integralen. 3.4.4 MATLAB:s numeriska integralräknare I MATLAB finns det en förbyggd funktion vid namn integral(f,a,b) där inputargumentet f är funktionen som ska integreras och a respektive b är början och slutet för intervallet som ska integreras. Som exempel kan vi pröva att se hur stor skillnaden är mellan våra numeriska integrallösare och MATLABs egna: f=@(x) x.ˆ0.5 − 0.7*x.ˆ2 + 0.35.*x.ˆ3+0.3*sin(5*x)−0.01*x.ˆ5.15; a=0; b=3; n=6; diff1=abs(riemann(f,a,b,n)−integral(f,a,b)) diff2=abs(trapets(f,a,b,n)−integral(f,a,b)) diff3=abs(simpson(f,a,b,n)−integral(f,a,b)) diff1 = 0.6746 diff2 = 0.1217 diff3 = 0.0086 Vilket ger det förväntade svaret att skillnaden mellan MATLABs egna och våra implementerade är minst för Simpson som är den vi vet borde ge bäst resultat. 3 MATEMATISK ANALYS MED MATLAB 3.5 48 Symbolhantering i MATLAB MATLAB förmågan att symboliskt derivera, integrera och lösa ekvationssystem. Detta är möjligt genom att använda sig av så kallade syms-uttryck. För att skapa ett syms-uttryck skriver man syms och sedan den eller de variabler man vill göra till symboler: >> syms x >> g=x+2*xˆ2 g = 2*xˆ2 + x För att derivera ett syms-uttryck används funktionen diff och för att integrera används funktionen int. Som exempel kan vi nyttja detta på vårt syms-uttryck: g = 2*xˆ2 + x >> diff(g) ans = 4*x + 1 >> int(g) ans = (xˆ2*(4*x + 3))/6 Ytterligare ett användningsområde är att man kan konvertera syms-uttryck till anonyma funktioner. Detta är möjligt tack vare funktionen matlabFunction. Om vi återanvänder vårt gamla exempel får vi att: >>g ano = matlabFunction(g) g ano = @(x)x+x.ˆ2.*2.0 >> g ano(2) ans = 10 Med hjälp av syms-uttrycken kan man dessutom lösa ekvationssystem genom funktionen solve: >>syms x >>eqn = sin(x) == cos(x); >>solve(eqn, x) ans = pi/4 4 LINJÄRA SYSTEM I MATLAB 4 49 Linjära system i MATLAB I det här kapitlet ska vi lära oss hur man med hjälp av MATLAB löser problem som förekommer i linjär algebra. Här kommer MATLAB verkligen till sin rätt då praktiskt taget alla frågeställningar till slut reduceras till matrishantering, vilket är MATLABs specialitet. 4.1 Vektorer och matriser I avsnitt 2.1 har vi lärt oss massa olika sätt att skapa vektorer och matriser, repetera gärna om du känner att det behövs. När man ska lösa linjära system i MATLAB finns det ytterligare några sätt att skapa matriser som kan vara användbara, t.ex. diag och eye. diag: Vill man skapa en diagonalmatris kan man använda funkionen diag: >> diagonal=[1,1,2,3,5,8]; >> diag(diagonal) ans = 1 0 0 0 0 0 eye: 0 1 0 0 0 0 0 0 2 0 0 0 0 0 0 3 0 0 0 0 0 0 5 0 0 0 0 0 0 8 För att skapa en diagonalmatris bestående av bara ettor är funktionen eye ett bra verktyg. Vill man exempelvis ha en matris med sex rader och ko- lumner skriver man: >> eye(6) ans = 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 Notera att detta är samma sak som identitetsmatrisen. 4.2 Matrisoperationer I den här delen ska vi utöka förmågan att hantera matriser på sätt som är vanligt förekommande i linjär algebra. Med MATLAB går det även för stora matriser enkelt att utföra operationer som annars hade tagit en smärre evighet att göra för hand. 4 LINJÄRA SYSTEM I MATLAB 4.2.1 50 Matrisaddition och subtraktion Både när man adderar och subtraherar matriser med varandra sker detta elementvis, så därför måste matriserna vara av samma dimension. Så är det både rent matematiskt och i MATLAB: >> A = [4, 5, 32; 3, 7, 14]; >> B = ones(2,3); >> A−B ans = 3 2 4 6 31 13 Om man adderar eller subtraherar matriser av olika dimensioner får man ett felmeddelande: >> A = [4, 5, 32; 3, 7, 14]; >> B = ones(3,2); >> A+B ??? Error using ==> plus Matrix dimensions must agree. Addition eller subtraktion av en skalär är detsamma som att lägga till eller ta bort skalärens värde på varje element i matrisen: A = [4, 5, 32; 3, 7, 14]; >> B = 10; >> A+B ans = 14 13 4.2.2 15 17 42 24 Matrismultiplikation För att matrismultiplikationen A · B ska vara definierad måste A vara en i × jmatris och B en j × k-matris. Produkten blir då sedan en i × k-matris. Skriver man * i MATLAB efterföljer man dessa regler: >> matrix1 = [4,5;7,4;9 0]; >> matrix2 = [3,5,11;7,9,12]; >> matrix1*matrix2 ans = 47 65 104 49 71 125 27 45 99 >> matrix2*matrix1 4 LINJÄRA SYSTEM I MATLAB 51 ans = 146 35 199 71 Uppföljs inte dessa villkor får man ett felmeddelande: >> matrix1*matrix1 %matrix1ˆ2 ??? Error using ==> mpower Inputs must be a scalar and a square matrix. Vill man att multiplikationen ska ske elementvis så kan man precisera detta genom att skriva en punkt framför: >> matrix1.*matrix1 %matrix1.ˆ2 ans = 16 49 81 4.2.3 25 16 0 Transponering av matriser i MATLAB Det finns två sätt att transponera matriser i MATLAB, antingen med hjälp av operatorn 0 eller med funktionen transpose (ekvivalent med .0 ). Vid första anblick verkar de identiska: >> RealMatrix=[2,1;3,4]; >> transpose(RealMatrix) ans = 2 1 3 4 >> RealMatrix' ans = 2 1 3 4 Men skillnader uppstår då man transponerar komplexa matriser. Transpose transponerar utan att byta ut alla inlägg till deras komplexa konjugat medan 0 operatorn gör det: ComplexMatrix = 2.0000 3.0000 1.0000 + 1.0000i 4.0000 >> transpose(ComplexMatrix) ans = 4 LINJÄRA SYSTEM I MATLAB 2.0000 1.0000 + 1.0000i 52 3.0000 4.0000 >> ComplexMatrix' ans = 2.0000 1.0000 − 1.0000i 4.2.4 3.0000 4.0000 Matrisinvers i MATLAB För att få inversen av en matris används funktionen inv: >> matrix=[1:2;3:4]; >> inv(matrix) ans = −2.0000 1.5000 1.0000 −0.5000 När det kommer till att lösa linjära system på formen Ax = b, där A är en n × n-matris, x en n × 1-vektor och b en n × 1-vektor, finns det bättre metoder än Ax = b ⇒ x = A−1 b. Att beräkna inversen är oftast mer beräkningskrävande än exempelvis gausseliminering. Ett effektivare sätt att lösa linjära system går att finna i avsnitt 4.3 Linjär algebra i MATLAB. 4.3 Linjär algebra i MATLAB För att lösa fysikaliska problem och hitta tillämpningar på linjär algebra är det mycket centralt att kunna lösa linjära system. Det här kapitlet handlar om att lösa dessa system, hur det kan underlättas och hur man utvärderar validiteten i svaren. 4.3.1 Determinanter i MATLAB För att beräkna determinanten hos en kvadratisk matris använder man funktionen det i MATLAB: >> matrix=[1:2;3:4]; >> det(matrix) ans = −2 En besläktad funktion till determinanten är cond. Dess inparametar är likt det en matris medan dess returvärde är ett mått på hur singulär, d.v.s. hur linjärt beroende, en matris är. Ju större tal desto mer linjärt beroende. Detta kan verka märkligt då en matris ur ett strikt matematiskt synsätt aldrig kan vara ett mellanting mellan linjärt beroende och oberoende. Antingen är någon eller flera rader av matrisen en multipel av en annan rad eller så är den inte det. 4 LINJÄRA SYSTEM I MATLAB 53 Anledningen till att matriser tycks kunna vara det i MATLAB är att MATLAB är ett numeriskt verktyg som approximerar och avrundar så att den absoluta matematiska sanningen ibland försvinner. Nedan följer två exempel, det första med ett fall som är linjärt oberoende och ett som antagligen inte är det: >> cond(matrix) ans = 14.9330 >> cond([1,1;1,1.00000000001]) ans = 3.9999e+011 4.3.2 Lösning av linjära system i MATLAB En oerhört vanlig problemuppställning är att man vill finna lösningen till det linjära systemet Ax = b där A är en n × n-matris och x och b är n × 1 vektorer. Att finna lösningen till ett sådant problem är enkelt i MATLAB. Genom att använda backslashoperatorn, alternativt skriva mldivide(A,B), får man värdet på vektorn x: >> A=[2,1;0,1]; >> b=[10;3]; >> x = A\b x = 3.5000 3.0000 Backslash-operatorn är en intelligent funktion som genom att först analysera matrisens egenskaper, dess dimensioner, om den är triangulär o.s.v., använder den metod som är bäst lämpad för att lösa systemet. Hela dess relativt avancerade algoritm går att finna på MathWorks hemsida. Det är dock lätt att förväxla backslash-operatorn med /. Skillnaden dem emellan är att / löser ut x när problemet är formulerat som xA = b, där A är fortfarande är en n×n-matris medan x och b är 1 × n-vektorer. Vad händer då matrisen A:s determinant är noll eller nära noll? Det som händer är att MATLAB efter bästa förmåga försöker lösa problemet ändå men utöver svaret bifogar den även ett varningsmeddelande. Nedan följer två varningsmeddelanden man säkerligen någon gång kommer träffa på: >> b=[1;2]; >> A=[1,1;1,1]; 4 LINJÄRA SYSTEM I MATLAB 54 >> A\b Warning: Matrix is singular to working precision. ans = −Inf Inf >> A=[10ˆ20,10ˆ19;9,1]; >> A\b Warning: Matrix is close to singular or badly scaled. Results may be inaccurate. RCOND = 1.000000e−021. ans = −2.0000 20.0000 I det senare meddelandet står det RCOND = 1.000000e−021. RCOND är ungefär som en invers, reciprocal, av den tidigare nämnda funktionen cond och visar att en matris är linjärt beroende om returvärdet är nära noll. Funktionen antar som mest värdet 1. Ju större ett linjärt system blir desto mer krävande blir det för datorn att lösa det. Den problematiken går ibland delvis att lösa med hjälp av funktionen sparse. Sparse formaterar om matrisen så att bara nollskilda matrisinlägg sparas. Har man en matris som innehåller få inlägg med väsentlig information, därav namnet sparse, går det att spara mycket datorkraft. Är man osäker om matrisen har tillräckligt få nollskilda inlägg kan man använda sig av funktionen issparse. För att använda sparse-funktionen skriver man helt enkelt: >> hollow=[1,0,0,0,2;0,0,3,0,2;0,0,7,0,0;0,1,0,0,1;0,0,0,2,1] hollow = 1 0 0 0 0 0 0 0 1 0 0 3 7 0 0 0 0 0 0 2 2 2 0 1 1 >> SparseHollow=sparse(hollow) SparseHollow = (1,1) (4,2) (2,3) (3,3) (5,4) (1,5) (2,5) (4,5) (5,5) 1 1 3 7 2 2 2 1 1 Returvärdet av sparse preciserar vilka index av matris som har något annat värde än noll och vad värdet vid det indexet är. Syntaxen för att lösa linjära system för sparse-matriser är exakt densamma som för vanliga matriser: 4 LINJÄRA SYSTEM I MATLAB 55 >> b=[1;6;2;0;1]; >> hollow\b ans = −4.1429 −2.5714 0.2857 −0.7857 2.5714 >> SparseHollow\b ans = −4.1429 −2.5714 0.2857 −0.7857 2.5714 Är sparse ett måste för stora matriser med få inlägg? - Kort sagt nej, men för de läsare som är skeptiskt lagda kommer nedan en demonstration att sparseformatet visst kan vara värd att använda. % creates a random 2000x2000 sparse matrix A=sprand(2000,2000,0.005); % converts the sparse matrix A to an ordinary matrix B=full(A); %creates a random 2000x1−vector c=rand(2000,1); % clocking the solution time for Ax=c tic disp('sparse matrix') A\c; toc % clocking the solution time for Bx=c tic disp('regular matrix') B\c; toc Exikverar man MATLAB-scriptet får man ett svar liknande detta sparse matrix Elapsed time is 0.165443 seconds. regular matrix Elapsed time is 0.601062 seconds. Som ni ser så är det i det här fallet går det nästan fyra gånger så fort att använda sparse-formatet som att använda sig av vanliga matriser. Noterbart är att en matris med storleken 2000 × 2000 sällan räknas till en stor matris i verkligheten så skillnaden blir ännu större för riktigt stora matriser. 4 LINJÄRA SYSTEM I MATLAB 4.3.3 56 Egenvärden och egensystem i MATLAB För att finna egenvärdena till en matris använder man i MATLAB funktionen eig. Som returvärde får man en kolumnvektor med egenvärdena: matrix = [0 1; 1 0] >> eig(matrix) ans = −1 1 Samma funktion kan utökas så att den även beräknar egenvektorer. Då är returvärdet dels en diagonalmatris med egenvärden och dels en matris där varje kolumn är en egenvektor >> [eigenvectors,eigenvalues]=eig(matrix) eigenvectors = −0.7071 0.7071 0.7071 0.7071 eigenvalues = −1 0 0 1 Egenvektorer och egenvärden har väldigt många tillämpningar bl.a. när man hanterar system av linjära differentialekvationer, löser Schrödingers ekvation, hittar stående akustiska vågor för att bara nämna några. Nu ska vi som exempel1 göra ännu en tillämpning, nämligen en förenklad populationsmodulering. Antag att det i en population av kaniner så överlever hälften första året, hälften andra året och blir som mest tre år gamla. Första året föder de inga ungar, andra året föder de 6 och tredje året föder de 8. Just nu finns det 304 kaniner mellan 0 och 1 år, 12 mellan 1 och 2 år och 12 mellan 2 och 3 år. Det är en förenklad modell där antalet individer ständigt ökar. • Hur kommer populationen se ut om ett år? • Hur stor var populationen för ett år sedan? • Hur kommer åldersfördelningen se ut när den stabiliserat sig? Vi börjar med att skriva ned villkoren på matrisform 0 6 8 304 a2 0.5 0 0 12 = b2 0 0.5 0 12 c2 1 Ett modifierat exempel från hemsidan http://homepage.ntu.edu.tw/ jryanwang/ course/Mathematics%20for%20Management%20(undergraduate%20level)/ Applications%20in%20Ch7.pdf 4 LINJÄRA SYSTEM I MATLAB 57 där a2 , b2 , c2 är antalet kaniner för individer på sina första, andra respektive tredje levnadsår året därpå. Första frågan reduceras alltså till ren matrismultiplikation som i MATLAB utförs som: >> A=[0,6,8;0.5,0,0;0,0.5,0]; >> year 1=[304,12,12]'; >> year 2=A* year 1 year 2 = 168 152 6 Året efter finns det alltså 168, 152 och 6 kaniner på första, andra respektive tredje levnadsåret. För att ta reda p hur det såg ut året innan skriver man: a0 304 0 6 8 0.5 0 0 b0 = 12 12 c0 0 0.5 0 där a0 , b0 , c0 är antalet kaniner för individer på sina första, andra respektive tredje levnadsår året före. Här gäller det alltså att lösa ett linjärt system som i MATLAB översätts till: >> year 0=A\year 1 year 0 = 24 24 20 Året före fanns det 24, 24 och 20 i de respektive ålderskategorierna. Om åldersfördelningen är stabil så kommer antalet första, andra och tredjeårskaniner an , bn , cn öka proportionerligt. Det betyder att an , bn , cn tillsammans bildar en egenvektor och att proportionalitetskonstanten är matrisens egenvärde. 0 6 8 an an 0.5 0 0 bn = λ · bn 0 0.5 0 cn cn I MATLAB är det lätt ordnat att finna egenvektorer och egenvärden med hjälp av funktionen eig. Resultatet blir två matriser där kolumnerna i matrisen eVector är systemets tre egenvektorer och diagonalelementen i matrisen eValue är motsvarande egenvärden. >> [eVector,eValue]=eig(A) eVector = −0.9684 −0.2421 −0.0605 0.8729 −0.4364 0.2182 −0.8729 0.4364 −0.2182 4 LINJÄRA SYSTEM I MATLAB 58 eValue = 2.0000 0 0 0 −1.0000 0 0 0 −1.0000 men som ni ser så spottar MATLAB inte ut ett entydigt svar direkt så det kommer krävas lite resultattolkning. Eftersom vi söker efter proportioner så vore det inte vettigt att ha negativa tal som inlägg i egenvektorn. Vid första anblick kan det då verka hopplöst eftersom alla egenvektorer har åtminstone något negativt inlägg men då glömmer man att ett tal gånger en egenvektor också är en egenvektor. Alltså kan vi multiplicera med ett negativt tal och därmed endast få positiva komponenter. För att försöka förenkla egenvektorn är det ibland användbart att använda sig av MATLABs bråkformat genom att skriva format rat. Gör vi det här får vi: >> format rat >> eVector eVector = −704/727 −176/727 −44/727 769/881 −769/1762 769/3524 −769/881 769/1762 −551/2525 Multiplicerar man egenvektorn i första kolumnen med -727/44 får man att T den egenvektorn blir (16, 4, 1) . Då har vi slutligen vårt svar. För varje tredjeårskanin går det alltså 4 andraårskaniner och 16 förstaårskaniner. Inte övertygad? - pröva då att upprepa matrismultiplikationen väldigt många gånger och sedan dividera med antalet tredjeårskaniner. 5 STATISTIK I MATLAB 5 59 Statistik i MATLAB Att göra statistik för hand är sällan roligt, särskilt inte då ens stickprov består av flera tusen observationer. I det här kapitlet ska vi lära oss hur man kan använda MATLAB som ett verktyg för att underlätta statistiska beräkningar. Här kommer vi främst ta upp hur man i MATLAB skapar slumptal, extraherar information utifrån diverse statistiska fördelningar, gör statistiska test och lite allmänt om sammanfattande statistik, d.v.s. medelvärde, varians, diagram etc. 5.1 Slumpvariabler och fördelningar i MATLAB Massor av statistisk teori bygger på fördelningar och fördelningsantaganden. Därför är det viktigt att bli bekant med vilka egenskaper de har och hur man kan simulera dem. I det här avsnittet ska vi lära oss hur MATLAB kan hjälpa oss i det avseendet. 5.1.1 Slumptalsgenerering För att skapa slumptal i MATLAB använder man sig av en familj av funktioner som alla slutar på -rnd, random förkortat. Framför skriver man en förkortning av fördelningens namn. Som inparameter skriver man i regel någon fördelningsparameter. Vill man exempelvis skapa ett slumptal ifrån normalfördelningen N (0, 1) skriver man: >> normrnd(0,1) ans = −1.3077 Hade man exekverat funktionen en gång till hade svaret antagligen blivit ett annat eftersom vi sysslar med slumptal. Som ytterligare inparameter kan man precisera hur många slumptal man vill ha och på vilken form de ska vara. Vill man ha en 1×5-vektor vars inlägg är slumptal ifrån samma fördelning som ovan skriver man: >> normrnd(0,1,1,5) ans = −0.0146 0.2171 0.1802 −0.5205 −0.2396 Det här var bara en av många funktioner som skapar slumptal men alla fungerar på mer eller mindre liknande sätt som exemplet ovan. Ett axplock av de mest förekommande slumptalen finner ni i tabell 3. 5 STATISTIK I MATLAB 60 Tabell 3: En tabell på förkortningar på slumpfördelningsfunktioner i MATLAB Fördelning pdf cdf slumptal Binomial binopdf binocdf binornd χ2 chi2pdf chi2cdf chi2rnd Exponential exppdf expcdf exprnd F fpdf fcdf frnd Normal normpdf normcdf normrnd Poisson poisspdf poisscdf poissrnd Student t tpdf tcdf trnd Uniform unifpdf unifcdf unifrnd En viktig sak att ha i åtanke är att slumptalen vi skapar inte är helt slumpmässiga utan är så kallade pseudoslumptal. De ser slumpmässiga ut och har samma egenskaper som verkliga slumptal men det går, om man vill, att upprepa dem. Talen man skapar följer en förutbestämd sekvens och med hjälp av kommandot randn går det att bestämma var någonstans i sekvensen man vill börja. Platsen i sekvensen brukar på engelska kallas för random seed. Nedan följer ett exempel hur man genom att använda slumpfröet kan återskapa samma händelse: >> randn('seed',1) >> normrnd(0,1,1,5) ans = 0.9794 −0.2656 −0.5484 −0.0963 −1.3807 −0.5484 −0.0963 −1.3807 >> randn('seed',1) >> normrnd(0,1,1,5) ans = 0.9794 −0.2656 Hade man inte angett slumpfröet innan hade raden normrnd(0,1,1,5) returnerat olika värden. 5.1.2 Slumpfördelningar För att se vilket värde som sannolikhetsfördelningen hos en fördelning antar använder man sig av en familj av funktioner som i likhet med de som skapar slumptal börjar med en förkortad version av fördelningens namn men slutar med sufixet -pdf. Pdf står för engelskans probability density function. Utöver att skriva var i x-led man vill veta sannolikhetsdensiteten måste man i inparametern, som hos rnd-funktionerna, precisera värdena på parametrarna i fördelningen. Om vi exempelvis skulle vilja veta sannolikhetsdensiteten i punkten X = 3 om X är Poissonfördelad enligt X ∼ P o(λ = 2) skriver vi: >> poisspdf(3,2) ans = 5 STATISTIK I MATLAB 61 0.1804 Analogt med pdf:en finns det funktioner som med suffixet -cdf, cumulative density function, ger den kumulativa sannolikhetsfördelningen hos en slumpfördelning. Återanvänder vi samma exempel som tidigare kan vi antingen utnyttja att pdf:en är diskret eller bara använda MATLABs inbyggda cdf-funktion: >> poisspdf(0,2)+poisspdf(1,2)+poisspdf(2,2)+poisspdf(3,2) ans = 0.8571 >> poisscdf(3,2) ans = 0.8571 Som ni säkert märker så finns det ett stort släktskap mellan funktionerna och hur de fungerar. I tabell 3 finns namnen på flera statistiska fördelningar som kan vara bra att kunna namnet på ifall ni behöver göra något av det vi gjort ovan. 5.2 Sammanfattande statistik För att kunna analysera och tolka data måste man först skapa sig en överblick, d.v.s. hitta medelvärde, spridningsmått och diagram av olika slag. I MATLAB finns en rad olika funktioner för att göra just detta som vi nu ska bli mer bekanta med. 5.2.1 Skattningar av väntevärde och spridningsmått Till att börja med läser vi in det data som vi vill analysera. Se avsnitt 2.4 om ni behöver repetera hur man läser in filer. Datat vi kommer analysera är en vektor med 60 årsmedeltemperaturer i den amerikanska staden New Haven i Fahrenheit mellan åren 1912-1971. För att läsa in datat använder vi importdata: >> temperature=importdata('NHtemp.txt'); För att beräkna medelvärdet använder man sig av funktionen mean och använder datat som inparameter: >> mean(temperature) ans = 51.1533 Medelvärdet av medelårstemperaturerna är alltså 51.1533 grader Fahrenheit eller runt 10.5 ◦ C. Med funktionen median kan vi även hitta medianen av vårt data: >> median(temperature) 5 STATISTIK I MATLAB 62 ans = 51.1000 Ett väldigt enkelt spridningsmått som är bra att använda för att få en intuitiv överblick av datat är spannet mellan det lägsta och högsta värdet. Det får man genom att använda kommandot range: >> range(temperature) ans = 6.7000 För statistiska beräkningar är dock variansen betydligt mer användbar. Den beräknas med hjälp av funktionen var: >> var(temperature) ans = 1.6012 För att beräkna standardavvikelsen kan man istället för att dra roten ur variansen skriva std som står för standard deviation: >> std(temperature) ans = 1.2654 Alla räkneoperationer vi gjort ovan går även att göra på matriser. Då ser MATLAB varje kolumn i matrisen som en separat serie mätvärden: >> A = [1 2 3; 4 5 6; 7 8 9] A = 1 2 4 5 7 8 >> range(A) 3 6 9 ans = 6 6 6 5 6 >> mean(A) ans = 4 Ofta arbetar man med ofullständigt data, vissa mätpunkter kanske råkar glömmas bort eller så har de antagit så konstiga värden att de måste försummas. Då brukar man i MATLAB ersätta de inläggen med NaN, not a number. Problematiken 5 STATISTIK I MATLAB 63 som då uppstår är att man då inte får väldefinierade svar ifall man beräknar saker som medelvärde eller varians. Om man exempelvis byter ut några inlägg i matrisen A från kodexemplet ovan med några NaN får man: >> A(2,1:2:3)=[NaN,NaN] A = NaN 4 7 2 5 8 NaN 6 9 5 NaN >> mean(A) ans = NaN Till vår räddning kommer funktionerna nanmean, nanmedian, nanstd och nanvar som utelämnar NaN:arna och sedan beräknar respektive kvantitet. >> nanmean(A) ans = 5.5000 5.2.2 5.0000 7.5000 Statistiska plottar Ett mycket vanligt, enkelt och bra sätt att visualisera statistisk data är att göra ett histogram. I det ändamålet används funktionen hist. Om vi återigen använder oss av temperaturdatat i avsnitt 5.2.1 och skriver hist(temperature,5) får vi en bild enligt figur 22, där siffran 5 representerar antalet staplar i den högra bilden.: 5 STATISTIK I MATLAB 64 18 35 16 30 14 25 12 10 20 8 15 6 10 4 5 2 0 46 48 50 52 54 0 46 48 50 52 54 Figur 22: Till vänster: automatiskt skapat histogram d.v.s. det histogram som erhålls av hist(temperature). Till höger: histogram där man specificerat fem staplar genom att skriva hist(temperature,5). Utöver att ge en överblick ger histogrammet även en hint om vilken fördelning stickprovet har. Är histogrammet klockformat så är datat normalfördelat. Är man osäker på om formen på histogrammet är klockformat eller inte går det med funktionen histfit till och med att få en normalfördelningskurva utritad i histogrammet, se figur 23. 5 STATISTIK I MATLAB 65 20 15 10 5 0 47 48 49 50 51 52 53 54 55 Figur 23: Histogram med utritad normalfördelning Det finns dock några svagheter med histogram. För det första är de mycket godtyckliga då antalet staplar och deras indelningar definieras av histogrammets skapare och för det andra går det bara att påvisa normalfördelning. En plot som är bättre på båda sakerna är den så kallade kvantil-kvantilplotten. Kvantilkvantilplotten, som namnet antyder, går ut på att man ritar ut kvantilerna hos två stickprov på x- respektive y-axeln och ser om de följer en rät linje. Om de gör det är formen d.v.s. fördelningen hos de båda fördelningarna lika oavsett om stickproven har olika storlek, skalor eller är förskjutna åt något håll. Eftersom vi har med slumptal att göra så kommer vi naturligtvis aldrig få att punkterna precis bildar en rät linje men så länge punkterna är sånär linjära bedöms fördelningarna som likartade. För att göra en kvantil-kvantilplot använder man funktionen qqplot. Om man inte specificerar två stickprov kommer MATLAB göra en kvantil-kvantilplot mot normalfördelningen. För att se hur en kvantilkvantilplot ser ut då fördelningsantagandet faktiskt stämmer kan vi pröva att stoppa in normalfördelat data i qqplot-funktionen, resultatet illustreras i figur 24. >> qqplot(normrnd(10,4,1,100)) %my=10 standard deviation=4 %system size=1x100−vektor 5 STATISTIK I MATLAB 66 QQ Plot of Sample Data versus Standard Normal Quantiles of Input Sample 20 15 10 5 0 −3 −2 −1 0 1 2 3 Standard Normal Quantiles Figur 24: Kvantil-kvantilplot av två stickprov tillhörande normalfördelningarna X ∼ N (0, 1) och Y ∼ N (10, 2) . Omvänt kan vi pröva att undersöka två stickprov som inte är lika fördelade, till exempel exponential och student t fördelade observationer: >> qqplot(trnd(5,1,100),exprnd(5,1,100)) %Student t: degree of freedom=5, system size=1x100−vektor %Exponential: lambda=5, system size=1x100−vektor Resultatet visar tydligt att stickproven inte är lika fördelade se figur 25. 5 STATISTIK I MATLAB 67 Y Quantiles 20 10 0 −10 −4 −3 −2 −1 0 1 2 3 4 X Quantiles Figur 25: Kvantil-kvantilplot av ett stickprov tillhörande exponentialfördelningen X ∼ exp(λ = 5) och ett tillhörande student t fördelningen Y ∼ t5 . 5.3 Statistiska test och konfidensintervall En central del av statistiken är tester och konfidensintervall. De allra flesta statistiska analyser mynnar tillslut ut i testandet av någon sorts nollhypotes. Här hade vi tänkt visa hur några endimensionella tester går till i MATLAB. 5.3.1 Normalitetstest Väldigt många statistiska test förutsätter att stickprovet vi testar är normalfördelat. Därför är det viktigt att kunna se om det antagandet är troligt. Ett sätt att göra det är med hjälp av statistiska normalitetstest. Dock är det viktigt att förstå testens natur innan man använder dem. I testen är nollhypotesen H0 att stickprovet är normalfördelat och den alternativa hypotesen H1 är att stickprovet inte är det. Om man kan förkasta H0 betyder det alltså att man motbevisat att datat är normalfördelat. Kan H0 inte motbevisas fortsätter den att gälla och det betyder att stickprovet kan vara normalfördelat. Det betyder dock inte att H0 är bevisad så var försiktig och gör alltid ett histogram eller en kvantilkvantilplot för att se en tendens till normalfördelning innan testet utförs. Det är exempelvis mycket svårt att med ett statistiskt test motbevisa normalitet när stickprovet är litet. Två användbara normalitetstest i MATLAB är kstest och jbtest. kstest: Kstest står för Kolmogorov-Smirnov test och testar om ett stickprov är standard-normalfördelat (d.v.s. ∼ N (0, 1)). 5 STATISTIK I MATLAB 68 jbtest: Jbtest står för Jarque-Bera test och testar om stickprovet antar någon form av normalfördelning. Båda funktionerna tar stickprovet som inparameter och returnerar antingen 0 om normalfördelning inte kan motsägas, eller 1 om normalfördelning kan förkastas. Preciserar man att man vill ha två returvärden får man utöver det också testets p-värde. 5.3.2 Test med normalfördelningsantagande Det första som bör göras innan alla sorter av parametriskt test är att testa fördelningsantagandet. För att se om datat är normalfördelat är det viktigt att visualisera datat man har. Det görs lätt med ett histogram eller en kvantilkvantilplot, se del 5.2.2. Ofta är det tillräckligt men för att vara säker kan man därefter göra ett normalitetstest. Efter det kan man börja testa sina nollhypoteser. Tre verktyg att göra detta med är funktionerna ttest, ttest2 och ztest. ttest: Funktionen ttest utför ett vanligt t-test på stickprovet. Specificerar man inte annat kommer MATLAB testa H0 : µ = 0. Har man två parvis observerade stickprov som input kommer MATLAB testa H0 : µ1 − µ2 = 0. Här antar man att skillnaden mellan de två stickproven är normalfördelad och antar inget om hurvida de två stickproven är normalfördelade eller ens ifrån samma fördelning. ttest2: Med ttest2 testar man för att undersöka om två normalfördelade stickprov har samma väntevärde, d.v.s. H0 : µ1 − µ2 = 0. Som default-läge kommer MATLAB anta att normalfördelningarna har samma (okända) varians. Vill man testa utan det antagandet skriver man 'unequal' som inparameter. ztest: Är variansen på stickprovets normalfördelningen känd, eller att stickprovet är så stort (> 30) att centrala gränsvärdessatsen kan användas, är det lämpligt att använda att använda z-testet. Dess inparameterar är stickprovet, väntevärdet man vill testa och variansen stickprovet har. Gemensamt för alla tre test ovan är att man kan skriva [H,P,CI,stats]=test(...). Då får man utöver ett eventuellt förkastande av nollhypotesen, p-värdet, ett konfidensintervall och en strukt med värdet på teststatistiken, antalet frihetsgrader och standardavvikelsen. 5.3.3 Exempel Det har gjorts ett experiment där man skulle testa om det spelar någon roll vilken hand man använder när man utför enklare uppgifter. 25 högerhänta studenter fick i uppgift att vira en viss mängd snöre runt ett rör som var fastmonterat i väggen. Första försöket skulle de bara använda högerhanden och i det andra försöket bara vänstra. Vår uppgift är att se om det är någon signifikant skillnad mellan vilken hand som används, det vill säga testa nollhypotesen H0 : µh − µv = 0. I det här exemplet är datat redan inläst som en matris där första kolumnen är tiden det tog att bli klar med högerhanden och andra kolumnen är tiden det tog för vänsterhanden. 5 STATISTIK I MATLAB 69 >> disp(experiment) 113 137 105 105 130 133 101 108 138 115 118 170 87 103 116 145 75 78 96 107 122 84 103 148 116 147 107 87 118 166 103 146 111 123 104 135 111 112 89 93 78 76 100 116 89 78 85 101 88 123 Det första vi gör är att se om stickprovet är normalfördelat så vi kan använda oss av ett parat t-test. Till att börja med så gör vi en kvantil-kvantilplot, resultatet ser vi i figur 26. >> qqplot(experiment) 5 STATISTIK I MATLAB 70 QQ Plot of Sample Data versus Standard Normal 180 Quantiles of Input Sample 160 140 120 100 80 60 40 −2.5 −2 −1.5 −1 −0.5 0 0.5 1 1.5 2 2.5 Standard Normal Quantiles Figur 26: Kvantil-kvantilplot mot standard-normalfördelning. Grön är tiden när vänsterhanden används och blå är tiden när högerhanden används. Både tiden för vänster- och högerhanden verkar normalfördelade. För att vara riktigt säkra gör vi även ett normalitetstest: >> jbtest(experiment(:,1)) ans = 0 >> jbtest(experiment(:,2)) ans = 0 Nollhypotesen att datat är normalfördelat går alltså inte förkasta. Nu när vi är relativt säkra på att normalfördelning gäller kan vi göra ett parat t-test. Vi antar att båda stickproven har samma varians. Utöver att veta utgången H av testet vill vi också veta p-värdet P och konfidensintervallet CI samt vad medelvärdet av tidsskillnaden mellan väster- och högerhand är. >> [H,P,CI]=ttest2(experiment(:,1),experiment(:,2)) H = 1 5 STATISTIK I MATLAB 71 P = 0.0398 CI = −25.9904 −0.6496 >> mean(experiment(:,1)−experiment(:,2)) ans = −13.3200 Vi kan alltså på grund av det låga p-värdet (p < 0, 05) förkasta nollhypotesen att det inte skulle vara någon skillnad vilken hand man använder. 5.3.4 ANOVA Har man fler än två stickprov och vill testa om alla har samma väntevärde kan man använda sig av ANOVA. ANOVA står för analysis of variance och är en statistisk metod för att analysera de kvadrerade residualerna d.v.s. variansen inom och mellan stickprov för att kunna påvisa skillnader i väntevärde mellan stickprov. De antaganden man gör i den här modellen är att alla observationer är oberoende, alla observationer inom ett stickprov har samma varians och att residualerna är normalfördelade. Metoden är ganska robust och brukar ge hyfsat bra resultat även om inte alla antaganden är uppfyllda. I MATLAB görs ANOVA-test med hjälp av funktionen anova1. Som inparameter använder man sig av en matris där varje kolumn är ett stickprov och som returvärde får man ett p-värde, en tabell med underlaget för p-värdet och en boxplot. Nedan följer ett kortare exempel där resultatet illustreras i figur 27: >> y1 = [18.2, 20.1, 17.6, 16.8, 18.8, 19.7, 19.1]; >> y2 = [17.4, 18.7, 19.1, 16.4, 15.9, 18.4, 17.7]; >> y3 = [15.2, 18.8, 17.7, 16.5, 15.9, 17.1, 16.7]; >> M = [y1',y2',y3']; >> anova1(M) ans = 0.0373 5 STATISTIK I MATLAB 72 Figur 27: Boxplot av grupperna y1, y2 och y3. 5.3.5 Oparametriska test Ibland är stickprovet inte normalfördelat och ibland tillhör datat ingen känd statistisk fördelning alls. Då är det läge att använda sig av oparametriska test eftersom de inte gör något fördelningsantagande. ranksum: Funktionen jämför om två stickprov har samma medianer genom att utföra Wilcoxons ranksummeringstest. Med den här metoden går det inte att få ett konfidensintervall. kruskalwallis: Funktionen gör en oparametrisk ANOVA genom att utföra ett Kruskal-Wallistest. 5.3.6 Konfidensintervall Med hjälp av funktionen paramci går det smidigt att skapa konfidensintervall. För att funktionen ska kunna skapa ett konfidensintervall för µ måste den veta vilken fördelning datat tillhör. Det betyder att vi först måste associera datat med en specifik fördelning och i detta ändamål använder man sig av funktionen fitdist. Antag att vi vill skapa ett konfidensintervall för temperaturdatat i del 5.2.2 och antag att stickprovet är normalfördelat. För att då göra ett 95procentigt konfidensintervall skriver man: >> pd = fitdist(temperature','normal'); >> ci = paramci(pd,'alpha',0.05) ci = 50.8265 1.0726 5 STATISTIK I MATLAB 51.4802 73 1.5433 Den första kolumnen ger ett konfidensintervall för väntevärdet och den andra ger ett konfidensintervall för standardavvikelsen. Kom ihåg att ett kort konfidensintervall nödvändigtvis inte är ett bra konfidensintervall. Det viktiga är att man kan rättfärdiga antagandena som görs. 5.4 Programspråket R Som ni märkt har MATLAB ett imponerande batteri av funktioner och rutiner för att utföra statistisk analys. Trots det kan det ibland märkas att statistik inte är MATLABs huvudfokus. Bland annat är det krångligt att hantera data som innehåller information som inte kan översättas i siffror som exempelvis kön, hårfärg, ort o.s.v. Därtill är mer avancerade statistiska metoder som flerdimensionella tester och bootstrapping relativt svåra att implementera i MATLAB. Om man ska hålla på mycket med statistik kan därför programspråket R, se figur 28, vara värt att prova. Dess syntax är väldigt lik MATLABs men istället för att fokusera på matriser fokuserar R på statistik. Lite förenklat kan man säga att R är statistikens MATLAB. R är ett open source alternativ som blivit allt mer populärt såväl inom som utanför den akademiska världen och används exempelvis i flera kurser som Institutionen för Matematik och Matematisk Statistik lär ut här i Umeå. Det kan laddas ned gratis på http://www.r-project.org/ och har en användbar editor kallad R-Studio som kan laddas ned gratis på http://www.rstudio.com/. Figur 28: R:s logga 6 KLASSISK MEKANIK MED MATLAB 6 74 Klassisk mekanik med MATLAB Ett stort användningsområde för MATLAB är att utföra tunga beräkningar och simuleringar. Idag har vanliga persondatorer tillräckligt med datorkraft för att genomföra relevanta fysikaliska simuleringar. Traditionellt används datorns CPU (Central Processing Unit) för att göra beräkningar, men i och med att utvecklingen av grafikkort idag går snabbare än utvecklingen av CPU har det även blivit intressant att använda datorns grafikkort för beräkningar. 6.1 Newtons rörelseekvationer med numeriska metoder I denna del kommer vi att beskriva hur man kan använda MATLAB för att simulera ett fysikaliskt system. Mer specifikt tänkte vi visa hur man löser Newtons andra lag och med hjälp ut av den kunna beskriva ett objekts rörelse givet de krafter som verkar med objektet och initialvillkor. Detta kommer vi göra genom att använda oss av numeriska metoder. Det innebär, till skillnad från att lösa problemet analytiskt, att vi delar upp vår lösning i ett bestämt antal diskreta steg. Sedan löser vi våra fysikaliska ekvationer för alla dessa steg med någon typ av uppskattning av en derivata, integral eller vad det nu är vi vill lösa. 6.1.1 Rörelse i en dimension Vi börjar med att studera rörelse i en dimension och när vi löst det endimensionella fallet, kan vi enkelt generalisera lösningen till två eller tre dimensioner. Antag att vi har ett föremål med position x, hastighet v och acceleration a. Vi vill beräkna hur föremålets position förändras över tiden, givet att vi vet föremålets position och hastighet vid tiden t = 0. Accelerationen hos ett föremål beskrivs av Newtons andra lag F , (10) m där F är kraften som påverkar föremålet, m är föremålets massa och a är föremålets acceleration. Sådana här mindre ekvationer kan fördelaktigt representeras med anonyma funktioner i MATLAB, se avsnitt 2.3 för repetition. Accelerationen definieras som andraderivatan av positionen med avseende på tiden enligt d2 x a= 2. (11) dt Om vi känner till kroppens position och hastighet vid tiden t = 0 samt om dess acceleration, eller den totala kraften som verkar på kroppen, är en känd funktion, kan vi lösa ovanstående differentialekvation och se hur positionen x varierar med tiden. För vissa problem går ekvationen att lösa för hand, men för mer avancerade problem eller problem med många föremål som rör sig samtidigt är ofta den enda lösningen att använda sig av datorhjälpmedel och numeriska metoder. Ekvation (11) är en andra ordningens differentialekvation. Den går att skriva om som två kopplade första ordningens differentialekvationer genom att införa hastigheten v som hjälpvariabel. Problemet som ska lösas blir då ( dx(t) dt = v(t) (12) dv(t) dt = a(t) F = ma → a = 6 KLASSISK MEKANIK MED MATLAB 75 där initialvärden x(0) = x0 och v(0) = v0 är kända, och a(t) är en känd funktion. Både ekvation (11) och (12) beskriver samma fysik. Numeriskt är det ofta lättare att hantera första ordningens differentialekvationer så vi kommer använda ekvation (12) som grund för våra simuleringar. Notera att den numeriska metoden vi ska använda förutsätter att kraften F är konservativ. 2 För att lösa problemet numeriskt diskretiserar vi det. Vi börjar med att dela in tiden i diskreta punkter t0 , t1 , t2 ..., tn , tn+1 , .... Mellan varje punkt råder konstant tidsskillnad ∆t så att t0 = 0, t1 = ∆t,t2 = 2∆t, ..., tn = n∆t. För att förenkla notationen kommer vi använda beteckningen xn för x(tn ) och v n för v(tn ) o.s.v. Den numeriska metoden kommer att hitta en approximativ lösning till ekvation (12). Grundidén är att om vi vet var vi är vid tiden tn kan vi approximera var vi kommer vara vid tiden tn+1 och på så sätt stega oss framåt i tiden. Detta gör vi genom att approximera tidsderivatan, alltså en förändring av positionen med avseende på tid i en viss punkt. Vi kan t.ex. approximera förändringen i x vid tiden tn med x(tn + ∆t) − x(tn ) xn+1 − xn dx ≈ = dt ∆t ∆t (13) som är derivatans definition om vi låter ∆t → 0. Steglängden ∆t avgör hur bra approximationen blir och vi ser att om ∆t → 0 blir derivatan exakt. Men om vi tar väldigt små tidssteg kommer simuleringen att ta lång tid eftersom vi måste ta fler steg för att simulera en given total tid. Konsten är att välja ett ∆t som är litet nog för att ge en bra approximation, men samtidigt inte för litet så att simuleringen tar orimligt lång tid att köra. En något bättre metod är att använda dx x(tn + ∆t) − x(tn − ∆t) xn+1 − xn−1 ≈ = (14) dt 2∆t 2∆t som använder ett tidigare och ett senare tidssteg för att räkna ut derivatan. En mer avancerad och ännu bättre integrationsmetod heter Velocity-Verlet, och bygger på två rekursiva uppdateringsformler som används för att stega framåt i tiden. De två formlerna, en för positionen och en för hastigheten, är n+1 2 x = xn + v n ∆t + 21 an ∆t (15) v n+1 = v n + 21 an + an+1 ∆t För att demonstrera Velocity-Verlet metoden skulle vi kunna använda oss utav ett exempel med ett objekt med en massa m fastspänd i en masslös fjäder med fjäderkonstant k utan dämpning, se figur 29. Objektet släpps sedan stillastående från ett avstånd x0 från jämviktsläget. Kraften som verkar på objektet från fjädern får vi från Hookes lag k x m där x är objektets förskjutning från fjäderns jämviktsläge. F = −kx → a = − (16) 2 En konservativ kraft är en sådan kraft att arbetet den uträttar är vägoberoende och beror alltså bara på start- och slutposition. Gravitationskraft och coulumbkraft är exempel på konservativa krafter, medan friktionskraft är ett exempel på en dissipativ kraft, vilket är motsatsen till en konservativ krafter. 6 KLASSISK MEKANIK MED MATLAB 76 Figur 29: Massa m fastspänd i masslös fjäder med fjäderkonstant k. Nedan följer ett exempel på hur man kan lösa detta problem med metoden ovan: function osc(m,k,x0,dt,tmax) %Function that simulates an oscillating object attached to a spring. In %parameters: m=mass of the object, k=spring constant, x0=object's distance %from equilibrium, dt=timestep, tmax=simulation time %Calculating number of steps in simulation steps=tmax/dt; %Create vector conating each time−step t=linspace(0,tmax,steps); %Creating function to calculate force on object ac = @(x) −k*x/m; %Allocate memory x=zeros(steps,1); v=zeros(steps,1); a=zeros(steps,1); %Set initial conditions x(1)=x0; v(1)=0; a(1)=ac(x0); for i=1:(steps−1) %Update position x(i+1)=x(i)+v(i)*dt+1/2*a(i)*dtˆ2; %Update acceleration a(i+1)=ac(x(i+1)); %Update velocity v(i+1)=v(i)+1/2*(a(i)+a(i+1))*dt; end %Plot the objects distance from the equilibrium over time plot(t,x); title('Oscillating object') xlabel('Elapsed time (t)') ylabel('Distance from equilibrium(x)') end Plotten som programmet producerar med m = 10kg, k = 0.5 N/m, x0 = 0.1, dt = 0.1 s och tmax = 100 s kan ses i figur 30. 6 KLASSISK MEKANIK MED MATLAB 77 Oscillating object Distance from equilibrium (x) 0.1 5 · 10−2 0 −5 · 10−2 −0.1 0 10 20 30 40 50 60 70 80 90 100 Elapsed time (t) Figur 30: Figur producerad av MATLAB-exemplet ovan. Skulle man vilja utöka exemplet med att till exempel lägga till en dämpning i fjädern så är det bara att ändra i definitionen av kraften som verkar på objektet. En härledning av Velocity-Verlet-metoden finns i avsnitt 6.2, läs gärna igenom den. Det är bra om man vet vilka antaganden som ligger bakom en metod man använder så man inte råkar ut för överraskningar. 6.1.2 Rörelse i flera dimensioner Systemet kan utökas till fler dimensioner; rörelsen i de olika riktningarna är bara kopplade genom kraften som är positionsberoende och i övrigt är rörelsen i de olika riktningarna oberoende av varandra. För två dimensioner får vi fyra uppdateringsekvationer x, y, vx och vy , enligt n+1 x = xn + vxn ∆t + 12 anx ∆t2 n+1 2 y = y n + vyn ∆t + 21 any ∆t . (17) 1 n+1 n n n+1 vx = vx + 2 ax + ax ∆t n+1 vy = vyn + 21 any + an+1 ∆t y Mer allmänt kan vi skriva om uppdateringsrelationerna på vektorform som n+1 = rn + vn ∆t + 21 a(rn )∆t2 r , (18) n+1 v = vn + 21 a(rn ) + a(rn+1 ) ∆t där r är positionsvektorn, v är hastighetsvektorn och a(r) är en känd funktion som beräknar accelerationsvektorn utifrån en given positionsvektor. Ekvationerna ovan gäller för rörelsen av en enskild kropp, men vi kan utöka dem till 6 KLASSISK MEKANIK MED MATLAB 78 att gälla flera kroppar. Antag att vi har två kroppar med positionerna r1 och r2 som växelverkar genom accelerationen, d.v.s. a(r1 , r2 ). Då får vi följande ekvationssystem: n+1 = rn1 + v1n ∆t + 21 a1 (rn1 , rn2 )∆t2 1 rn+1 r2 = rn2 + v2n ∆t + 21 a2 (rn1 , rn2 )∆t2 . (19) n+1 n+1 n+1 1 n n n v1n+1 = v1 + 2 a1 (r1 , r2 ) + a1 (r1n+1 , r2n+1 ) ∆t v2 = v2n + 21 a2 (rn1 , rn2 ) + a2 (r1 , r2 ) ∆t Har man fler kroppar är det bara att fortsätta lägga till fler ekvationer. Det ser mycket ut när man skriver ut ekvationerna så här men när man programmerar behöver man bara skriva in ekvationerna en gång. Har man flera kroppar i sitt system löser man det med en loop se avsnitt 2.2, eller ännu hellre, ordnar datat i vektorer där man låter varje element i vektorn representera en kropp. Använder man vektoriseringen kan man med fördel använda MATLABs inbyggda vektoroperationer se avsnitt 2.1.3. Alltså är skillnaden från exemplet i en dimension att det adderas två nya ekvationer per dimension, t.ex. för två dimensioner är det bara de fyra uppdateringsformlerna i ekvation (17) som behövs användas. 6.2 Verlet-integration Vi ska nu härleda en metod för att integrera accelerationen och beräkna banan som ett föremål kommer att följa. Notera att det här kapitlet kan vara lite knepigt att förstå fullt ut och det är inte nödvändigt att förstå i detalj för att använda metoden. Det kan däremot vara bra att läsa igenom för att få lite förståelse för hur den fungerar. Vi börjar med att anta att föremålets position och hastighet vid t = 0 är kända. Vi utgår från Newtons andra lag, F (x(t)) d2 x = a(x(t)) = , dt2 m (20) där F (x(t)) är en konservativ kraft som endast beror på positionen x. Vi diskretiserar tiden och approximerar andraderivatan enligt följande: a(x) = d2 x ≈ dt2 xn+1 −xn ∆t n −x ∆t −xn−1 ∆t = xn+1 − 2xn + xn−1 = an . ∆t2 (21) Om vi löser ut xn+1 får vi xn+1 = 2xn − xn−1 + an ∆t2 (22) vilket är en fullt fungerande integrationsmetod som kan användas för att räkna ut framtida positioner xn+1 givet de två föregående positionerna xn och xn−1 samt accelerationen. Metoden har utvecklats flera gånger av olika forskare genom historien. Den kallas oftast Verlets metod efter den senaste upphovsmannen som gjorde metoden känd, men man kan även se andra namn som t.ex. Störmers metod. Verlets metod använder bara positionen och accelerationen vid en given tidpunkt för att räkna ut nästa position och använder inte den momentana hastigheten. Detta har både för och nackdelar beroende på vilken typ av problem vi vill lösa. Om vi inte behöver känna till hastigheten är metoden mycket effektiv 6 KLASSISK MEKANIK MED MATLAB 79 eftersom det går att räkna ut positionen direkt utan att gå omvägen via hastigheten. Men om vi behöver hastigheten blir det lite omständligt. Hastigheten i en given position xn går att räkna ut från skillnaden mellan två positioner som omgärdar den i tid, enligt vn = xn+1 − xn−1 , 2∆t (23) men vi ser att för att räkna ut denna hastigheten v n måste vi redan känna till nästkomande positionen xn+1 , vilket inte alltid är praktiskt. En annan sak att tänka på är att man oftast bara känner till position x0 , alltså positionen vid t = 0, så för att komma igång måste xn−1 först beräknas med hjälp av någon annan metod. För att Verlet-metoden ska fungera måste ∆t vara konstant under hela simuleringen. Ändrar man tidsteget under simuleringen kommer partikelns rörelse att bli felaktig om man inte samtidigt skalar om de andra termerna för att kompensera för förändringen. 6.2.1 Velocity-Verlet Det går att skriva om Verlets metod så att man får ut hastigheten direkt. Metoden kallas då Velocity-Verlet. Det är en populär metod som ofta används för att simulera rörelsen hos t.ex. molekyler. Vi utgår ifrån den vanliga Verletmetoden och börjar med att lösa ut xn−1 från ekvation (23). Vi får då xn−1 = xn+1 − 2v n ∆t. (24) Vi sätter in uttrycket för xn−1 i ekvation (22) vilket ger 1 xn+1 = xn + v n ∆t + an ∆t2 , 2 (25) som är en ny uppdateringsmetod för positionen som även beror på hastigheten. Men för att få en fungerande metod behöver vi även veta hur vi ska uppdatera hastigheten. Utifrån ekvation (23) kan vi sätta upp ett uttryck för v n+1 : v n+1 = xn+2 − xn . 2∆t (26) Vi kan ta fram uttryck för xn+2 och xn från ekvation (25) och sätta in dem i ekvation (26) vilket ger xn+1 + v n+1 ∆t + 12 an+1 ∆t2 − xn+1 − v n ∆t − 21 an ∆t2 n+1 v = , (27) 2∆t vilket efter förenkling blir v n+1 = v n + 1 n a + an+1 ∆t. 2 Vi har nu två uppdateringsekvationer n+1 2 x = xn + v n ∆t + 21 an ∆t 1 n+1 n n n+1 v = v + 2 a +a ∆t (28) (29) som tillsammans utgör metoden vi kallar Velocity-Verlet. Metoden är självstartande, d.v.s. vi behöver inte använda en separat metod för att beräkna xn−1 eftersom 6 KLASSISK MEKANIK MED MATLAB 80 det värdet aldrig används till skillnad från i den vanliga Verlet-metoden. Även Velocity-Verlet kräver ett konstant ∆t för att fungera korrekt. Man bör också vara medveten om att metoden bygger på antagandet att kraften är konservativ, d.v.s. att kraften går att skriva som gradienten av en potential, F = −∇V (r), där r är positionsvektorn, och V är en funktion som beskriver den potentiella energin i systemet. Slutligen kan nämnas att det finns en annan populär metod som kallas Leapfrog som också går att härleda genom att göra en omskrivning av Verletmetoden. Velocity-Verlet och Leapfrog är väldigt lika och har i princip samma egenskaper. A APPENDIX: RAPPORTSKRIVNING MED MATLAB A 81 Appendix: Rapportskrivning med Matlab Nästan alla kurser på Teknisk fysik innehåller på ett eller annat sätt laborationer och en stor del av dessa är att skriva rapporter. I detta appendix kommer vi att gå igenom de mest användbara sakerna som gör det enklare för dig att producera snygga och bra labbrapporter. A.1 Bilder i MATLAB I denna del tänkte vi utöka kunskapen från kapitel 1.6 och visa på lite fler saker som man kan göra för att förändra sina figurer. A.1.1 Plot-kommandot Till att börja med tänkte vi visa på lite inparametrar till plot-kommandot som man kan använda för att ändra utseendet på sina plottar. Färg: För att bestämma färgen på sin plot mer specifikt använder vi oss av RGB-färgvärdet där RGB står för rött, grönt och blått. RGB-färgvärdet bestämmer hur mycket av varje färg som ska emitteras och anges som ett värde mellan 0 och 1. Ett exempel som ger en blågrön nyans är: plot(x,y,'Color',[0,0.7,0.9]) Linjebredd: Bredden på linjen anges i punkter där en punkt är ungefär 0.35 mm. För att byta linjebredd på sin plott skriver man: plot(x,y,'LineWidth',0.75). Linjestil: Man kan även byta stil på linjen som man plottar, nedan listas de olika alternativ som finns. ’-’ (standard) ’- -’ ’-.’ ’:’ ’none’ (ingen linje) För att implementera detta skriver man: plot(x,y,':') Markörer: Ofta när man använder plottar är det för att visualisera mätpunkter. För att markera dess tydligt kan man använda sig av de olika alternativen som listas nedan. ’o’ ’+’ ’*’ ’.’ ’pentagram’ eller ’p’ ’square’ eller ’s’ ’∧ ’ ’v’ ’<’ ’>’ ’x’ ’hexagram’ eller ’h’ ’diamond’ eller ’d’ ’none’ För att implementera detta skriver man: plot(x,y,'+'). Skulle vi kombinera alla saker nämnda ovan skriver man plot(x,y,'x−.','LineWidth',1,'Color',[0,0.7,0.9]) och vi få något som ser ut så som figur 31. A APPENDIX: RAPPORTSKRIVNING MED MATLAB 82 2-D Line Plot 1 sin(x) 0.5 0 −0.5 −1 0 2 4 6 8 10 x Figur 31: Sinuskurva skapad i MATLAB Det finns även möjlighet att ändra kantfärgen, fyllningsfärg och storleken på markörerna vilket görs genom att lägga till t.ex: 'MarkerEdgeColor',[0.1,0.2,0.3] 'MarkerFaceColor',[0.5,0.5,0.5]) 'MarkerSize', 10 Färgen bestäms av samma RGB-värde som för linjefärgen och storleken bestäms i samma enhet som för linjebredden. A.1.2 Sub-plottar Ibland räcker det inte med en plot för att presentera sitt resultat utan man vill ha flera stycken. För att fortfarande ha alla dessa i samma figurer är subplotkommandot användbart. Subplot(m,n,p) delar in figuren i rutnät med m × n platser där varje plats innehåller en plot. p specificerar platsen i rutnätet som är radnumrerat, det vill säga plats 1 är rad 1 kolumn 1, plats 2 är rad 1 kolumn 2 o.s.v. Vi skulle exempelvis kunna skriva: subplot(2,2,1) plot(x,cos); title('Cos(x)') subplot(2,2,2) plot(x,sin) title('Sin(x)') subplot(2,2,3) plot(x,e) title('eˆx') subplot(2,2,4) A APPENDIX: RAPPORTSKRIVNING MED MATLAB 83 plot(x,ln) title('ln(x)') matlab2tikz('sub.tikz') Den resulterande figuren ses i figur 32. Cos(x) Sin(x) 1 1 0.5 0.5 0 0 −0.5 −0.5 −1 0 2 4 6 e ·104 8 10 −1 0 2 4 6 ln(x) 8 10 0 2 4 8 10 x 4 2 2 0 1 −2 0 0 2 4 6 8 10 −4 6 Figur 32: Figur med fyra sub-plottar A.1.3 Symboler När man skriver titlar, axelnamn m.m. är det ibland nödvändigt att använda olika symboler t.ex. grekiska bokstäver eller matematiska tecken. Nedan, i tabell 4, listas symboler som man kan finna användbara där sekvensen är den del som man lägger till i strängen för att skapa symbolen. A APPENDIX: RAPPORTSKRIVNING MED MATLAB Tabell Symbol α β γ δ ζ η θ ι κ λ µ ν ξ π ρ σ τ υ φ χ qψ ω 84 4: Matematiska symboler och hur man skriver dem i MATLAB. Sekvens Symbol Sekvens Symbol Sekvens \alpha ∗ \ast ∝ \propto \beta ≡ \equiv ∂ \partial \gamma ⊃ \supset • \bullet R \delta \int ÷ \div \epsilon ⊥ \perp ℵ \aleph \zeta ∧ \wedge ⇐ \Leftarrow \eta ∨ \vee ⇒ \Rightarrow \theta h \langle ∇ \nabla \iota i \rangle ∃ \exists \kappa ⊆ \subseteq ∀ \forall \lambda ∈ \in ± \pm \mu · \cdot ... \ldots \nu 6= \neq | \mid √ \xi \surd 0 \prime ∼ \pi \cong < \Re = \rho ≈ \approx ← \leftarrow \sigma ∼ \sim → \rightarrow \tau ≤ \leq ↔ \leftrightarrow \upsilon ≥ \req ↑ \uparrow \phi ∞ \infty ↓ \downarrow \chi ℘ \wp × \times \psi \oslash Nedsänkttext Nedsänkt {text} \omega ◦ \circ Upphöjdtext Upphöjd∧ {text} Om man söker den versala varianten av en grekisk bokstav byter man första bokstaven i sekvensen till en versal. Om man söker en annan variant än den som finns tabulerad kan man lägga till prefixet var till sekvensen t.ex. \vartheta ger ϑ. A.2 MATLAB med andra program Nu är det så att MATLAB inte är den enda användbara mjukvaran som man använder sig av på Teknisk fysik. I denna del nämner vi några andra användbara program och hur man kan integrera dessa med MATLAB. A.2.1 MATLAB2tikZ När man väl har fått sin plot bra i MATLAB och vill överföra den till sin rapport kan det ibland hända att det inte blir som man vill. Om man skriver sin rapport i LaTeX så kan man undvika många av de här problemen genom att använda scriptet MATLAB2tikZ som konverterar MATLAB-figurer till tikZ-filer. TikZ är ett paket till latex som hjälper till att skapa sofistikerad grafik. För att göra detta måste man först ladda ner scriptet MATALAB2tikZ från mathworks.com. När nedladdningen är klar lägger man M-filerna där MATLAB hitta dem. För att sedan generera den tikz-fil som ska in i rapporten anropar man matlab2tikz('MyFile.tikz') efter plot-kommandot. Det borde nu ha skapats en fil med namnet M yF ile.tikz. A APPENDIX: RAPPORTSKRIVNING MED MATLAB 85 För att lägga till bilden i rapporten måste man först lägga till paketet \usepackage{pgfplots} överst i LaTeX-filen. Sedan lägger man tikz-filen i samma mapp som sitt rapportdokument och lägger in bilden i rapporten genom att skriva \input{myfile.tikz} där man vill ha den. En stor fördel med denna metod är att man nu kan gå in i skapade tikzfiler med hjälp av en vanlig textredigerare, till exempel Notepad i Windows och ändra utseendet på sin plot. Man kan t.ex. ändra storlek, namn och storlek på axlar, namn på titlar, färger m.m. Det finns mycket information om vad som går att göra med tikz-filen på mathworks egna hemsida eller olika forum. Om man skulle vilja addera till exempel figurtexter kan man lägga till figurmiljön som vanligt. Det skulle kunna se ut såhär: \begin{figure} \centering \input{MyFile.tikz} \caption{Sinuskurva} \label{Sinus} \end{figure} A.2.2 MATLAB med COMSOL COMSOL Multiphysics är en mjukvara som använder sig av numeriska metoder för att modellera och simulera olika fysikaliska problem. COMSOL kan användas för att beräkna problem i t.ex. flödesmekanik, hållfasthetslära eller elektromagnetism. Dessutom kan COMSOL koppla ihop modeller från olika områden vilket gör den väldigt användbar. MATLAB och COMSOL kan integreras väldigt väl med hjälp av LiveLink for MATLAB som förlänger COMSOLs gränssnitt till MATLAB. Med detta kan man skapa modeller med hjälp av script i MATLAB eller analysera resultat i MATLAB m.m. Vill man veta mer om detta hänvisar vi till Google. Istället tänkte vi ge ett kort exempel på hur man kan föra över data från COMSOL till MATLAB på smidigt sätt. Säg att man har gjort sin modell i COMSOL, fått den data man vill ha i tabell-format och exporterat den till en txt-fil. För att öppna den i MATLAB sparar man txt-filen där MATLAB kan hitta den och importer den genom att skriva ComsolImport=importdata('myFile.txt'). Tillbaka får man en strukt innehållande två delar; data och textdata. I data finns en matris med alla värden som man fått fram i Comsol. Dessa värden är uppradade i kolumnvektorer och i textdata finns information om filen bl.a. namnen på de olika variablerna. Om vi t.ex. har räknat värdet för den beroende variabel y och för värdet på den oberoende variabel x kan vi skriva: ComsolImport=importdata('myFile.txt') data=ComsolImport.data x=data(:,1) y=data(:,2) A APPENDIX: RAPPORTSKRIVNING MED MATLAB A.3 86 Feluppskattningar Vi har tidigare nämnt, i del 1.7, en metod för att göra en regressionsanalys och tänkte här beskriva ett annat sätt som gör att man enkelt kan plocka ut konfidensintervall för de koefficienter som analysen ger. Funktionen som vi tänkte ta upp i denna del är fit(x,y,fitType). Här står x för den oberoende variabeln, y för den beroende variabeln och fitType bestämmer vilken typ av regression man vill göra. För att göra en polynomisk anpassning skriver man polyn där n är ordningen på sitt polynom. Det går också att använda denna funktionen för att anpassa en polynomisk yta, då måste den oberoende variabeln vara en 2 × k-matris där k är antalet mätvärden och man skriver 'polynm' där n är ordningen på den första variabeln och m ordningen på den andra. Givet att vi har mätpunkter i F beroende på x kan vi skriva som nedan för att göra vår regressionsanalys: x=0:0.05:0.45; F=[0 0.4 0.87 1.35 1.82 2.29 2.78 3.24 3.7 4.19]; [fitobject gof]=fit(x',F','poly1') Den utskrift vi får är de uppskattade koefficienterna till linjäranpassningen med ett 95%-konfidensintervall. Om man önskar att få en annan procentsats på konfidensintervallet för koefficienterna kan man skriva confint(fitobject,1−a) där a är det konfidensintervall man vill ha. Om vi exempelvis skulle vilja veta de koefficienterna ovan med ett 99% konfidensintervall skriver man: confint(fitobject, 0.01) ’gof’-variabeln eller goodness-of-fit innehåller data på hur bra modellen är anpassad till mätvärdena, här hittar man bland annat R2 -värdet och R2 − adjust. Skulle man nu vilja plotta sina mätvärden med sin regression skriver man plot(fitobject,x',F'). B APPENDIX: VANLIGA FEL B 87 Appendix: Vanliga fel I detta appendix kommer vi ta upp vanliga fel, dess felmeddelanden och hur man undersöker dem för att förstå vad man gjort fel. En del fel är vanligare och kommer gå fortare att känna igen medan en del andra bara händer ibland och därför inte är lika lätta att identifiera i början. Vi kommer även gå igenom hur man på ett enkelt sätt felsöker sin kod. Detta kommer vi göra både genom att manuellt felsöka koden och genom att använda MATLABs inbyggda debuggerverktyg. B.1 Dimensionsfel Bland de vanligaste felen man får i MATLAB är dimensionsfel eftersom man så ofta arbetar med vektorer och matriser. Dimensionsfel kallas de fel som uppstår då man försöker utföra operationer som inte är genomförbara med de dimensioner som man arbetar med. Om man försöker multiplicera en vektor med en matris elementvis får man detta felmeddelande: >> A = [1 2 3 ; 4 5 6 ; 7 8 9]; >> b = [1 2 3]; >> A.*b Error using .* Matrix dimensions must agree. Detta beror på att vektorn och matrisen inte har samma dimensioner. För att multiplicera en vektor med en matris måste vi använda oss utav matrismultiplikation, men ibland blir även det fel: >> A*b Error using * Inner matrix dimensions must agree. Den här gången beror felet på att radvektor inte går att multiplicera med en matris. Om vi istället tar kolumnvektorn genom att transponera vektorn fungerar det: >> A*b' ans = 14 32 50 Ett liknande fel uppstår om man försöker upphöja en vektor med 2 och glömmer punkten som representerar elementvisa operationer: >> bˆ2 Error using ˆ Inputs must be a scalar and a square matrix. To compute elementwise POWER, use POWER (.ˆ) instead. B APPENDIX: VANLIGA FEL 88 Eftersom detta är ett relativt vanligt fel och inte kan bero på så många olika anledningar får man av MATLAB ett exempel på vad man förmodligen menade, att använda den elementvisa operationen. Man kan även få dimensionsfel när man försöker tilldela två element till en position: >> A(1,2) = [2 3] Subscripted assignment dimension mismatch. Om man försöker plotta två vektorer med olika längd mot varandra får man följande fel: >> f = [13 16 6 15]; >> time = [1 2 3]; >> plot(time, f) Error using plot Vectors must be the same lengths. Även här är det ganska tydligt vad man gjort fel. Det som kan vara svårt är att veta varför vektorerna man skapat har olika längder om man t.ex. skapar vektorerna i loopar. När det gäller dimensionsfel, oberoende av vilken sort, är den enklaste lösningen att skriva ut längden på vektorn och/eller storleken på matrisen med length och size för att på så vis hitta varför operationen inte fungerade. B.2 Indexeringsfel Efter dimensionsfel är nog indexering det man oftast gör fel på. Med dessa fel är det en aningen lättare att inse vart problemet uppstår. Det man har gjort då man får fel på grund utav indexeringen är att man antingen försöker komma åt index som inte finns eller som är ogiltiga. Exempel på ogiltiga index är negativa index eller tal som inte är heltal: >> A(0.5) >> A(−1) Subscript indices must either be real positive integers or logicals. Felmeddelandet som man får säger tydligt varför man inte får använda 0.5 eller -1 som index. 0 är i MATLAB inte ett giltigt index eftersom det första indexet i MATLABs vektorer är 1. Detta kanske är relativt enkelt att komma ihåg då man skapar vektorer i början men problem kan uppstå när man i en loop t.ex. vill spara undan tiden i sekunder från 0 till 10: for i=0:10 time(i) = i; end Attempted to access (0); index must be a positive integer or logical. I detta fall måste man istället spara tiden 0 i index 1, vilket fungerar med raden: time(i+1) = i; B APPENDIX: VANLIGA FEL 89 Andra indexfel man gör är att man försöker komma åt index som inte existerar i sin vektor. T.ex. om man har en vektor som består av 5 element men försöker komma åt element 6: >> x=1:5; >> x(6) Index exceeds matrix dimensions. for i= 1:6 x(i)*2; end Attempted to access x(6); index out of bounds because numel(x)=1. I det här fallet får man två olika felmeddelanden beroende på om man vill skriva ut eller använda elementet. Innebörden är dock den samma, det finns inget värde på plats 6 i en vektor med längden 5. När det gäller indexeringsfel får man fundera på varför man försöker komma åt ett element som inte existerar. Kanske har man sparat sina vektorer på ett annat sätt än vad man trodde. Även här är det bra med length och size för att undersöka problemet. B.3 Syntaxfel Syntaxfel är ofta fel som inte ens behöver förklaras men för ordningens skull tar vi upp några sådana också. Om man skriver ett uttryck som MATLAB inte förstår kommer MATLAB klaga: >> A = 1+3+ | Error: Expression or statement is incomplete or incorrect. I detta fall har vi ett inkomplett uttryck, antingen är det ett plustecken för mycket eller ett tal för lite. Andra fel kan vara tecken som inte existerar i MATLAB: >> ` | Error: The input character is not valid in MATLAB statements or expressions. >> A(1::, 2) | Error: Unexpected MATLAB operator. Ovanstående exempel innebär samma fel men ger olika felmeddelanden beroende på om det är tecken som MATLAB kan hantera i huvudtaget eller inte. I det senare exemplet klagar MATLAB på grund av det dubbla kolon-tecknet. Ett kolon är en giltig MATLAB-operation, men inte två efter varandra. Vanliga fel som uppstår då man har lite bråttom är att man glömmer parenteser eller använder dem där det inte fungerar: >> f = (3*4)+5); B APPENDIX: VANLIGA FEL 90 Error: Expression or statement is incorrect−−possibly unbalanced (, {, or [. >> A(1)) | Error: Unbalanced or misused parentheses or brackets. Ett lite mer oklart syntaxfel man kan göra är att t.ex. använda bara ett likhetstecken då man vill ha två: >> a = 2; >> if a = 3 if a = 3 | Error: The expression to the left of the equals sign is not a valid target for an assignment. MATLAB tolkar detta som att man försöker tilldela a värdet 3 då man egentligen vill undersöka om a är lika med 3. När det gäller syntaxfel är det generellt sett bara att ändra det som står i felmeddelandet så kommer det lösa sig. Eventuellt att det kan vara svårt att räkna parenteserna om det är många men då gäller det bara att vara noggrann. Ett tips kan vara att först skriva ut parenteserna och sedan det som ska vara innanför. B.4 Övriga fel Här nedan har vi samlat en del övriga fel som kan uppstå. Övriga fel i MATLAB kan vara allt från att anropa felstavade funktioner till att försöka läsa in filer som ligger i fel mapp. Ett vanligt fel kan vara att man glömmer att MATLAB skiljer på stora och små bokstäver: A = [1 4 53 3]; T = [1 2 3 4]; A(t) Undefined function or variable 't'. Funktionen t anses odefinierad eftersom den inte existerar. På samma sätt blir det fel om man försöker anropa en funktion med felaktigt namn: >> A = [1 2; 1 4] >> eigenvalue = eigen(samplemat); Undefined command/function 'eigen' for input arguments of type 'double'. I detta fall är det eig som man vill använda, det finns ingen inbyggd MATLABfunktion som heter eigen. Andra fel man kan göra vid funktionsanrop är att ange för få eller för många inparametrar: >> A = plus(1,2,3) Error using + Too many input arguments. >> A = ode45() Error using ode45 Not enough input arguments. See ODE45. B APPENDIX: VANLIGA FEL 91 I det övre fallet vill plus ha två inparametrar för att utföra addition och i det nedre exemplet vill ODE45 ha minst en inparameter. Man kan även försöka komma åt fler returvärden än vad funktionen returnerar: >> A = [1,2;3,4] >> [V,D,Mistake] = eig(A); Error using ==> eig Too many output arguments. Funktionen eig har endast två returvärden och frågar man efter tre får man ovanstående felmeddelande. Andra fel man kan göra är att döpa en funktionsfil till något annat än funktionsnamnet eller försöka läsa in en fil som inte ligger i samma mapp. Om man döper en funktionsfil till ett annat namn än funktionsnamnet kan man få felmeddelandet: >> function name(3) Undefined function 'function name' for input arguments of type 'double'. Felmeddelandet säger att funktionen function name inte är definierad trots att vi precis skapat den, dock är den sparad i en M-fil som heter file.m. Problemet är att MATLAB inte förstår att vi skapat en funktion med det namnet utan väntar på ett anrop till file.m. Lösningen är att ändra filnamnet till function name. Om man försöker läsa in en fil som ligger i en annan mapp får man felmeddelandet: >> A = load('numbers.txt') Error using load Unable to read file numbers.txt: No such file or directory. Lösningen är att antingen flytta M-filen eller textfilen till den mapp där man vill spara sitt material. B.5 Felsökning i MATLAB De vanliga felen som vi har listat här ovan går ofta att lösa genom att ändra något på samma rad som MATLAB klagar på. Andra fel, de så kallade logiska felen som gör att programmet inte gör det man vill, måste man själv hitta. Dessa fel är både svåra att hitta och förstå att man har om man inte kontinuerligt testar sin kod. När man förstått att man har fel i sin kod och vill undersöka vart problemen uppstår kan man antingen manuellt felsöka koden eller ta hjälp av MATLABs inbyggda debugger. B.5.1 Felsökning i MATLAB Att felsöka sin kod är något man lär sig allt eftersom man gör det. Det handlar om vana och att veta vilka problem som kan uppstå. Det underlättar också om man vet själv vad man ofta gör fel på och kan titta igenom sådana kodstycken tidigt. I felsökning finns det inget som är rätt och fel utan alla gör olika. Här kommer vi ge lite generella tips på hur man ska gå till väga för att hitta felen. När man felsöker sin kod är det bra att börja med att översiktligt kolla igenom koden för att se hur man byggt upp sitt program och om man ser några B APPENDIX: VANLIGA FEL 92 självklara fel, något kanske kommer i fel ordning t.ex. Nästa steg är att systematiskt gå igenom koden, först lite mer översiktligt och om man inte hittar felet första vändan, köra en till sväng då man är lite mer noggrann. Det viktigaste är att man är systematisk så man vet att när man gått igenom en sekvens i sin kod så fungerar den som det är tänkt. Om man inte hittar felen direkt kan man använda sig utav kommandot keyboard. För att använda detta kommando skriver man in det i sin kod och när man kör koden kommer MATLAB att stanna vid raden där man skrivit keyboard. Det man kan göra är då att i kommandofönstret undersöka vad variablerna för tillfället är: B = zeros(4,1); for i=1:3 B(i,1) = i; keyboard end K>> B B = 1 0 0 0 I ovanstående exempel avbryts körningen efter ett varv i loopen. Vektorn B har alltså bara hunnit ändra sitt första värde. K:et framför kommandopromten indikerar att man har aktiverat keyboard-läget. För att fortsätta i filen skriver man return i kommandofönstret: K>> return B = 1 2 0 0 För att avsluta felsökningen kan man skriva dbquit och då slutar även M-filen att köras. Det här sättet att felsöka sina filer är väldigt användbart då man kan följa hur variablerna förändras. Man kan även använda sig av kommandot who för att se vilka variabler som existerar för tillfället. Under fliken Editor finns det även debugger-verktyg som fungerar på liknande sätt som förklarats ovan. Skillnaden är att man istället sätter ut breakpoints i sin kod genom att klicka till höger om radsnumret i M-filen. Därefter kan man använda sig av knapparna under fliken Editor, så som contiue, step, step in, run to cursor och quit debugging. Vilket av alternativet man väljer att använda är upp till användaren. Det enklaste sättet att motverka logiska fel är såklart att koda på ett förebyggnade sätt genom att t.ex. testa koden kontinuerligt. Fler tips finns i appendix C Tips och Trix. C APPENDIX: TIPS OCH TRIX C 93 Appendix: Tips och Trix Desto mer man jobbar med MATLAB desto bättre blir man såklart, men i början kan det vara bra med lite tips och trix. I det här kapitlet kommer vi först lista en massa småtips som man borde försöka att alltid följa. I början av sin MATLAB-karriär kan det vara bra att kolla igenom den med jämna mellanrum så man ser att man fått med allt. Men ni kommer att inse efter ett tag att det mesta kommer naturligt då man känner sig mer bekväm med MATLAB, då behöver ni inte småtipsen längre. De andra delkapitlen i detta appendix handlar om sådant man kanske inte behöver kunna men som kan vara bra att veta att det finns utifall man känner att man vill använda det. Så ett tips är att läsa igenom dessa delkapitel och komma tillbaka när det känns som att det finns en användning av något. För att det som står i dessa kapitel ska vara användbara krävs det att läsaren själv testar kommandona och lär känna dess för- och nackdelar. Det som står här är endast inspiration, använd help och MathWorks för mer utförlig information. C.1 Småtips att alltid följa Här nedan kommer en massa småtips att listas som är bra att alltid ha för vana att använda sig utav. Indentering: MATLAB kräver inte att man indenterar sin kod för att den ska kunna köras. Men det ger en bättre översikt, gör koden enklare för andra att läsa och med en korrekt indentering är det lättare att upptäcka fel. För att indentera hela sin kod på en gång kan man om man sitter i Windows använda sig av smart indent genom att markera hela filen och trycka ctrl+I. Om detta inte fungerar borde det fungera om man under Home → Preferences ändrar till Windows Default Set under Active settings. Denna ändring kan man även göra om man sitter på andra operativsystem. En annan bra inställning man kan göra är att under Home → Preferences under Indenting klicka i rutan Apply smart indenting while typing. Det som händer då är att koden man skriver indenteras automatiskt när man skriver t.ex. loopar där det är uppenbart att nästa rad ska indenteras. Kommentarer: Kommentera alltid koden. Man glömmer fortare än vad man vill erkänna och om man vill att någon annan ska kunna läsa och förstå koden är kommentarer ett måste. I större program är det oftast mer än en programmerare som jobbar med samma arbete och då är det av största vikt att koden är kommenterad. Att veta hur mycket man ska kommentera och hur tydligt är alltid svårt innan man fått en känsla för vad som är lagom. Man kanske inte behöver kommentera varje loop t.ex. men det är bättre att kommentera för mycket än för lite om man är osäker. Man kan ha som regel att kommentera vad alla funktioner gör, dess inparametrar och returvärden, nya variabler, vad en sekvens med kod gör, o.s.v. Överallt där det inte är helt uppenbart vad som kommer att göras borde man kommentera. I MATLAB görs kommentarer med % och all efterföljande text blir grön och bortkommenterad. I Windows kan man kommentera bort ett helt stycke genom att markera texten och trycka ctrl+R. Om man vill ta bort kommenteringen C APPENDIX: TIPS OCH TRIX 94 markerar man texten, trycker ctrl+T och får tillbaka koden. Notera att detta gäller då man har angett Windows Default Set under Active settings. Spara: Detta tips kan verka självklart men det händer rätt ofta att man inte sparar sina filer med förklarande namn eller på smarta ställen. Ta för vana att alltid spara undan sina filer med förklarande namn, man vet aldrig när man kan behöva kolla upp hur man gjorde tidigare. Namnge variabler smart: Att namnge sina variabler med förklarande innebörd gör även det att det är lättare för andra att förstå koden. Vad variabeln g innehåller är väldigt svårt att förstå medan time förmodligen innehåller tiden. Att namnge sina variabler på liknande sätt genom hela koden ger även en snyggare helhet. Börja om/skriv om: Man ska inte vara rädd för att radera allt som man precis skrivit och börja om. Ofta blir man blind av att stirra på en kod som inte fungerar och då kan det vara bra att börja om helt. Även om koden fungerar kan det vara bra att skriva om vissa delar för att optimera och förbättra koden. Kan man skriva om sin kod så den inte använder massa loopar tjänar man massa datorkraft. M-filer: Att använda sig utav M-filer kan vara mycket användbart då man vill köra en funktion som har många olika inparametrar. Istället för anropa funktionen i kommandofönstret och behöva komma ihåg i vilken ordning man ska ange inparametrarna kan det vara smart med en M-fil där man sparar alla inparameterar och dess värden och sen kallar på funktionen. Om man vill ändra en inparameter kan man enkelt göra det och sen köra M-filen igen. M-filer är även användbart om man läser in data från filer för att sedan plotta resultaten. Om datafilen ändras kan man få nya plottar bara genom att köra samma M-fil igen fast med en förändrad textfil. Testa koden kontinuerligt: Ju större program man programmerar desto viktigare är det att testa sin kod kontinuerligt. När man är ovan att jobba med vektorer och matriser kan det t.ex. vara bra att hela tiden testa om de skapas på det sätt man tänkt sig. Ett vanligt fel är att man blandar ihop rader och kolumner vilket man enkelt kan undersöka med kommandot size. Efter varje ny funktion eller loop borde man också undersöka om man får det resultatet som man har tänkt sig. Att sitta med en kod på flera hundra rader och inse att slutresultatet inte stämmer gör det svårt att hitta vart man gjort fel. När MATLAB inte klagar på det man skrivit men ändå inte gör som man menar uppstår de svåraste felen. Dessa är helt omöjliga att upptäcka om man inte testar sin kod kontinuerligt. Tiden det tar att testa varje ny del i koden direkt sparar man flera tusen gånger om i jämförelse med att försöka hitta felen i efterhand. Utskrifter: För att testa sin kod, både när man vill se om den fungerar eller för att hitta fel, är det ett bra första sätt att använda sig utav utskrifter som man sedan tar bort. Utskrifterna hjälper till att visa vart en variabel ändras till C APPENDIX: TIPS OCH TRIX 95 ett värde som verkar ologiskt. Man kan sedan gå bakåt i koden för att hitta vart felet uppstår. Dölj kodstycken: Ibland när man har långa koder som man arbetar med kan det vara bra att dölja saker som man inte använder just då. T.ex. loopar som man vet fungerar. Det kan man göra genom att trycka på det lilla minustecknet till vänster om loopen så döljs allt utom den översta raden följt av punkter. För att sedan kunna se loopen igen kan man trycka på plustecknet som skapats istället för minustecknet. Om man vill dölja eller visa all kod kan man göra det genom att högerklicka någonstans i koden och sedan trycka Code folding följt av Fold all respektive Expand all. Kör koden i sektioner: Att dela upp sin kod i sektioner kan vara användbart om man vill göra samma saker flera gånger men kanske för olika funktioner eller att man vill köra bara delar av sin kod. Att dela upp sin kod i sektioner gör man genom att skriva %%. MATLAB skapar då en sektion, eller cell, i koden. För att bara köra sektionen kan man trycka på Run section under fliken Editor eller trycka ctrl+Enter. När man skapat sektioner kan man köra hela sin kod som vanligt genom att tryck Run under fliken Editor eller trycka F5. Avbryta oändliga loopar: Ibland råkar man skapa oändliga loopar, alltså loopar som aldrig kommer bli färdiga. För att avbryta en sådan körning kan man trycka crtl+C. MATLAB kommer då skriva ut Operation terminated by user during exempel (line3), om man kör en fil med namnet exempel. Allokering: Att allokera minne för sina vektorer och matriser sparar datorkraft, vilket är något som man alltid ska sträva efter att göra. Hur man allokerar finns att läsa under avsnitt 2.2. Clear all, close all: Något man alltid bör skriva överst i sina M-filer är close all och clear all, så slipper man bli förvirrad av resultat från tidigare körningar. close all stänger alla figurfönster och clear all raderar alla tidigare variabler. Att skriva detta överst i sina M-filer gör att man alltid nollställer MATLAB på samma sätt som om man hade startat om programmet. Humörhöjande: Om det går extra tungt en dag framför MATLAB är det dags att prova på nya kommandon. Testa t.ex. spy, why, load train följt av sound(y,Fs) eller varför inte testa att plotta detta: x=[−2:.001:2]; y=(sqrt(cos(x)).*cos(200*x)+sqrt(abs(x))−0.7).*(4−x.*x).ˆ0.01; plot(x,y) C.2 Publish I MATLAB finns det ett kommando som heter publish som man kan använda om man vill publicera sin kod på ett snyggt sätt, vilket kan vara användbart då man t.ex. föreläser eller ska redovisa en uppgift. Kommandot publish sparar C APPENDIX: TIPS OCH TRIX 96 kod, kommentarer och resultat som en html-fil. För att publicera sin kod kan man under fliken Publish trycka publish. Då sparas html-filen i en mapp som skapas i samma mapp som sin M-fil ligger i. En fördel med att publicera sin kod jämfört med att bara kopiera koden är att med publish skapas även figurer i den publicerade filen. Om man vill skapa en pdf istället kan man använda sig av samma kommando i kommandofönstret och lägga till valet 'pdf': publish('publishmaterial.m', 'pdf') Detta kommando kan man göra med alla sina M-filer men det finns trick som gör den sparade filen snyggare, t.ex kan man använda sig av dubbla % för att skapa rubriker. Nedan visas en exempelkod som skapats för att demonstrera publish, kopiera och testa för att se hur den publicerade filen ser ut. %% Publish MATLAB−filer % This is a M−file made in order to demonstrate the MATLAB command publish. % In order to demonstrate that a sine−function is plotted. %% Create vectors and plot it t = 0:.1:pi*4; func = sin(t); plot(t, func) hold on %% Add lines in the figure % In each iteration a new line is added into the figure and displayed % togheter with the value of k. for k =0:2 %% %A new line with the slope k. disp('k = '), disp(k) plot(t, k*t, 'r') end C.3 Multi-dimensionella matriser I avsnitt 2.1.2, om matriser, har vi tidigare nämnt att man kan skapa matriser med flera dimensioner. Detta kan man göra på samma sätt som när man skapar vanliga matriser men man anger tre dimensioner när matrisen skapas: >> temperature=zeros(3,3,3) temperature(:,:,1) = 0 0 0 0 0 0 0 0 0 temperature(:,:,2) = 0 0 0 0 0 0 0 0 0 C APPENDIX: TIPS OCH TRIX 97 temperature(:,:,3) = 0 0 0 0 0 0 0 0 0 Fler-dimensionella matriser kan t.ex. vara bra att använda då man vill spara temperaturen i ett rätblock eller spara saker i två dimensioner som ändras med tiden. I ovanstående exempel skrivs temperaturerna ut ”blad för blad”. Med tre dimensioner kan man tänka sig att det första indexet representerar x, det andra y och det tredje z. Notera att det är väldigt enkelt att det blir dimensionsfel då man försöker utföra operationer på fler-dimensionella matriser. Det gäller att vara noggrann och hålla koll på vad man gör när man arbetar med flerdimensionella matriser. C.4 Cell-arrayer Om man vill spara olika sorters data i samma datatyp kan man använda sig av cell-arrayer. Cell-arrayer innehåller antingen textsträngar, kombinationer av text och siffror eller matriser med siffror med olika längder. För att skapa cellarrayer används kommandot cell: >> C = cell(2,3) C = [] [] [] [] [] [] Med kommandot cell skapas en tom cell-array. Enkelt sett kan man se en cell-array som en matris med matriser på varje index. För att ändra värden i cell-arrayen används måsvingar: >> C(1,1:3) = {'one', 'two', 'three'}; >> C(2,1:3) = {1,2,3}; C = 'one' [ 1] 'two' [ 2] 'three' [ 3] Man kan även lägga till en ny matris på ett specifikt index: >> C(2,1) = {[1,2,3]} C = 'one' [1x3 double] 'two' [ 2] 'three' [ 3] Cell-arrayer kan vara användbara om man behöver spara olika sorters data som hör ihop. T.ex namn på personer som en textsträng tillsammans med åldern som är ett tal. C APPENDIX: TIPS OCH TRIX C.5 98 Struct Ett annat sätt, förutom cell-arrayer, att spara data på av olika typer är att använda sig av struktar. Dom fungerar på ungefär samma sätt men istället för index för att komma åt datat används namn. Säg att vi vill spara information om studenter, t.ex. vad dom heter och vilket år dom går. Då kan man skapa en strukt med kommandot struct: >> student = struct('Name', 'Kajsa', 'Year', 3) student = Name: 'Kajsa' Year: 3 >> student.Name ans = Kajsa Här har vi skapat en student med namnet Kajsa som går tredje året. Vill vi lägga till en person i vår strukt måste vi använda index: >> student(2) = struct('Name', 'Kalle', 'Year', 2) student = 1x2 struct array with fields: Name Year Nu skrivs inte namnet och årskursen ut eftersom den inte vet vems namn eller årskurs den ska skriva. Istället får man information om vilken information som lagras i strukten student, i detta fall Name och Year. Vill man skriva ut något får man igen använda index: >> student(2) ans = Name: 'Kalle' Year: 2 Man kan även välja att lägga till studenter där man inte har all information, t.ex. om man inte vet vilken klass Sara går i: >> student(3).Name = 'Sara'; >> student(3) ans = Name: 'Sara' Year: []