Kodeforståelsesoppgaver (total videotid: 32:52)

Oppgave 5.1.1: Vektoriserte funksjoner

Funksjonen under regner ut omkretsen av en sirkel. Er den vektorisert?
f.m
function y = f(x)
    y = 2 * pi * x;
end

Videoforklaring (5:07)

Svar

Ja, funksjonen lar oss regne ut en mengde omkretser på en gang ved å utnytte Matlabs innebygde operasjoner.

Oppgave 5.1.2: Vektoriserte funksjoner

 

Funksjonen under regner ut arealet av en sirkel. Er den vektorisert? Hvordan kan den eventuelt vektoriseres?
g.m
function y = g(x)
    y = pi * x ^ 2;
end 

Videoforklaring (3:54)

Svar

 Nei, for funksjonen lar oss ikke regne ut svar for hele vektorer av gangen. Den kunne blitt vektorisert med bruk av elementvis opphøyd-i.

Oppgave 5.1.3: Vektoriserte funksjoner 

 

Funksjonen under regner ut verdien som tilsvarer et romersiffer. Er den vektorisert? Hvordan kan den eventuelt vektoriseres?
fra_romersiffer.m
function retur = fra_romersiffer(siffer)
    switch siffer
        case 'I', verdi = 1;
        case 'V', verdi = 5;
        case 'X', verdi = 10;
        case 'L', verdi = 50;
        case 'C', verdi = 100;
        case 'D', verdi = 500;
        case 'M', verdi = 1000;
    end
    retur = verdi;
end

Videoforklaring (10:48)

Svar

Den er ikke vektorisert, for den bruker ikke Matlabs innebygde operasjoner eller funksjoner og den kan heller ikke regne ut resultat for en hel vektor av verdier av gangen. Det er generelt vanskelig å vektorisere kode som trenger å bruke switch-setninger, siden de gjerne har mye sprikende oppførsel som det er vanskelig å uttrykke med én eller et lite knippe innebygde funksjoner og operasjoner. Det finnes en måte å gjøre det på her (se utdelt kode i oppgave 5.2.6), men generelt er det beste vi kan gjøre å lage en "liksom-vektorisert"-funksjon vha en for-løkke som behandler ett og ett element i parameteren. Ved å gjøre dette, kan kode som bruker funksjonen fortsatt skrives vektorisert.

Oppgave 5.1.4: Preallokering

 

Hva er ineffektivt med funksjonen som står i filen under? Hvordan kan den forbedres?
fib.m
function retur = fib(n)
    retur = [0, 1];
    for i = 3:n
        retur(i) = retur(i-1) + retur(i-2);
    end
    retur = retur(1:n);
end 

Videoforklaring (8:21)

Svar

Listen retur vokser med ett element for hver iterasjon i løkken. Det fører til mye kopiering av data. Den forbedres hvis man før for-løkken binder retur til en vektor med plass til alle elementene som skal produseres.

Oppgave 5.1.5: Preallokering

Er preallokering nødvendig i funksjonen under?
diff.m
function retur = diff_2(x)
    retur = x(2:end) - x(1:end-1);
end

Videoforklaring (4:42)

Svar

Nei, koden er vektorisert og produserer hele resultatlisten på en gang. Preallokering er bare nødvendig hvis vi produserer ett og ett element av gangen i en selvskrevet løkke.

Kodeoppgaver (total videotid: 1:39:13)

Oppgave 5.2.1: Samlet Fahrenheitoversikt

Du har tidligere laget et script som konverterer Celsius-verdier en bruker skriver inn fortløpende til Fahrenheit. For at utskriften fra scriptet skal bli mer oversiktlig, ønsker du å samle all utskriften i stedet for at den kommer fortløpende.

Endre scriptet slik at utskriften kommer først etter at brukeren har skrevet inn alle Celsius-verdiene.  Konverteringene skal også skrives ut i rekkefølge etter stigende Celsius-verdi.

Utdelt kode

fahrenheit.m
svar = input('Ønsker du å skrive inn egne Celsius-verdier? (ja/nei) ', 's');
if strcmp(svar, 'ja')
    celsius = les_inn_tall('Skriv inn Celsius-verdi: ');
    while ~isempty(celsius)
        skriv_ut_fahr(celsius);
        celsius = les_inn_tall('Skriv inn Celsius-verdi: ');
    end
