Föreläsning 11 12 ALGORITMER: SÖKNING, REGISTRERING, SORTERING
Seminarier: Fredagsklubben för dig som tycker att programmering är svårt (0 eller möjligen 1 poäng på delmålskontrollen) inte avsedda för dem med 2 poäng inte obligatoriska goda erfarenheter från 2016 start på fredag kl 15, därefter (från nästa vecka) fredagar kl 8
Kursadministrativt se kurssidan Labbgrupperna består (men i andra salar se kurssidan!) Kolla i utskicket att du är godkänd på labbar Matlab-komplettering (för F): idag kl 15-17 Lab 6: sköldpadda i labyrint
Sökning Uppgift: Sök upp givet element i en följd av element. Lösning: Gå igenom elementen i tur och ordning, kontrollera för varje element om det är det sökta. Avbryt om det sökta elementet påträffats. Algoritm (linjärsökning) i pseudokod: i = "platsen för det första elementet" while ("fler element kvar att söka igenom" && "elementet på plats i inte är det vi söker") i = platsen för nästa element
Linjärsökning /** Sök efter talet nbr i vektorn v. Om nbr finns returneras platsen för nbr, annars -1 */ public static int indexof(int[] v, int nbr) { int i = 0; while (i < v.length && v[i]!= nbr) { i++; Att fundera på: if (i < v.length) { return i; else { return -1; Varför måste delvillkoret i < v.length stå först? (Läs om short-circuit-evaluering, boken s. 78.) Att fundera på: Varför kan man inte använda villkoret v[i] == nbr för att avgöra om man funnit talet nbr eller ej?
Linjärsökning, snarlik lösning. Men vad är detta? /** Sök efter talet nbr i vektorn v. Om nbr finns returneras platsen för nbr, annars -1 */ public static int indexof(int[] v, int nbr) { int i = 0; while (i < v.length && v[i]!= nbr) { i++; return (i < v.length)? i : -1;
Villkorsuttryck (se kap 6.9 i boken) Värdet av det logiska uttrycket anger vilket av de båda uttrycken som ska beräknas. logiskt uttryck? uttryck 1 : uttryck 2 Beräknas om det logiska uttrycket är sant Detta kan alltid skrivas om med if/else-satser. Beräknas om det logiska uttrycket är falskt Du ska emellertid förstå uttryck av detta slag när du ser dem.
Linjärsökning: kommentar I exemplet på föregående sida förutsätter vi att vektorns alla platser används och ska sökas igenom. Vi har alltså v.length element att söka igenom: while (i < v.length && v[i]!= nbr) I motsvarande exempel i läroboken (avsnitt 8.8) har man en vektor där de bara de n första platserna i vektorn utnyttjas: while (i < n && v[i]!= nbr)
Linjärsökning, en variant till Eftersom metoden inte ska göra något annat än att returnera ett index, så kan man göra det direkt om man vill. /** Sök efter talet nbr i vektorn v. Om nbr finns returneras platsen för nbr, annars -1 */ public static int indexof(int[] v, int nbr) { for (int i = 0; i < v.length; i++) { if (v[i] == nbr) { return i; return -1;
men varning för fel! public static int indexof(int[] v, int nbr) { for (int i = 0; i < v.length; i++) { if (v[i] == nbr) { return i; else { return -1; Varför fungerar inte det här? (Vanligt fel på tentor.)
Exempel: klass för att representera en användare för passerkort User /** Skapar en ny användare med givet kortnummer och namn. */ User(int cardnbr, String name); /** Hämtar användarens kortnummer. */ int getcardnbr(); /** Hämtar användarens namn. */ String getname();
Linjärsökning bland objekt User[] users = new User[100];... // Satser för att skapa 100 User-objekt Uppgift: Sök efter användaren med kortnummer 64319 och skriv ut användarens namn. int i = 0; while (i < 100 && users[i].getcardnbr()!= 64319) { i++; if (i < 100) { System.out.println(users[i].getName());
Varför fungerar inte detta? User[] users = new User[100];... // Satser för att skapa 100 User-objekt Uppgift: Sök efter användaren med kortnummer 64319 och skriv ut användarens namn. int i = 0; while (i < 100 && users[i]!= 64319) { i++; Incompatible operand types User and int if (i < 100) { System.out.println(users[i].getName());
Andra sökalgoritmer Linjärsökning är enkelt, men inte alltid mest effektivt. I en sorterad mängd är binärsökning effektivare. Hur gör du själv när du söker upp ett tal eller ett ord i en sorterad mängd? (Kom ihåg laboration 4.)
Binärsökning: idén Sök upp talet 61 i vektorn v.? v 2 9 22 31 42 56 61 72 76 97? v 2 9 22 31 42 56 61 72 76 97 v 2 9 22 31 42 56 61 72 76 97? v 2 9 22 31 42 56 61 72 76 97!
Binärsökning: formulera algoritmen Sök upp talet 61 i vektorn v. low=0 mid=4 high=9 v 2 9 22 31 42 56 61 72 76 97 Sätt mid = (low + high) / 2 Är v[mid] == 61? Nej, 42 42 < 61, så sätt low = mid + 1 low=5 mid=7 high=9 v 2 9 22 31 42 56 61 72 76 97 Sätt mid = (low + high) / 2 Är v[mid] == 61? Nej, 72 72 > 61, så sätt high = mid 1 mid=5 low=5 high=6 v 2 9 22 31 42 56 61 72 76 97 Sätt mid = (low + high) / 2 Är v[mid] == 61? Nej, 56 56 < 61, så sätt low = mid + 1 mid=6 high=6 low=6 v 2 9 22 31 42 56 61 72 76 97 Sätt mid = (low + high) / 2 Är v[mid] == 61? Ja!
Binärsökning: Java-kod Metoden söker i int-vektorn v efter talet nbr. Returnerar indexet om nbr hittas, annars -1. public static int binarysearch(int[] v, int nbr) { int low = 0; // undre gräns int high = v.length - 1; // övre gräns while (low <= high) { int mid = (low + high) / 2; // mittpunkt if (v[mid] == nbr) { return mid; else if (v[mid] < nbr) { low = mid + 1; else { high = mid - 1; return -1;
Hur lång tid tar sökningen? Anta att vi söker bland N element. Linjärsökning: t ~ N Binärsökning: t ~ log N t k 1 N På den dubbla tiden kan vi linjärsöka 2N element binärsöka N 2 element k 2 log N (Tecknet ~ betyder här proportionellt mot ) N
Registrering Uppgift: Räkna antal element av olika slag. Lösning: Använd en vektor av typ int[] för att lagra de olika antalen. Exempel: Räkna mynt. Vi behöver en låda för enkronor, en för 5- kronor och en för 10-kronor: int[] nbrcoins = new int[3]; enkronor 5-kronor 10-kronor
Exempel: räkna olika tärningsslag Programmet på nästa sida räknar antalet 6:or som en tärning slår. Hur kan man ändra detta till att samla statistik för alla tärningens utfall, det vill säga räkna hur många 1:or, 2:or, 3:or, 4:or, 5:or och 6:or som slås?
Räkna sexor (jämför även lab 7 och exemplet på föreläsning 8) public class DieTest { public static void main(string[] args) { Die d = new Die(); Scanner scan = new Scanner(System.in); System.out.println("Antal tärningskast: "); int nbr = scan.nextint(); int nbr6 = 0; for (int i = 0; i < nbr; i++) { d.roll(); if (d.getresult() == 6) { nbr6++; System.out.println("Det blev en 6:a i " + ((double)nbr6/nbr * 100) + "% av fallen");
Hur spara samtliga tärningsresultat? int[] res = new int[6]; for (int i = 0; i < nbr; i++) { d.roll(); int a = d.getresult(); res[a - 1]++; [0] [1] [2] [3] [4] [5] res 16454 16512 16786 16725 16731 16792 antal 1:or antal 2:or antal 6:or
Exempel: registrera studenters poäng på prov antag att vi har en klass Student: Student /** Tar reda på studentens poäng på provet. */ int getpoints(); (Klassen Student innehåller säkert även en konstruktor och andra metoder vi bortser här från dessa.)
Exempel: registrera studenters poäng på prov public class Test { private Student[] students; // studenterna private int n; // antalet studenter /** Skapar ett prov med plats för max studenter. */ public Test(int max) { students = new Student[max]; n = 0; /** Lägger till studenten s. */ public void add(student s) { students[n] = s; n++; /** Skriver ut antalet studenter som har 0,1,...,50 poäng på provet */ public void printstatistics() {...
Exempel: registrera studenters poäng på prov Antag att vi vill registrera hur många som hade 0, hur många som hade 1, hur många som hade 2 poäng etc. etc. Maxpoäng är 50. public void printstatistics() { int[] nbrs = new int[51]; for (int i = 0; i < n; i++) { int index = students[i].getpoints(); nbrs[index]++; System.out.println("Poäng\tAntal studenter "); for (int i = 0; i < nbrs.length; i++) { System.out.println(i + "\t " + nbrs[i]);
Exempel: registrera studenters poäng på prov Antag att vi vill registrera hur många som hade 0-9 poäng, 10-19 poäng, 20-29 poäng, 30-39 poäng och 40-50 poäng. Detta är ett (nästan) regelbundet intervall och kan därför lösas relativt enkelt. public void printstatistics() { int[] nbrs = new int[5]; for (int i = 0; i < n; i++) { int index = students[i].getpoints() / 10; if (index == 5) { // specialfall: om 50 poäng index = 4; nbrs[index]++; //... skriv ut antalen
Exempel: registrera studenters poäng på prov Tänk istället att 0-24 poäng ger U, 25-34 poäng betyg 3, 35-42 poäng betyg 4 och 43-50 poäng betyg 5. Då har vi ett oregelbundet intervall som vi får hantera annorlunda. Antalet U-betyg registreras i nbrs[0], antalet 3-betyg i nbrs[1], osv. int[] nbrs = new int[4]; // plats för U, 3, 4, 5 //För varje student int points = students[i].getpoints(); int index; if (points < 25) { index = 0; else if (points < 35) { index = 1; else if (points < 43) { index = 2; else { index = 3; nbrs[index]++;
Sortering Lunds Tekniska Högskola Xxxxxxxxxxxxxxxx Xxxxxxxxxxxxxx ÅÅÅÅ-MM-DD
På tentan förväntas du kunna (bland annat) Söka i en vektor eller lista (linjärsökning OK; om vi vill ha t ex binärsökning får du ledtrådar) Registrering Sortering av osorterad vektor eller lista (valfri algoritm; om en viss algoritm behövs får du ledtrådar) Här visas urvalssortering och (något om) bubbelsortering (Senare visas även insättningssortering.)
Urvalssortering: idén Sortera vektorn v. 4 v 2 9 22 31 97 76 72 61 56 42
Urvalssortering (selection sort) Urvalssortering beskrivs i boken (avsnitt 8.9). Princip: Börja på första elementet. Hitta det minsta elementet i resten av listan; byt plats på det minsta och det första. Det första elementet är nu klart. Fortsätt till det andra. Hitta det minsta elementet i återstoden av listan; byt plats på det minsta och det andra. De första två elementen är nu klara. Fortsätt till det tredje. Osv.
Urvalssortering: Java-kod public static void selectionsort(int[] a) { for (int i = 0; i < a.length - 1; i++) { // hitta det minsta elementet efter a[i] for (int k = i + 1; k < a.length; k++) { if (a[k] < a[i]) { int temp = a[i]; // byt värde på a[i] och a[k] a[i] = a[k]; a[k] = temp; i k
Urvalssortering (en lite annan variant) (Per Holm 2007, s. 135. Något modifierad.) public static void selectionsort(int[] a) { for (int i = 0; i < a.length - 1; i++) { int min = Integer.MAX_VALUE; int minindex = -1; for (int k = i; k < a.length; k++) { if (a[k] < min) { min = a[k]; minindex = k; a[minindex] = a[i]; a[i] = min;
Hur lång tid tar urvalssortering? Anta att vi har N element Låt J vara antalet jämförelser av vektorelement Då är J = (N-1) + (N-2) +... + 1 = N(N-1)/2 N 2 /2 Tiden ökar väsentligen kvadratiskt: dubbelt antal element ger fyrdubbel exekveringstid Effektivare sorteringsalgoritmer (N log N) möter ni i EDAA01 Programmeringsteknik fördjupningskurs
En annan enkel sorteringsalgoritm: Bubbelsortering Gå igenom elementen, och jämför alla par av närliggande element (dvs i och i+1, för 0 i < N 1). Om de båda jämförda värdena inte är i ordning, byt plats på dem. Det största elementet bubblar på så vis till den sista platsen. Upprepa alltsammans igen, så att det näst sista elementet får rätt värde, det näst näst sista, och så vidare. Två strategier: Upprepa ovanstående N-1 gånger Upprepa ovanstående tills ingen förändring längre sker Det finns många sätt att göra bubbelsortering
Switch-satsen Lunds Tekniska Högskola Xxxxxxxxxxxxxxxx Xxxxxxxxxxxxxx ÅÅÅÅ-MM-DD
Exempel antal dagar Skriv en sats som givet ett månadsnummer beräknar antal dagar i månaden. Vi bortser från skottår. Scanner scan = new Scanner(System.in); int monthnbr = scan.nextint(); // månadens nummer (1..12) int nbrofdays; // antal dagar if (monthnbr == 2) { nbrofdays = 28; else if (monthnbr==4 monthnbr==6 monthnbr==9 monthnbr==11) { nbrofdays = 30; else { nbrofdays = 31;
Antal dagar switch-sats Scanner scan = new Scanner(System.in); int monthnbr = scan.nextint(); //månadens nummer (1..12) int nbrofdays; // antal dagar switch (monthnbr) { case 2: nbrofdays = 28; break; case 4: case 6: case 9: case 11: nbrofdays = 30; break; default: nbrofdays = 31; break;
Viktigt om switch-case Ersätter upprepande enklare if..else if..else if.. Värdet som testas kan vara av typen byte, short, int, char eller String. Utan break exekveras nästa alternativ också: vid break avbryts hela switch-satsen. Ett vanligt misstag i switch-satser är att glömma break. default är det som körs om inga andra alternativ matchar.
Typisk användning av switch-sats Scanner scan = new Scanner(System.in); System.out.println( Ange kommando (1-3): ); int cmd = scan.nextint(); switch (cmd){ case 1: //Satser för att utföra kommando 1 break; case 2: //Satser för att utföra kommando 2 break; case 3: //Satser för att utföra kommando 3 break; default: System.out.println("Inget giltigt kommando"); break;