Tuesday, December 20, 2016

Pripremni zadaci - veliki brojevi

Napisati program za rad sa dva velika pozitivna cela broja a, b sa standardnog ulaza (sa ne više od 100 cifara) i na standardni izlaz ispisuje a,b (u klasi od po 3 cifre kako smo i navikli da pišemo), a+b, a-b(računa samo ako a>b), a*b, a/b, a%b. Učitavanje para brojeva se prekida unosom karaktera koji nije dekadna cifra. Program napisati tako da maksimalan broj cifara možemo jednostavno povećati ili smanjiti.

OBJAŠNJENJE:
Brojevi koji se ne mogu smestiti u neki od standardnih tipova podataka nazivaju se "veliki". Ukoliko ima potrebu da radi sa takvim brojevima programer mora izgraditi skup funkcija koje nad njima mogu realizovati osnovne aritmetičke operacije.

Primeri:
$40!= 815915283247897734345611269596115894272000000000$ - broj ima 47 cifara.
$2^{128}=340282366920938463463374607431768211456$ - broj ima 38 cifara.

SAVET: Uradite jednu funkciju (npr. za sabiranje velikih brojeva) pa je proverite (testirajte). Nakon toga predjite na drugu funkciju. Prilikom rada na novoj funkciji ne zaboravite da možete koristiti one koje ste već uradili.
Rešenje
#include <stdio.h>
#include <ctype.h>

#define DA 1
#define NE 0
#define MAX 50

typedef int LRez; /* Tip za logicke rezultate LRez*/

// provera da li j dati broj 0
LRez nula (const char a[], int na) 
{ 
   return na==1 && a[0]==0; 
}

// uporedjuje dva broja
// vraca: 0, ako si jednaki
// negativan broj, ako je prvi broj manji
// pozitivan broj, ako je prvi broj veci
int uporedi (const char a[], int na, const char b[], int nb) 
{
   int i;
   if (na != nb) 
      return na - nb;
   for (i= na-1;i>0 && a[i]==b[i]; i--);
   return a[i] - b[i];
}

// kopira niz a u niz b
void kopiraj (const char a[], int na, char b[], int *nb) 
{
   int i;
   for (i=0; i<na; i++) b[i] = a[i];
   *nb = na;
}

// sabira a i b i rezultat stavlja u c
// vraca DA uvek
LRez zbir (const char a[], int na, const char b[], int nb, char c[], int *nc) 
{
   int i, p;
   for (p=i=0; i<na || i<nb; i++)  
   {
      int d = p;
      if (i < na) 
      d += a[i];
      if (i < nb) 
      d += b[i];
      if (d >= 10) 
   { 
      p = 1; 
   d -= 10; 
      } 
   else 
      p = 0;
      c[i] = d;
   }
   if (p) c[i++] = p;
   * nc = i;
   return DA;
}

// oduzima a i b i rezultat stavlja u c
// vraca DA ako je operacija uspela, NE ako nije
LRez razlika (const char a[], int na, const char b[], int nb, char c[], 
              int *nc) 
{
   int i, p;
   if (uporedi (a, na, b, nb) < 0) 
      return NE;
   for (p=i=0; i<na; i++) 
   {
      int d = a[i] + p;
      if (i <nb) 
      d -= b[i];
      if (d < 0) 
   { 
      p = -1; 
   d += 10; 
      } 
   else 
      p = 0;
      c[i] = d;
   }
   for (i=na-1; i>0 && c[i]==0; i--);
   *nc = i + 1;
   return DA;
}

// mnozi a i b i rezultat stavlja u c
// vraca DA uvek
LRez proizvod (const char a[], int na, const char b[], int nb, char c[],
               int *nc) 
{
   if (!nula(a,na) && !nula(b,nb)) 
   {
      int i, j, p;
      *nc = na + nb;
      for (i=0; i<*nc; c[i++]=0);
      for (i=0; i<na; i++)
      for (p=j=0; j<nb || p; j++) 
      {
         int d = c[i+j] + p;
         if (j < nb) 
      d += a[i] * b[j];
         c[i+j] = d % 10; 
   p = d / 10;
      }
      if (c[*nc-1] == 0) (*nc)--;
   } 
   else 
   { 
      *nc = 1; 
   c[0] = 0; 
   }
   return DA;
}