else
    skriv_ut_fahr(-20);
    skriv_ut_fahr(0);
    skriv_ut_fahr(18);
    skriv_ut_fahr(37);
    skriv_ut_fahr(100);
end 
les_inn_tall.m
function retur = les_inn_tall(melding)
    tallstr = input(melding, 's');
    tall = str2double(tallstr);
    er_ugyldig = isnan(tall);
    while er_ugyldig && ~isempty(tallstr)
        fprintf('Teksten %s kan ikke tolkes som et tall\n', tallstr);
        tallstr = input(melding, 's');
        tall = str2double(tallstr);
        er_ugyldig = isnan(tall);
    end
    if isempty(tallstr)
        retur = [];
    else
        retur = tall;
    end
end 
skriv_ut_fahr.m
function skriv_ut_fahr(celsius)
    fahr = cels2fahr(celsius);
    fprintf('Celsiusverdi %.2f tilsvarer Fahrenheitverdi %.2f\n', celsius, fahr);
end 
cels2fahr.m
function retur = cels2fahr(celsius)
    retur = 9/5*celsius + 32;
end 

 

Videoforklaring (19:00)

Del 1 (9:47)Del 2 (9:13)
Ooppgaveintroduksjon, gradvis vektorkonstruksjon, komma for å sette vektorer ved siden av hverandre, og preallokering.Vektorisert kode for å unngå for-løkker, og semikolon for å stable vektorer i høyden.

 

Løsningsforslag

fahrenheit.m
svar = input('Ønsker du å skrive inn egne Celsius-verdier? (ja/nei) ', 's');
if strcmp(svar, 'ja')
    celsius = les_inn_tall('Skriv inn Celsius-verdi: ');
    les_inn_flere = ~isempty(celsius);
    while les_inn_flere
        siste_verdi = les_inn_tall('Skriv inn Celsius-verdi: ');
        les_inn_flere = ~isempty(siste_verdi);
        celsius = [celsius, siste_verdi];
    end
    celsius = sort(celsius);
    skriv_ut_fahr(celsius);
else
    skriv_ut_fahr([-20, 0, 18, 37, 100]);
end
skriv_ut_fahr.m
function skriv_ut_fahr(celsius)
    fahr = cels2fahr(celsius);
    fprintf('Celsiusverdi %.2f tilsvarer Fahrenheitverdi %.2f\n', [celsius; fahr]);
end 

Oppgave 5.2.2: Karakterer

Lag en funksjon som tar inn en vektor med poengsummer fra 0 til 100, og som returnerer en vektor med bokstavkarakterer som tilsvarer poengsummene i argumentet. Karaktergrensene er som oppgitt i tabellen under.
PoengsumKarakter
89-100A
77-88B
65-76C
53-64D
41-52E
0-40F

Videoforklaring (10:25)

Løsningsforslag

karakterer.m
function retur = karakterer(poengsummer)
    retur = blanks(length(poengsummer));
    for i = 1:length(retur)
        poeng = poengsummer(i);
        if poeng >= 89
            retur(i) = 'A';
        elseif poeng >= 77
            retur(i) = 'B';
        elseif poeng >= 65
            retur(i) = 'C';
        elseif poeng >= 53
            retur(i) = 'D';
        elseif poeng >= 41
            retur(i) = 'E';
        else
            retur(i) = 'F';
        end
    end
end 

Oppgave 5.2.3: Utvikling av kvadratrotestimat

I en tidligere oppgave lagde du en funksjon som estimerte hva kvadratroten av et tall var, ved å bruke iterasjoner av Newtons metode. Du har nå lyst til å analysere hvordan estimatene utvikler seg. I stedet for å skrive ut estimatene, har du lyst til å returnere dem i en vektor. Oppdater funksjonen til å fungere slik i stedet.

Utdelt kode

