Lunds universitet FYTA11 Institutionen för Astronomi och Teoretisk fysik HT 12 Lösningsförslag tentamen FYTA11 Javaprogrammering Onsdag 5 december 2012, 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 fyra uppgifter som vardera kan ge upp till tio poäng. För G och VG krävs 20 respektive 30 poäng, inräknat de högst fem bonuspoängen från simuleringsövningarna. Uppgift 1: Små fel Korrigera felen i följande kodstycken. a. b. c. d. e. //if(arr.length = 3) System.out.println("Length is 3"); if(arr.length == 3) System.out.println("Length is 3"); //JButton button; JButton button = new JButton(); button.settext("click me!"); public static void main(string[] args) if(args.length!= 1) System.out.println("This program requires one argument!"); System.exit(1); //System.out.println("argument was: " + args[1]); System.out.println("argument was: " + args[0]); //int year = 60 * 60 * 24 * 365; long year = 60 * 60 * 24 * 365; // (eller möjligen double) System.out.println("medellivslängd: " + year * 80 + " sekunder"); private String myname = "Steve"; //... public boolean ismyname(string name) // return myname == name; return myname.equals(name);
f. g. h. i. j. //System.out.println("pi is roughly = " + 22/7); System.out.println("pi is roughly = " + 22./7); //System.out.println("pi is roughly = " + 3 + Math.sqrt(0.02)); System.out.println("pi is roughly = " + (3 + Math.sqrt(0.02))); double x, y; do x = Math.random() * 2-1; y = Math.random() * 2-1; // double r2 = x * x + y * y; // while(r2 < 1); while(x * x + y * y < 1); Object[] v = new Object[2]; v[0] = "Something"; v[1] = 123; //... //String thing = v[0]; String thing = (String)v[0]; public class Vehicle private String model; public Vehicle(String model) this.model = model; // I en annan fil: public class Bicycle extends Vehicle public Bicycle(String model) // this.model = model; super(model);
Uppgift 2: Median av avrundade argument Skriv ett program som tar en uppsättning tal som argument då det startas, och skriver ut medianen av talen efter att ha avrundat vart och ett av dem till närmaste heltal. Medianen av ett udda antal tal definieras som det tal som ligger i mitten när talen sorterats. Medianen av ett jämnt antal tal är medelvärdet av de två mittersta talen (och behöver alltså inte vara ett heltal). Exempelvis skall % java Median 11.2-33.3 12.2 0 90 skriva ut talet 11 (eller 11.0), medan % java Median 11.2 12.2 0 90 skriver ut 11.5. Om något av argumenten inte är ett tal skall en varning skrivas ut men programmet får inte avbrytas utan skall försöka använda resterande argument. Om det inte finns några giltiga tal alls så skall ett felmeddelande skrivas ut. public class Median public static void main(string[] args) // Gör plats till så många tal som det finns argument // men håll reda på hur många som stoppats in. double[] arr = new double[args.length]; int num = 0; // antal tal for(int i = 0; i < args.length; ++i) try // Om konverteringen lyckas sparar vi talet (avrundat) och ökar num double v = Double.parseDouble(args[i]); arr[num++] = Math.rint(v); catch(numberformatexception e) System.err.println(args[i]+ " är inte ett tal"); if(num == 0) System.err.println("inga tal givna"); return; // Sortera talen och finn medianen java.util.arrays.sort(arr, 0, num); if(num % 2 == 1) System.out.println(arr[num/2]); else System.out.println((arr[num/2] + arr[num/2-1]) / 2); // alternativt kan man bli av med if-satsen genom att notera att // num/2 och (num-1)/2 är identiska om num är udda: // System.out.println((arr[num/2] + arr[(num-1)/2]) / 2);
Uppgift 3: Molly Malone Molly Malone går med sin kärra och säljer fisk i Dublin. Hon antecknar fisksorten och antalet vid varje försäljning, och vill sedan sammanställa informationen med hjälp av ett datorprogram. Du behöver hjälpa Molly att skriva en metod som sorterar fiskarna och summerar ihop antalet av respektive sort. I klassen FishNotes finns en inre klass FishCount som representerar en rad i Mollys anteckningar, med namnet på en fisk och antalet av den sorten. Ditt jobb är att: (a) skriva klart metoden summarize() som skall byta ut innehållet i fishcounts så som beskrivs i koden: Efter summarize() skall fiskarna vara sorterade alfabetiskt och ihopsummerade efter fisksort (8p). (b) beskriva i ord (eller med kod) hur du skulle göra för att i stället sortera fiskarna efter antal (fortfarande summerade efter sort), utifrån din lösning till (a) (2p). Det finns flera olika sätt att lösa problemet. Följande sidor ger dokumentation för några olika klasser och metoder som kan vara användbara. Minns också hur man itererar över en Collection (som t.ex. ArrayList): for(iterator<?> it = c.iterator(); it.hasnext(); )... it.next()... alternativt for(element type e : collection)... Exempel: om fishcounts innehåller (Sill, 12), (Torsk, 2), (Lax, 5), (Sill, 7) före anropet till summarize så skall fishcounts efter anropet innehålla (Lax, 5), (Sill, 19), (Torsk, 2). Vissa dagar säljer Molly otroligt många fiskar så det är lämpligt att metoden inte skalar kvadratiskt (eller värre) med antalet element i fishcounts. Lösningsförslag till (a) finns på följande sidor. Uppgift (b) löses bäst genom att man sorterar med en Comparator liknande den i andra lösningsförslaget, men med jämförelse av count. Om man vill kan man i andra hand (dvs om talen är lika) också jämföra fish så att fiskar med samma antal blir sorterade alfabetiskt.
En lösning med Comparable, med ändringar i FishCount: import java.util.*; public class FishNotes /** Ett par av (namn, antal) för någon sorts fisk */ // public class FishCount ** ändrad till: public class FishCount implements Comparable<FishCount> public String fish; public int count; public FishCount() public FishCount(String f, int c) fish = f; count = c; // Ny metod: public int compareto(fishcount o) return fish.compareto(o.fish); /** Listan över sålda fiskar */ private ArrayList<FishCount> fishcounts = new ArrayList<FishCount>(); /** Lägger till en post till fisklistan */ public void addfish(string name, int count) fishcounts.add(new FishCount(name, count)); /** Sammanställer listan över fiskar så att fiskarna kommer i alfabetisk ordning och varje fisksort finns med en gång, tillsammans med totala antalet av den sorten. */ public void summarize() Collections.sort(fishCounts); ArrayList<FishCount> newfc = new ArrayList<FishCount>(); String prevfish = null; int cnt = 0; for(fishcount fc : fishcounts) if(fc.fish.equals(prevfish)) cnt += fc.count; else if(prevfish!= null) newfc.add(new FishCount(prevfish, cnt)); prevfish = fc.fish; cnt = fc.count; if(prevfish!= null) newfc.add(new FishCount(prevfish, cnt)); fishcounts = newfc; /** Skriver ut fisklistan */ public void print() for(fishcount fc : fishcounts) System.out.println(fc.fish + "\t" + fc.count);
En lösning med Comparator och en anonym klass som gör att man slipper ändra utanför summarize: public void summarize() // Vi vill slippa hantera tomma samlingar if(fishcounts.size() < 2) return; // Sortera på fisknamn med en anonym Comparator-klass Collections.sort(fishCounts, new Comparator<FishCount>() public int compare(fishcount f1, FishCount f2) return f1.fish.compareto(f2.fish); ); // Skapa en ny samling fiskar. Går igenom fisklistan och adderar upp // antalen och sparar en fisk när vi ser en växling till en annan sort. ArrayList<FishCount> newfc = new ArrayList<FishCount>(); FishCount nfc = null; for(fishcount fc : fishcounts) if(nfc == null) // Första fisken, ta den (vi sabbar gamla fishcounts) nfc = fc; else if(fc.fish.equals(nfc.fish)) // Samma fisk; öka antalet nfc.count += fc.count; else // Annan fisk; nu kan vi spara den förra med rätt antal newfc.add(nfc); nfc = fc; newfc.add(nfc); // Sista fisken i listan fishcounts = newfc; En kortare lösning med TreeMap: public void summarize() // Vi lägger fiskarna i ett träd så blir de sorterade efterhand. TreeMap<String, Integer> tree = new TreeMap<String, Integer>(); for(fishcount fc : fishcounts) // Om fisken redan finns i trädet så öka dess antal. int cnt = fc.count; if(tree.containskey(fc.fish)) cnt += tree.get(fc.fish); tree.put(fc.fish, cnt); // Kopiera från trädet till fishcounts fishcounts = new ArrayList<FishCount>(); // clear() vore bättre for(map.entry<string,integer> ent : tree.entryset()) addfish(ent.getkey(), ent.getvalue());
Uppgift 4: Partiklar i potential Du skall simulera partiklar som rör sig i en potential och påverkar varandra. För enkelhets skull antas alla partiklar ha massan 1, så acceration och kraft är ekvivalenta. Simuleringsrogrammet finns redan, men du behöver skriva kod för ditt specifika system. Allting görs i två dimensioner, och klassen Pair (se nästa sida) används för att representera både en punkt (x,y) och en kraftvektor (F x,f y ). Potentialen som du har ges av k r 1 r U(r) = k r < 1 där k är någon konstant, vilket ger accelerationen (och kraften) k r r 1 r a(r) = 3 0 r < 1 för en partikel i punkten r = (x,y). r är längden av r. (a) Implementera interfacet Potential (se nästa sida), i överensstämmelse med ekvationerna ovan. Din klass heter lämpligen MyPotential, och skall ta konstanten k som argument till sin konstruktor. (b) Potential har en metod randomposition som behövs för att kunna slumpa ut positioner på lämpliga positioner. Implementera den metoden så att den returnerar en slumpmässig position r jämnt fördelad över det område där 1 < r < 2. Kraften som verkar på en partikel i till följd av växelverkan med övriga partiklar är F i = j i och den totala energin i växelverkningarna är E = i (r i r j ) r i r j 3 j i 1 r i r j (c) Lägg till interaktioner mellan partiklarna genom att implementera interfacet InteractionPotential. De båda metoderna tar in positionerna hos samtliga partiklar. getforces returnerar krafterna från växelverkan mellan partiklarna, med en kraftvektor per partikel. getenergy returnerar den totala energin hos växelverkningarna.
/** Ett par av värden (x,y), dvs en vektor i 2 dimensioner */ public class Pair public double x; public double y; public Pair() public Pair(double xx, double yy) x = xx; y = yy; public interface Potential /** Returnerar den potentiella energin hos en partikel på den givna positionen. */ double getenergy(pair position); /** Returnerar ett Pair (en vektor (Fx,Fy)) som representerar kraften som verkar på en partikel på den givna positionen (gradienten av -energin). */ Pair getforce(pair position); /** Genererar en slumpmässig punkt i ett lämpligt område för en partikel ** att starta från i simuleringen. */ Pair randomposition(); Lösningsförslag (a-b): public class MyPotential implements Potential private double k; public MyPotential(double k) this.k = k; // For convenience: distance to origin for a position private static double posr(pair p) return Math.sqrt(p.x * p.x + p.y * p.y); public double getenergy(pair pos) double r = posr(pos); if(r < 1) return k; // skulle ha varit -k return -k / r; public Pair getforce(pair pos) double r = posr(pos); if(r < 1) return new Pair(); double a = -k / (r * r * r); return new Pair(a * pos.x, a * pos.y); public Pair randomposition() double a = Math.random() * 2 * Math.PI; double r = Math.random() + 1; return new Pair(r * Math.cos(a), r * Math.sin(a));
public interface InteractionPotential /** Returnerar den totala interaktionsenergin hos en uppsättning partiklar på de givna positionerna. */ double getenergy(pair[] positions); /** Returnerar ett fält med lika många Pair som i argumentet. Argumentets Pair representerar partiklarnas koordinater och returvärdets Pair representerar de krafter som verkar på var och en av partiklarna till följd av växelverkan mellan dem. */ Pair[] getforces(pair[] positions); Lösningsförslag (c): public class MyInteraction implements InteractionPotential // Avståndet mellan två punkter, så vi slipper skriva uttrycket flera gånger private static double dist(pair a, Pair b) return Math.sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); public double getenergy(pair[] pos) double E = 0; for(int i = 0; i < pos.length; ++i) for(int j = 0; j < pos.length; ++j) if(i!= j) E += 1 / dist(pos[i], pos[j]); return E; public Pair[] getforces(pair[] pos) Pair[] f = new Pair[pos.length]; for(int i = 0; i < pos.length; ++i) f[i] = new Pair(); for(int j = 0; j < pos.length; ++j) if(i!= j) double d3 = Math.pow(dist(pos[i], pos[j]), 3.); f[i].x += (pos[i].x - pos[j].x) / d3; f[i].y += (pos[i].y - pos[j].y) / d3; return f;