// deli a i b i kolicnik stavlja u c a ostatak u d
// vraca DA ako je operacija uspela, NE ako nije
LRez kolicnik (const char a[], int na, const char b[], int nb, char c[], 
               int *nc, char d[], int *nd) 
{
   int i, j, nn;
   if (nula (b, nb)) 
      return NE; /*ne mozemo deliti nulom*/
   kopiraj (a, na, d, nd);
   nn = na - nb + 1;
   if (nn > 0)  
   {
      *nc = nn; *nd = nb;
      for (i=*nc-1, j=na-nb; i>=0; i--, j--, (*nd)++)
      {
         c[i] = 0;
         while (uporedi (d+j, *nd, b, nb) >= 0) 
   {
            razlika (d+j, *nd, b, nb, d+j, nd);
            c[i]++;
         }
      }
      if (*nc > 1 && c[*nc-1] == 0) 
      (*nc)--;
      while (*nd>1 && d[*nd-1]==0) 
      (*nd)--;
   } 
   else 
   {
      *nc = 1; 
   c[0] = 0;
   }
   return DA;
}

// cita i popunjava broj a i broj cifara n
// vraca DA ako je operacija uspela, NE ako nije
LRez citaj (char a[], int *n) 
{
   char broj[MAX+1]; 
   int i, j;
   scanf ("%s", broj);
   for (i=0; broj[i]; i++)
   if (! isdigit (broj[i])) 
      return NE;
   *n = i;
   for (i=*n-1, j=0; i>=0; a[i--]=broj[j++]-'0');
   while (*n>1 && a[*n-1] == 0) 
      (*n)--;
   return DA;
}

// ispisuje broj a
// sir je sirina - maksimalan broj cifara
// grupe je DA ako zelimo odvajanje u grupe po 3 cifre, NE ako ne zelimo
// ne vraca nista
void pisi (const char a[], int n, int sir, LRez grupe) 
{
   int i;
   printf ("%*s", (sir - n - (n-1)/3*grupe), "");
   for (i=n-1; i>=0; i--) 
   {
      putchar (a[i] + '0');
      if (grupe && i % 3 == 0) 
      putchar (' ');
   }
}

// glavni program - ucitava dva velika broja, racuna njihov zbir, razliku,
// proizvod,kolicnik i ostatak pri deljenju i ispisuje ih. Ako neka od 
// operacija nije definisana ispisuje "ne moze" umesto rezultata
void main () 
{
   char a[MAX], b[MAX], c[2*MAX], d[MAX]; /* brojevi a,b,c,d zadati nizom */
   int na, nb, nc, nd; /*dimenzije nizova a,b,c,d*/
   while (citaj (a, &na) && citaj (b, &nb)) 
   {
      printf ("a= "); 
   pisi (a, na, MAX, DA); 
   putchar ('\n');
      printf ("b= "); 
   pisi (b, nb, MAX, DA); 
   putchar ('\n');
      printf ("a+b= "); 
   zbir (a, na, b, nb, c, &nc);
      pisi (c, nc, MAX, DA); 
   putchar ('\n');
      printf ("a-b= ");
      if (razlika (a, na, b, nb, c, &nc))
      { 
      pisi (c, nc, MAX, DA); putchar ('\n'); 
      }
      else 
      printf ("ne moze\n");
      printf ("a*b= "); 
   proizvod (a, na, b, nb, c, &nc);
      pisi (c, nc, MAX, DA); 
   putchar ('\n');
      printf ("a/b= ");
      if (kolicnik (a, na, b, nb, c, &nc, d, &nd)) 
   {
         pisi (c, nc, MAX, DA); 
   putchar ('\n');
         printf ("a%%b= "); 
   pisi (d, nd, MAX, DA); 
   putchar ('\n');
      } 
   else 
      printf ("ne moze\n");
      putchar ('\n'); 
   }
}

Friday, December 16, 2016

Funkcije

Funkcije u programiranju su liče na funkcije u matematici (preslikavaju date vrednosti u rezultat) ali su dosta opštije. Funkcije u programiranju predstavljaju programske celine koje obavljaju odredjeni deo posla. Ideja je da se jednom opiše nešto što je često potrebno (npr. izračunavanje faktorijela, izračunavanje površine trougla, kvadrata, kruga, ...) pa da se koristi kada god zatreba. Dakle imamo dva važna mesta:

  • Definicija funkcije (opis šta treba da uradi)
  • Poziv funkcije (korišćenje)
DEFINICIJA funkcije se sastoji od naslova i tela

Naslov funkcije opisuje ime funkcije, tipove i imena podataka koje preuzima (ulazni parametri) i tip podatka koji vraća (rezultat).

Primer: 
long faktorijel(int n)
je naslov funkcije koja preuzima jedan ceo broj (n), zove se faktorijel i vraća ceo broj kao rezultat.