kvadratrot.m
function retur = kvadratrot(tall)
    x = 1;
    er_over_feilgrense = true;
    i = 1;
    while er_over_feilgrense
       ny_x = x + (tall - x^2)/(2*x);
       fprintf('Iterasjon #%d: x_%d = %.10f, x_%d = %.10f\n', i, i-1, x, i, ny_x);
       relativ_endring = abs(ny_x - x)/x;
       er_over_feilgrense = relativ_endring >= 1e-9;
       x = ny_x;
       i = i + 1;
    end
    retur = x;
end 

Videoforklaring (15:46)

Del 1 (4:00)Del 2 (9:12)Del 3 (2:34)
Introduksjon av problemet, og vanlig, enkel løsning.Effektiv løsning.Demonstrasjon av kjøretidsfordelen man kan få med denne teknikken.

Løsningsforslag

kvadratrot.m
function retur = kvadratrot(tall)
    x = 1;
    er_over_feilgrense = true;
    i = 1;
    N = 4;
    retur = zeros(1, N);
    retur(1) = x;
    while er_over_feilgrense
       ny_x = x + (tall - x^2)/(2*x);
       relativ_endring = abs(ny_x - x)/x;
       er_over_feilgrense = relativ_endring >= 1e-9;
       x = ny_x;
       i = i + 1;
       if i > N
           N = 2*N;
           retur(N) = 0;
       end
       retur(i) = x;
    end
    retur = retur(1:i);
end 
test_lag_lang_liste.m
teststorrelser = [10:10:100, 200:100:1000, 1000:1000:10000, 10000:10000:100000];
reps = 20;
res_gradvis_dobling = zeros(reps, length(teststorrelser));
res_utvid_liste = zeros(reps, length(teststorrelser));
res_preallokering = zeros(reps, length(teststorrelser));
for i = 1:length(teststorrelser)
    n = teststorrelser(i);
    for j = 1:reps
        fprintf('#%d - lag_lang_liste_gradvis_dobling(%d)\n', j, n);
        tic
        lag_lang_liste_gradvis_dobling(n);
        res_gradvis_dobling(j,i) = toc;
    end
    for j = 1:reps
        fprintf('#%d - lag_lang_liste_utvid_liste(%d)\n', j, n);
        tic
        lag_lang_liste_utvid_liste(n);
        res_utvid_liste(j,i) = toc;
    end 
    for j = 1:reps
        fprintf('#%d - lag_lang_liste_preallokering(%d)\n', j, n);
        tic
        lag_lang_liste_preallokering(n);
        res_preallokering(j,i) = toc;
    end 
end
fprintf('%15s %25s %25s %25s\n', 'Listestørrelse', 'Gradvis dobling [ms]', 'Utvid liste [ms]', 'Preallokering [ms]');
fprintf('%15d %25.3f %25.3f %25.3f\n', [teststorrelser; 1000*[mean(res_gradvis_dobling);  mean(res_utvid_liste); mean(res_preallokering)]]);
plot(teststorrelser, mean(res_gradvis_dobling), 'g-', teststorrelser, mean(res_utvid_liste), 'm-', teststorrelser, mean(res_preallokering), 'b-');
lag_lang_liste_utvid_liste.m
function retur = lag_lang_liste_utvid_liste(n)
    retur = [];
    for i = 1:n
        retur(i) = i;
    end
end 
lag_lang_liste_gradvis_dobling.m
function retur = lag_lang_liste_gradvis_dobling(n)
    N = 20;
    retur = zeros(1, N);
    for i = 1:n
        if i > N
            N = 2*N;
            retur(N) = 0;
        end
        retur(i) = i;
    end
    retur = retur(1:n);
end 
lag_lang_liste_preallokering.m
function retur = lag_lang_liste_preallokering(n)
    retur = zeros(1, n);
    for i = 1:n
        retur(i) = i;
    end
end 

Oppgave 5.2.4: Utregning av karaktersnitt

Studieavdelingen har lyst til å regne ut gjennomsnittskarakteren for studentene i forskjellige studieprogram. De planlegger å lage en matrise hvor karakterene for hver student er samlet i kolonner, og karakterene i et gitt fag er oppgitt langs radene. Det er allerede samlet inn testdata i scriptet karakterdata.m. Lag en funksjon som tar inn en matrise med karakterer og en kolonnevektor med antall studiepoeng for hvert fag, og som returnerer en vektor som inneholder snittkarakteren for hver student. Man kan regne ut vektet gjennomsnitt med følgende formel: 

