Lunds universitet FYTA11 Institutionen för Astronomi och Teoretisk fysik HT 15 Lösningsförslag FYTA11 Javaprogrammering Fredag 18 december 2015, 10:15 14:15 Instruktioner Hjälpmedel: Papper och penna. Behandla högst en uppgift per papper och sätt ditt namn och uppgiftens nummer på varje papper. Skriv läsligt och kommentera utförligt vad du gör det kan ge dig poäng även om resultatet blir fel. Tentamen omfattar i år tre uppgifter som sammanlagt kan ge 40 poäng. För G och VG krävs 20 respektive 30 poäng, inräknat de högst fyra bonuspoängen från simuleringsövningarna. Uppgift 1: Små kodsnuttar (10p) Korrigera felen i följande kodstycken där felens natur anges av kommentarer i/efter koden. Skriv en korrekt version av raden/raderna som ändras. a. b. c. // double 7root = Math.sqrt(7); double root7 = Math.sqrt(7); // variabelnamn börjar en på siffra //error: <identifier> expected // long flags[] = new int[0]; long flags[] = new long[0]; // längd 0 är tillåtet //error: incompatible types class Test // void Test() /*...*/ // void Test(int i) /*...*/ Test() /*...*/ Test(int i) /*...*/ // konstruktorer har ej returtyp public static void main(string[] args) Test t = new Test(14); //error: constructor Test in class Test // cannot be applied to given types
d. class Super protected void print() /*... */ class Sub extends Super // public int print() /*... */ public void print() /*... */ // måste ha samma returtyp här //error: print() in Sub cannot override print() in Test e. double side = 3.31; // double volume = side ^ 3; double volume = side * side * side; // alt. använd Math.pow //error: bad operand types for binary operator ^ f. g. h. // List<int> list = new ArrayList<int>(); List<Integer> list = new ArrayList<Integer>(); // referenstyp //error: unexpected type Set<Double> set = new TreeSet<Double>(); // set.add(25); set.add(25.); // auto-wrappning funkar bara från double //error: no suitable method found for add(int) class MyListener implements ActionListener @Override // protected void actionperformed(actionevent e) /*... */ public void actionperformed(actionevent e) /*... */ // ^^ måste förbli public //error: actionperformed(actionevent) in MyListener cannot implement // actionperformed(actionevent) in ActionListener
Följande program går att kompilera trots varningen Note: Test.java uses unchecked or unsafe operations. Dock krashar programmet när man kör det. Förklara kortfattat vad som är fel och hur man kunnat förhindra felet. (2p) j. import java.util.*; class Test public static void print(list<float> list) for(float f : list) System.out.println(f); // Exception in thread "main" java.lang.classcastexception: // java.lang.double cannot be cast to java.lang.float // Här är problemet: List utan <> är föråldrat och mindre // typsäkert. Vi kan stoppa in fel typer i listan och får här // en Double i stället för en Float. Bästa lösningen här är // att genomgående använda List<Float>. // Alternativ: list.add(25.0f) eller list.add(new Float(25)); public static void populate(list list) list.add(25.0); public static void main(string[] args) List<Float> list = new ArrayList<Float>(); populate(list); print(list);
Uppgift 2-3: Testa och lösa sudoku (20p) Sudoku är, säger Wikipedia, ett logikspel som går ut på att man ska placera ut siffror i ett rutmönster. Som figuren visar består rutmönstret av 9 9 rutor som delas upp i 3 3 block om 3 3 rutor. I dessa rutor skall sifforna 1 9 skrivas så att alla de nio siffrorna förekommer på varje rad, i varje kolumn och i varje block. Spelet utgår från en delvis ifylld spelplan sådan att den bara medger en enda lösning. När man löser sudoku för hand brukar man börja med att identifiera rutor vars siffra lätt inses och siffror som lätt går att placera, för att sedan titta på mer komplicerade samband. Ett sudokulösande datorprogram kan baseras på en enklare rekursiv metod som visserligen inte är optimal men som ändå kan finna lösningar på rimlig tid. Denna stora uppgift går ut på att a. implementera en metod som underlättar hanteringen av reglerna (cirka 6p) b. avgöra om en delvis ifylld spelplan uppfyller spelets regler (cirka 6p) c. implementera en metod som löser en sudoku (cirka 8p) Den abstrakta klassen SudokuSolver innehåller redan en del saker som behövs för uppgiften. Implementera den konkreta subklassen MySudokuSolver vars konstruktor också skall ta en char[][]. Syftet med metoden squaredigit är att sköta omvandlingen från (nummer på rad/kolumn/block och position däri) till (x,y i rutnätet). Metoden checksudoku skall använda squaredigit för att testa så att rutnätet inte innehåller några ogiltiga rader/kolumner/block. Grunden för det sista steget är att den rekursiva metoden solverecursively returnerar sant så fort problemet är löst. Om den hamnar i en återvändsgränd returneras falskt. 1. Finn en ledig ruta. 2. Om brädet är fullt: kontrollera lösningen och returnera sant/falskt. 3. Ansätt en siffra på rutan. 4. Följer förslaget reglerna? Om nej, gå till 3. och välj nästa siffra. 5. Gör ett rekursivt anrop till metoden. Om det ger sant är problemet löst. 6. Gå annars till 3 och välj nästa siffra. 7. Om alla siffror är testade är detta en återvändsgränd: Sudda bort siffran och returnera falskt.
public abstract class SudokuSolver /** The board, with digits 1 to 9. All other characters mean empty squares. */ protected char[][] board; /** Constructor that checks the size of the input board. */ protected SudokuSolver(char[][] input) if(input.length!= 9) throw new IllegalArgumentException("Wrong input size!"); for(char[] a : input) if(a.length!= 9) throw new IllegalArgumentException("Wrong input size!"); board = input; /** The three types of regions on the board */ protected final static int ROW = 0, COLUMN = 1, BLOCK = 2; /** Should return 0 if the given square is empty, otherwise a number (not a character!) between 1 and 9, inclusive. Squares are identified by three numbers which should be mapped to (x, y) on the board. regiontype is ROW, COLUMN or BLOCK. region is the number of the row, column or block (0 to 8). coord in the position within the region (also 0 to 8). The layout of the blocks is not really important. examples: ROW 2, coord 5 has y=2, x=5 COLUMN 2, coord 5 has x=2, y=5 BLOCK 0 probably has y<3, x<3 */ protected abstract int squaredigit(int regiontype, int region, int coord); /** Returns false only if the board breaks the game rules. */ public abstract boolean checksudoku(); /** Attempts to solve the sudoku. Returns true on success. */ public abstract boolean solverecursively();
class MySudokuSolver extends SudokuSolver public MySudokuSolver(char[][] input) super(input); private static int squarex(int rt, int reg, int coord) if(rt == ROW) return coord; if(rt == COLUMN) return reg; return (reg % 3) * 3 + coord % 3; private static int squarey(int rt, int reg, int coord) if(rt == ROW) return reg; if(rt == COLUMN) return coord; return (reg / 3) * 3 + coord / 3; protected int squaredigit(int rt, int reg, int coord) char v = board[squarey(rt, reg, coord)][ squarex(rt, reg, coord)]; if(v >= 1 && v <= 9 ) return v - 0 ; return 0; public boolean checksudoku() for(int rt = 0; rt < 3; ++rt) for(int b = 0; b < 9; ++b) boolean[] checked = new boolean[10]; for(int c = 0; c < 9; ++c) int d = squaredigit(rt, b, c); if(d > 0) if(checked[d]) return false; checked[d] = true; return true; public boolean solverecursively() for(int y = 0; y < 9; ++y) for(int x = 0; x < 9; ++x) if(squaredigit(row, y, x)!= 0) continue; for(char d = 1 ; d <= 9 ; ++d) board[y][x] = d; if(checksudoku() && solverecursively()) return true; board[y][x] = ; return false; return checksudoku();
Uppgift 4: Piratjakt! (10p) Du jobbar på Antipiratbyrån och har fått i uppgift att hitta blodtörstiga pirater. För att göra detta behöver du para ihop användare med nedladdningar via de IPadresser användarna använt. En användare tilldelas en IP-adress när han kopplar sig till nätverket, och ingen annan kan använda adressen förrän han loggar ut. Från en icke namngiven internetleverantör har du fått en lista med användare, IP-adresser och start- och sluttid för sådana sessioner. Det som är känt om gjorda nedladdningar är vilken fil det gäller, vilken IP-adress filen laddades ned till och hur dags detta skedde. Informationen representeras av typerna Login och Download: public static class Login public String user; public String ip; public Date start; public Date end; public static class Download public String filename; public String ip; public Date when; En Download hör ihop med en Login om IP-adressen är identisk och dessutom start when end. Dufårenarraymedobjektavvarderatypen.Bådadessaförväntasvaramycketlånga, vilket gör att det inte är lämpligt att kontrollera alla nedladdningar mot alla inloggningar. En mer effektiv lösning går att bygga med hjälp av de nedan dokumenterade klasserna. Ledning: Notera att beskrivningen ovan säger att två sessioner som överlappar i tiden inte kan ha samma IP-adress. Om man för en given IP-adress vet vilken session som var den sista att starta före en given tidpunkt, så kan man också avgöra om den tidpunkten inföll innan sessionen tog slut. (Notera dessutom att det kan finnas data om nedladdningar som av olika skäl inte faller inom någon känd session.) Lösningen skall ges som en metod public static void printpirates(login[] logins, Download[] downloads) som identifierar vem som laddat ner filer. Output kan exempelvis se ut så här: Carl laddade ner Trolltider.avi Nisse laddade ner Teskedsgumman.mov
java.util public class Date extends Object implements Serializable, Cloneable, Comparable<Date> The class Date represents a specific instant in time, with millisecond precision. java.lang public Interface Comparable<T> Type Parameters: T - the type of objects that this object may be compared to Method Detail: int compareto(t o) Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. java.util public Interface Map<K,V> Type Parameters: K - the type of keys maintained by this map V - the type of mapped values Method Summary: boolean containskey(object key) Returns true if this map contains a mapping for the specified key. V get(object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key. V put(k key, V value) Associates the specified value with the specified key in this map.
java.util Class TreeMap<K,V> public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, Serializable Method Summary: Map.Entry<K,V> ceilingentry(k key) Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key. K ceilingkey(k key) Returns the least key greater than or equal to the given key, or null if there is no such key. Map.Entry<K,V> floorentry(k key) Returns a key-value mapping associated with the greatest key less than or equal to the given key, or null if there is no such key. K floorkey(k key) Returns the greatest key less than or equal to the given key, or null if there is no such key. java.util public static interface Map.Entry<K,V> A map entry (key-value pair). Method Summary: K getkey() Returns the key corresponding to this entry. V getvalue() Returns the value corresponding to this entry. V setvalue(v value) Replaces the value corresponding to this entry with the specified value.
// Tanke: // Spara först all login-information i en Map av Map: // ip -> (start -> Login) // Genom att använda TreeMap för start -> Login går det sedan att // söka efter inloggningen närmas före nedladdningen med hjälp av // floorentry. En jämförelse med utloggningstiden avgör om det var // rätt Login som hittades. public static void printpirates(login[] logins, Download[] downloads) // Notera att typen uttryckligen säger TreeMap så vi kan använda // TreeMap-specifika metoder. Map<String, TreeMap<Date, Login>> loginmap = new HashMap<String, TreeMap<Date, Login>>(); for(login l : logins) // Finn TreeMap för denna ip TreeMap<Date, Login> tm = loginmap.get(l.ip); // Om trädet saknades... if(tm == null) //...lägg till det och uppdatera referensen tm. tm = new TreeMap<Date, Login>(); loginmap.put(l.ip, tm); tm.put(l.start, l); for(download d : downloads) // Känner vi till denna ip? TreeMap<Date, Login> tm = loginmap.get(d.ip); if(tm == null) continue; // Hitta inloggningen närmast före nedladdningen Map.Entry<Date, Login> prev = tm.floorentry(d.when); // Om den finns och slutar efter nedladdningen, skriv ut. if(prev!= null && prev.getvalue().end.compareto(d.when) > 0) System.out.println(prev.getValue().user + " laddade ner " + d.filename + " på " + d.when);