Telo funkcije opisuje komande koje treba izvršiti nad ulaznim podacima da bi se dobio rezultat. Rezultat se vraća komandom
return izraz;
gde je vrednost izraza rešenje (izlazni podatak funkcije).

Dozvoljeno je i da funkcija nema nikakav rezultat. U tom slučaju tip rezultata je void

Primer:
void ispisi(int x, float y)
Ova funkcija preuzima dva podataka - ceo broj x i decimalni broj y i ne vraća nikakav rezultat. Naziv funkcije je ispisi.

POZIV funkcije: funkcija se poziva navodjenjem imena i promenljivih ili izraza čije se vrednosti koriste.

Primeri: 
a = faktorijel(7);
Poziva funkciju sa imenom faktorijel i argumentom 7 (vrednost za n). Rezultat funkcije se upisuje u promenljivu a.

ispisi(243, -87.51);
Poziva funkciju sa imenom ispisi i argumentima 243 (vrednost za x) i -87.51 (vrednost za y). Funkcija nema rezultat.

U telu funkcije dozovoljene su sve komande koje su inače dozvoljene u programu ali ima i nekih novih mogućnosti. Funkcija može pozivati samu sebe (sa izmenjenim ulaznim parametrima) i to se u programiranju zove rekurzija a takva funkcija - rekurzivna funkcija.

QUICK sort je primer rekurzivne funkcije. 

Treba imati na umu da rekurzivne funkcije traže više memorijskog prostora računara (svaki rekurzivni poziv zahteva od računara čuvanje trenutnih vrednosti promenljivih i vraćanje tih vrednosti po završetku rekurzivnog poziva) pa ih ne možemo uvek koristiti na takmičenju - zbog ograničenja.

PRIMER 
Napisati funkciju koja za dati prirodan broj određuje zbir cifara.
a) nerekurzivno (iterativno)
b) rekurzivno
Rešenje
a) iterativno
int zbirCifara(int broj)
{
    int zbir=0;
    while(broj!=0)
    {
        zbir += (broj%10);
        broj /= 10;
    }
    return zbir;
}

b) rekurzivno
int zbirCifara(int broj)
{
    if(broj==0)
        return 0;
    else
        return broj%10 + zbirCifara(broj/10);

}

Korišćenje (poziv) funkcije
int main()
{
    int broj, zbir;
    scanf("%d", &broj);
    zbir = zbirCifara(broj);
    printf("Zbir cifara broja %d je %d",broj, zbir);
    return 0;
}

Saturday, December 3, 2016

Osnovni algoritmi za rad sa nizovima - treći deo

Od često korišćenih postupaka u radu sa nizovima treba još da upoznamo SORTIRANJE tj. promenu redosleda elemenata tako da budu složeni prema nekom kriterijumu - najšešće rastuće ili opadajuće.

Postoji veći broj algoritama kojima se ovo postiže a razlikuju se po brzini izvršavanja. Jednostavniji za pisanje su uglavnom nedovoljno brzi ako niz ima veliki broj elemenata. Pokazaćemo nekoliko najpoznatijih algoritama. Klikom na link odgledajte demonstraciju algoritma a potom pogledajte program koji radi po tom algoritmu - sortira se niz a koji ima na elemenata u rastući poredak. QUICK sort je najbrži od navedenih i sortira niz a od pozicije beg do pozicije end. Biće vam jasniji kada objasnimo rekurziju (u sledećem članku).

SELECTION sort

for(i=0; i<na-1; i++)
{
    jmin = i;
    for(j=i+1; j<na; j++)
    {
        if(a[j]<a[jmin])
        jmin=j;
    }
    t = a[jmin];
    a[jmin]= a[i];
    a[i] = t;
}

BUBBLE sort

do
{
    promena = 0;
    for (i = 0; i < na - 1; i++)
    {
        if (a[i] > a[i + 1])
        {
            promena = 1;
            t = a[i];
            a[i] = a[i+1];
            a[i+1] = t;
        }
    }
}
while (promena);

QUICK sort

void quickSort(int a[], int beg, int end)
{
    int i, t, wall;
    if (end == beg)
        return;
    wall = beg;
    for (i = beg; i < end; i++)
    {
        if (a[i] < a[end])
        {
            t = a[wall];
            a[wall] = a[i];
            a[i] = t;
            wall++;
        }
    }
    t = a[wall];
    a[wall] = a[end];
    a[end] = t;
    if (wall > beg)
        quickSort(a, beg, wall - 1);
    if (wall < end)
        quickSort(a, wall + 1, end);
}