Utdelt kode

karakterdata.m
karakterer = [
    4, 3, 4, 3;
    4, 4, 4, 4;
    4, 4, 4, 4;
    4, 1, 2, 4;
    4, 4, 4, 4;
    3, 5, 6, 4;
    3, 3, 4, 6;
    3, 5, 4, 2 ];
studiepoeng = [ 
    7.5;
    7.5;
    7.5;
    15;
    7.5;
    7.5;
    15;
    7.5 ]; 

Videoforklaring (20:39)

Del 1 (6:31)Del 2 (6:34)Del 3 (7:34)
Introduksjon og forklaring av problemet, og definisjon av funksjonens ytre.Løsning med for-løkke og preallokering.Vektorisert løsning.

 

Løsningsforslag

karaktersnitt.m
function retur = karaktersnitt(karaktermatrise, studiepoeng)
    retur = zeros(1, size(karaktermatrise, 2));
    for i = 1:length(retur)
        % Regn ut verdien til retur(i)
        karakterer = karaktermatrise(:, i);
        vektede_karakterer = karakterer .* studiepoeng;
        retur(i) = sum(vektede_karakterer) / sum(studiepoeng);
    end
end
karaktersnitt.m
function retur = karaktersnitt(karaktermatrise, studiepoeng)
    tellere = studiepoeng' * karaktermatrise;
    nevner = sum(studiepoeng);
    retur = tellere ./ nevner;
end

Oppgave 5.2.5: Flyplasskø

I sikkerhetskontrollen på Værnes er det gjerne lite trykk på Fast-track-sporet, men mange med dårlig tid i den vanlige køen. Det er derfor ønskelig å lage et system som velger ut personene med dårligst tid fra den vanlige køen slik at man kan sluse de til Fast-track i stedet. Datasystemene til Værnes representerer personene i køen som tall i en matrise, hvor tallene representerer antall minutt til personens fly går. Eksempeldata er samlet i scriptet testkoe.m. Lag en funksjon som tar inn en slik matrise, og som returnerer en ny matrise med en kolonne mindre slik at personene med dårligst tid er fjernet. Tips: man kan finne den sorterte rekkefølgen til en vektor med Matlab-koden [~, ordning] = sort(v).

Figuren illustrerer en flyplasskø til venstre, og matriserepresentasjonen som brukes til høyre.

Utdelt kode

testkoe.m
koe = [
    93, 91, 200, 25, 37;
    75, 93, 112, 30, 52;
    121, 141, 134, 69, 66;
    32, 133, 118, 39, 111;
    27, 68, 93, 95,  35;
    86, 22, 83, 75, 141;
    91, 88, 120, 93, 95
]; 

 

Videoforklaring (33:23)

Del 1 (5:10)Del 2 (14:55)Del 3 (13:18)
Introduksjon til problemet, og definisjon av funksjonens ytre.Løsning med for-løkker og preallokering.Løsning med vektorisert kode.

 

Løsningsforslag

ny_flyplasskoe.m
function retur = ny_flyplasskoe(koe)
    [n_rad, n_kol] = size(koe);
    retur = zeros(n_rad, n_kol - 1);
    flat_koe = koe(:);
    [~, sortert_paa_tid] = sort(flat_koe);
    pers_med_daarligst_tid = sortert_paa_tid(1:n_rad);
    neste_person = 1;
    for j = 1:n_kol - 1
        for i = 1:n_rad
            % Regn verdien til retur(i, j)
            while ismember(neste_person, pers_med_daarligst_tid)
                neste_person = neste_person + 1;
            end
            retur(i, j) = flat_koe(neste_person);
            neste_person = neste_person + 1;
        end
    end
end
ny_flyplasskoe.m
function retur = ny_flyplasskoe(koe)
    flat_koe = koe(:);
    [~, sortert_paa_tid] = sort(flat_koe);
    [m, n] = size(koe);
    pers_med_best_tid = sortert_paa_tid(m+1:end);
    pers_mgtir = sort(pers_med_best_tid);
    ny_flat_koe = flat_koe(pers_mgtir);
    retur = reshape(ny_flat_koe, m, n-1);
end 


  • No labels