Umu logo

Umeå Universitet
Institutionen för datavetenskap
Programmeringsteknik för tekniska fysiker


Niklas Frykholm, 24 februari 1999

Gruppövning 2 - Lösningsförslag

1. Finn sex fel

g2_1.c


#include <stdio.h>

int upphoj(int x, int y);

int main(void)
{
  printf("3 ^ 5 = %d\n", upphoj(3,5));
  printf("5 ^ 3 = %d\n", upphoj(5,3));
  return 0;
}


/* Beräknar värdet av x upphöjt till y. */
int upphoj(int x, int y)
{
  int res=1, i;

  for (i=1; i<=y; i++)
    res *= x;
  return res;
}

2. Funktionsvärden

g2_2.c


#include <stdio.h>
#include <math.h>

double gamma(double a);

int main(void)
{
  int i;

  /* Formateringskoder för att få snyggare utskrift. Se
     kap 11. */
  printf("%6s %6s\n", "a", "gamma"); 
  for (i=0; i<=19; i++)
    printf("%6.2lf %6.2lf\n", i*0.05, gamma(i*0.05));
  return 0;
}

double gamma(double a)
{
  return 1/sqrt(1-a*a);
}

3. Prognosmaskin

g2_3.c


#include <stdio.h>

int main(void)
{
  double p_ja = 0, p_nej = 0;
  int ja=0, nej=0;
  int val;

  while(1) {
    printf("\n\nÄr det rätt att ta pengar från försvaret och lägga "
       "på vården?\n\n"
       "  1. Ja (%.1lf %%)\n"
       "  2. Nej (%.1lf %%)\n\n"
       "  3. Avsluta programmet\n\n"
       "Välj ett alternativ: ", p_ja, p_nej);

    scanf("%d", &val);
    switch (val) {
    case 1: ja++; break;
    case 2: nej++; break;
    case 3: exit(0);
    }

    if (ja || nej) {
      p_ja = (double)ja/(ja+nej)*100.0;
      p_nej = 100 - p_ja;
    }
  }
}

4. Funktioner

Några viktiga fall att fundera över är:

Exakt hur vi hanterar dessa olika fall spelar inte så stor roll, så länge som vi ser till att dokumentera vårt val i kommentaren till funktionen, så att den som skall använda funktionen förstår hur den skall användas.

g2_4.c


#include <stdio.h>
#include <math.h>

int storsta_primtal(int a, int b);
int primtal(int x);

int main(void)
{
  int a, b, res;
  printf("Skriv in a och b (a <= b): ");
  scanf("%d %d", &a, &b);
  res = storsta_primtal(a,b);
  if (res!=-1)
    printf("Det största primtalet mellan %d och %d är %d.\n",
       a, b, res);
  else
    printf("Det finns inget primtal mellan %d och %d.\n",a,b);

  return 0;
}

/* Ger det största primtalet mellan a och b.
   Funktionen skall anropas med a <= b.
   Om det inte finns något primtal mellan talen returneras -1. */
int storsta_primtal(int a, int b)
{
  int i;
  for(i=b; i>=a; i--)
    if (primtal(i))
      return i;
  return -1;
}


/* Ger värdet sant om x är ett primtal. */
int primtal(int x)
{
  int y;

  for (y=2; y<=sqrt(x); y++)
    if (x%y==0)
      return 0;
  return 1;
}

5. Rekursion

Utskriften blir E D C B A A B C D E.

Det enklaste sättet att se det är att först kolla vad som händer när funktionen anropas med rek1('A'), därefter med rek1('B') osv.

6. Funktionsuppdelning

Svårt att ge ett facit på denna uppgift. Allt är rätt, bara det är väl genomtänkt.

7. Rekursion 2

Det enklaste sättet att lösa detta problem är att först titta på det enklaste fallet n=0 och se vad returvärdet blir då, sedan på det näst enklaste n=1, osv, och därefter försöka skriva en iterativ funktion som ger samma resultat.

g2_7.c


#include <stdio.h>

double rek1(double x, int n);
double iter1(double x, int n);

int main(void)
{
  double x;
  int n;

  for (x=0.2; x<0.7; x+=0.2)
    for (n=-1; n<=5; n+=2)
      printf("rek1(%.1lf,%2d) = %6.2lf  iter1(%.1lf,%2d) = %6.2lf\n",
         x,n,rek1(x,n),x,n,iter1(x,n));
  return 0;
}

double rek1(double x, int n)
{
  if (n <= 0)
    return 1.0;
  else
    return 1.0 + x * rek1(x, n-1);
}

double iter1(double x, int n)
{
  double res=1;
  int i;

  for (i=1; i<=n; i++)
    res = 1.0 + x * res;

  return res;
}

8. Loopar

Här är ett program som skriver ut största talet, minsta talet och medelvärdet. Vi gör det genom att låta en variabel hålla reda på det största tal vi har sätt hittills.

Notera specialfallet för n==0. Det första talet vi läser in är alltid det största och det minsta hittills. (Utan specialfallet skulle programmet inte fungera eftersom storsta och minsta har slumpmässiga värden.)

g2_8a.c


#include <stdio.h>

int main(void)
{
  double tal, storst, minst, sum;
  int n=0;

  while(1) {
    scanf("%lf", &tal);

    if (tal < 0)
      break;

    if (n==0) {
      storst = tal;
      minst = tal;
    }
    else {
      if (tal > storst)
    storst = tal;
      if (tal < minst)
    minst = tal;
    }
    sum += tal;
    n++;
  }

  if (n==0)
    printf("Du har inte skrivit in några tal.\n");
  else
    printf("Största: %.2lf, Minsta: %.2lf, Medel: %.2lf\n", storst, minst,
       sum/n);
  return 0;
}

Här är ett program som skriver ut det näst största talet. Notera att vi måste hålla reda på både det största och det näst största talet, eftersom det största kan bli det näst största.

g2_8b.c


#include <stdio.h>

int main(void)
{
  double tal, storst, nast_storst;
  int n=0;

  while (1) {
    scanf("%lf", &tal);

    if (tal <0)
      break;

    if (tal > storst || n==0) {
      nast_storst = storst;
      storst = tal;
    }
    else if (tal > nast_storst || n==1)
      nast_storst = tal;

    n++;
  }

  if (n<2)
    printf("Du har skrivit in färre än två tal.\n");
  else
    printf("Det näst största talet är %lf.\n", nast_storst);
  return 0;
}

9. Rekursion 3

a)

Eftersom varje anrop av funktionen har sin egen variabel summa så kommer programmet inte att fungera. Anropet summera(7) kommer att ge värdet 7.

b)

Nu är summa global och delas av alla funktionsanrop. Varje funktionsanrop kommer alltså att lägga sitt tal till summan och vi får rätt resultat.

Men det är inte någon speciellt bra lösning - för att resultatet skall bli rätt krävs att den globala variabeln har värdet 0 när funktionen anropas. Om vi anropar summera(7) och summera(3) så kommer det gamla värdet på summa att ligga kvar och förstöra för det andra funktionsanropet. För att få rätt resultat måste vi nollställa summa mellan varje funktionsanrop.

s7 = summera(7);
summa = 0;
s3 = summera(3);

Det är besvärligt och lätt att glömma bort -- en dålig lösning.

c)

g2_9.c


#include <stdio.h>

int summera(int n);

int main(void)
{
  int n;
  scanf("%d", &n);
  printf("summera(%d) = %d\n", n, summera(n));
  return 0;
}

int summera(int n)
{
  if (n<=0)
    return 0;
  else
    return n + summera(n-1);
}


© Niklas Frykholm, februari 1999