Eksamen: REA3049 | Semester: Høst 2023 | Tema: Pseudokode, OOP-prinsipper, billettsystem-flytdiagram, nest-størst-algoritme, IoT-personvern, YouTube-datasett, Manic Mansion-spill
| Alt. | Beskrivelse | Skriver ut |
|---|---|---|
| 1 | FOR i ≤ 5: PRINT i | 1, 2, 3, 4, 5 ✅ |
| 2 | WHILE i < 5: PRINT i; INCREMENT | 1, 2, 3, 4 (mangler 5) ❌ |
| 3 | FOR i ≤ 4: PRINT i+1 | 1, 2, 3, 4, 5 ✅ |
| 4 | WHILE i ≤ 5: PRINT i; INCREMENT BY 2 | 1, 3, 5 ❌ |
┌──────────┐
│ START │ ──▶ LES alder ──▶ ╱ alder ≤ 15? ╲──Ja──▶ "Barnebillett: 30 kr"
└──────────┘ Nei
▼
╱ alder ≥ 67? ╲──Ja──▶ "Pensjonistbillett: 35 kr"
Nei
▼
"Voksenbillett: 50 kr"
Java-implementasjon (referanse):
public static String billettpris(int alder) {
if (alder <= 15) return "Barnebillett: 30 kr";
if (alder >= 67) return "Pensjonistbillett: 35 kr";
return "Voksenbillett: 50 kr";
}
| Alt. | Strategi | Korrekt? | Hvorfor |
|---|---|---|---|
| 1 | Finn størst, fjern, finn nest | ❌ | Fjerner bare én ved duplikater. |
| 2 | Initialiser med to første, oppdater nøye | ✅ | Sjekker likhet med tall ≠ størst. |
| 3 | Én løkke uten likhets-sjekk | ❌ | Returnerer 9 som nest-størst i [9, 9, 5]. |
| 4 | Sorter, hopp over duplikater | ✅ | Korrekt selv ved duplikater. |
Tidskompleksitet: Løsning 2 er O(n) — én pass. Løsning 4 er O(n log n) på grunn av sortering.
Plasskompleksitet: Løsning 2 bruker konstant ekstra plass; løsning 4 trenger plass til sortert kopi.
Lesbarhet: Løsning 4 er enklere å forstå, men løsning 2 er mer effektiv. For store datasett bør man velge løsning 2; for typiske skoleeksempler er løsning 4 mest attraktiv.
// NestStorst.java — to korrekte løsninger
import java.util.*;
import java.util.stream.*;
public class NestStorst {
// Løsning 2 — én pass, O(n)
public static OptionalInt lineaer(int[] tall) {
if (tall.length < 2) return OptionalInt.empty();
int storst = Integer.MIN_VALUE, nestStorst = Integer.MIN_VALUE;
for (int t : tall) {
if (t > storst) {
nestStorst = storst;
storst = t;
} else if (t < storst && t > nestStorst) {
nestStorst = t;
}
}
return nestStorst == Integer.MIN_VALUE ? OptionalInt.empty() : OptionalInt.of(nestStorst);
}
// Løsning 4 — sorter og hopp over duplikater, O(n log n)
public static OptionalInt sortert(int[] tall) {
if (tall.length < 2) return OptionalInt.empty();
int[] sortert = IntStream.of(tall).boxed()
.sorted(Comparator.reverseOrder())
.mapToInt(Integer::intValue).toArray();
int storst = sortert[0];
for (int i = 1; i < sortert.length; i++) {
if (sortert[i] != storst) return OptionalInt.of(sortert[i]);
}
return OptionalInt.empty();
}
public static void main(String[] args) {
int[][] eksempler = {
{9, 9, 5, 3}, // forventet: 5
{1, 2, 3, 4, 5}, // forventet: 4
{7}, // forventet: empty
{3, 3, 3} // forventet: empty
};
for (int[] e : eksempler) {
System.out.printf("%-18s → lineær=%s, sortert=%s%n",
Arrays.toString(e), lineaer(e), sortert(e));
}
}
}
Endring: Den mest gjennomgripende endringen i finanssektoren er overgangen til digitale betalingsløsninger — Vipps, BankID, mobilbank — som har erstattet kontant og papirgiro. Norge er blant verdens mest «kontantløse» økonomier; under 3 % av betalinger skjer med kontanter (Norges Bank, 2023). Endringen er drevet av smarttelefonens utbredelse, BankID, åpne API-er gjennom PSD2-direktivet, og maskinlæringsbasert svindeldeteksjon.
Dilemmaet: Når alle transaksjoner er digitale, etterlater hvert kjøp en datasti. Bankene kan slutte seg til helsetilstand, politisk engasjement, forhold og hele livsstilen.
Spenning: Dataene er nyttige for svindelhindring og rådgivning, men også svært sensitive. Lekkasje eller misbruk kan brukes til diskriminering, målrettet manipulasjon eller statlig overvåkning.
GDPR og dataminimering: Forordningen krever at banker bare lagrer det de trenger til avtalt formål — men «svindeldeteksjon» og «kunderelasjon» er bredt nok til at lite begrenses i praksis.
Vurdering: Den kontantløse økonomiens bekvemmelighet kommer med en pris i form av redusert anonymitet. Et velregulert system kan dempe risikoen, men aldri fjerne den.
// YouTubeStatistikk.java — analyse av YouTube-datasett
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.util.*;
import java.util.stream.*;
public class YouTubeStatistikk {
/* Forberedelse: datasettet kan være kodet med ukjent tegnsett.
* Vi prøver UTF-8 først, deretter ISO-8859-1 (latin-1).
* Numeriske felter er kodet som tekst — vi konverterer der det kreves. */
private static List<Map<String, String>> lesData(String filsti) throws IOException {
String innhold;
try {
innhold = Files.readString(Paths.get(filsti), StandardCharsets.UTF_8);
} catch (IOException ex) {
innhold = Files.readString(Paths.get(filsti), StandardCharsets.ISO_8859_1);
}
String[] linjer = innhold.split("\\R");
String[] headers = linjer[0].split(",");
List<Map<String, String>> result = new ArrayList<>();
for (int i = 1; i < linjer.length; i++) {
String[] v = linjer[i].split(",", -1);
Map<String, String> rad = new LinkedHashMap<>();
for (int j = 0; j < headers.length; j++) {
rad.put(headers[j].trim(), j < v.length ? v[j].trim() : "");
}
result.add(rad);
}
return result;
}
private static long tilTall(String s) {
if (s == null || s.isBlank()) return 0;
try { return (long) Double.parseDouble(s.replace(",", "")); }
catch (NumberFormatException e) { return 0; }
}
private static List<Map.Entry<String, Integer>> toppTiLand(List<Map<String, String>> data) {
Map<String, Integer> teller = new HashMap<>();
for (Map<String, String> r : data) {
String land = r.getOrDefault("Country", r.getOrDefault("country", "")).trim();
if (!land.isEmpty() && !land.equalsIgnoreCase("nan")) {
teller.merge(land, 1, Integer::sum);
}
}
return teller.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.limit(10).toList();
}
private static Map<String, double[]> snittPerLand(List<Map<String, String>> data, Set<String> land) {
Map<String, long[]> samlet = new HashMap<>(); // {abo, views, antall}
for (Map<String, String> r : data) {
String l = r.getOrDefault("Country", r.getOrDefault("country", "")).trim();
if (land.contains(l)) {
samlet.computeIfAbsent(l, x -> new long[3]);
samlet.get(l)[0] += tilTall(r.get("subscribers"));
samlet.get(l)[1] += tilTall(r.get("video views"));
samlet.get(l)[2]++;
}
}
Map<String, double[]> snitt = new HashMap<>();
samlet.forEach((l, arr) -> snitt.put(l, new double[]{
arr[2] == 0 ? 0 : (double) arr[0] / arr[2],
arr[2] == 0 ? 0 : (double) arr[1] / arr[2]
}));
return snitt;
}
public static void main(String[] args) throws IOException {
List<Map<String, String>> data = lesData("youtube.csv");
List<Map.Entry<String, Integer>> topp = toppTiLand(data);
System.out.println("\nTopp 10 land etter antall YouTube-kanaler:");
for (int i = 0; i < topp.size(); i++) {
System.out.printf("%-4d%-20s%d%n", i + 1, topp.get(i).getKey(), topp.get(i).getValue());
}
Set<String> landSet = topp.stream().map(Map.Entry::getKey).collect(Collectors.toSet());
Map<String, double[]> snitt = snittPerLand(data, landSet);
System.out.println("\nGjennomsnitt per kanal i topp 10:");
topp.forEach(e -> {
double[] s = snitt.get(e.getKey());
System.out.printf("%-20s%,15.0f abonnenter%,20.0f visninger%n",
e.getKey(), s[0], s[1]);
});
}
}
En naturlig OO-modell består av en abstrakt Spillobjekt-klasse med posisjon og størrelse, og fire spesialiseringer: Menneske, Spokelse, Hindring og Sau. I Java realiseres dette med en abstrakt baseklasse og fire extends-klasser. Spill-klassen håndterer kollisjoner, frisoner, poengtelling og spilløkken via Swing's Timer.
// ManicMansion.java — pythonelle implementasjon med Swing
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
abstract class Spillobjekt {
double x, y;
int storrelse;
Color farge;
Spillobjekt(double x, double y, int s, Color f) {
this.x = x; this.y = y; this.storrelse = s; this.farge = f;
}
Rectangle rect() {
return new Rectangle((int)(x - storrelse / 2.0), (int)(y - storrelse / 2.0), storrelse, storrelse);
}
void tegn(Graphics g) {
g.setColor(farge);
Rectangle r = rect();
g.fillRect(r.x, r.y, r.width, r.height);
}
}
class Menneske extends Spillobjekt {
boolean bærerSau = false;
int poeng = 0;
Menneske(double x, double y) { super(x, y, 28, new Color(26, 43, 74)); }
double fart() { return 4.0 * (bærerSau ? 0.5 : 1); }
void oppdater(Set<Integer> taster, List<Hindring> hindringer, int bredde, int hoyde) {
double f = fart();
double nx = x, ny = y;
if (taster.contains(KeyEvent.VK_LEFT) || taster.contains(KeyEvent.VK_A)) nx -= f;
if (taster.contains(KeyEvent.VK_RIGHT) || taster.contains(KeyEvent.VK_D)) nx += f;
if (taster.contains(KeyEvent.VK_UP) || taster.contains(KeyEvent.VK_W)) ny -= f;
if (taster.contains(KeyEvent.VK_DOWN) || taster.contains(KeyEvent.VK_S)) ny += f;
double s = storrelse / 2.0;
nx = Math.max(s, Math.min(bredde - s, nx));
ny = Math.max(s, Math.min(hoyde - s, ny));
// Aksevis kollisjonshåndtering
double forrigeX = x;
x = nx;
if (hindringer.stream().anyMatch(h -> rect().intersects(h.rect()))) x = forrigeX;
double forrigeY = y;
y = ny;
if (hindringer.stream().anyMatch(h -> rect().intersects(h.rect()))) y = forrigeY;
}
}
class Spokelse extends Spillobjekt {
double dx, dy;
Spokelse(double x, double y) {
super(x, y, 24, new Color(229, 115, 115));
double v = Math.random() * 2 * Math.PI;
dx = Math.cos(v) * 3; dy = Math.sin(v) * 3;
}
void oppdater(int bredde, int hoyde, int frisone) {
x += dx; y += dy;
double s = storrelse / 2.0;
if (x < s || x > bredde - s) dx = -dx;
if (y < s || y > hoyde - s) dy = -dy;
if (x < frisone + s) dx = Math.abs(dx);
if (x > bredde - frisone - s) dx = -Math.abs(dx);
}
}
class Hindring extends Spillobjekt { Hindring(double x, double y) { super(x, y, 36, new Color(120, 100, 80)); } }
class Sau extends Spillobjekt { Sau(double x, double y) { super(x, y, 22, new Color(240, 240, 230)); } }
public class ManicMansion extends JPanel {
private static final int BREDDE = 800, HOEYDE = 600, FRISONE = 80;
private final Menneske menneske = new Menneske(40, HOEYDE / 2.0);
private final List<Spokelse> spokelser = new ArrayList<>();
private final List<Hindring> hindringer = new ArrayList<>();
private final List<Sau> sauer = new ArrayList<>();
private final Set<Integer> taster = new HashSet<>();
private boolean tapt = false;
private final Random rng = new Random();
public ManicMansion() {
setPreferredSize(new Dimension(BREDDE, HOEYDE));
setBackground(new Color(232, 215, 185));
nySpokelse(); nyHindring(); nyHindring(); nyHindring();
nySau(); nySau(); nySau();
setFocusable(true);
addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) { taster.add(e.getKeyCode()); }
public void keyReleased(KeyEvent e) { taster.remove(e.getKeyCode()); }
});
new Timer(1000 / 60, e -> { oppdater(); repaint(); }).start();
}
private double[] tilfeldigMidten() {
return new double[]{
FRISONE + 30 + rng.nextDouble() * (BREDDE - 2 * FRISONE - 60),
30 + rng.nextDouble() * (HOEYDE - 60)
};
}
private void nySpokelse() { double[] p = tilfeldigMidten(); spokelser.add(new Spokelse(p[0], p[1])); }
private void nyHindring() { double[] p = tilfeldigMidten(); hindringer.add(new Hindring(p[0], p[1])); }
private void nySau() {
sauer.add(new Sau(
BREDDE - FRISONE + 10 + rng.nextDouble() * (FRISONE - 30),
20 + rng.nextDouble() * (HOEYDE - 40)));
}
private void oppdater() {
if (tapt) return;
menneske.oppdater(taster, hindringer, BREDDE, HOEYDE);
spokelser.forEach(s -> s.oppdater(BREDDE, HOEYDE, FRISONE));
Rectangle mr = menneske.rect();
if (spokelser.stream().anyMatch(s -> mr.intersects(s.rect()))) { tapt = true; return; }
if (!menneske.bærerSau) {
for (Iterator<Sau> it = sauer.iterator(); it.hasNext(); ) {
if (mr.intersects(it.next().rect())) {
it.remove();
menneske.bærerSau = true;
break;
}
}
} else {
if (menneske.x < FRISONE) {
menneske.bærerSau = false;
menneske.poeng++;
nySau(); nySpokelse(); nyHindring();
} else if (sauer.stream().anyMatch(s -> mr.intersects(s.rect()))) {
tapt = true;
}
}
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(200, 220, 200));
g.fillRect(0, 0, FRISONE, HOEYDE);
g.fillRect(BREDDE - FRISONE, 0, FRISONE, HOEYDE);
hindringer.forEach(o -> o.tegn(g));
sauer.forEach(o -> o.tegn(g));
spokelser.forEach(o -> o.tegn(g));
menneske.tegn(g);
g.setColor(Color.BLACK);
g.setFont(new Font("SansSerif", Font.BOLD, 18));
g.drawString("Poeng: " + menneske.poeng + (tapt ? " — TAPT" : ""), 10, 25);
}
public static void main(String[] args) {
JFrame f = new JFrame("Manic Mansion");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ManicMansion spill = new ManicMansion();
f.add(spill);
f.pack(); f.setLocationRelativeTo(null); f.setVisible(true);
spill.requestFocusInWindow();
}
}
Spillobjekt definerer tegn(); subklasser kan overstyre etter behov.123456/
├── oppgave5/billettpris_flytdiagram.pdf
├── oppgave7c/NestStorst.java
├── oppgave11/YouTubeStatistikk.java + youtube.csv
├── oppgave12/ManicMansion.java
└── README.md (Java 17+)