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);
}

Tuesday, November 29, 2016

Osnovni algortimi za rad sa nizovima - drugi deo

Preostalo je da objasnimo
  • izdvajanje elemenata niza koji ispunjavaju neki uslov u drugi niz
  • pretraživanje niza
  • promena redosleda elemenata niza (sortiranje)
IZDVAJANJE elemenata koji ispunjavaju neki uslov u novi niz radimo tako što koristimo posebnu promenljivu za praćenje koliko je elemenata novog niza popunjeno. Na primer, ako želimo da odvojimo pozitivne članove niza a (koji ima na elemenata) u niz b onda to radimo ovako

nb=0;
for(i=0; i<na; i++)
   if(a[i]>0)
   {
      b[nb]=a[i];
      nb++;
   }

Zamenom uslova (a[i]>0) nekim drugim postižemo da se odvajaju drugačiji elementi niza a.

PRETRAŽIVANJE niza ima više varijatni:
  • tarženje jednog podatka u datom nizu podataka
  • traženje jedog niza (podniza) u drugom nizu  
Traženje jednog podatka se može realizovati proverom svakog elementa (to zovemo linearnim pretraživanjem) ili određivanjem dela sortiranog niza u kome se mora nalaziti zadati element (to zovemo binarnim pretraživanjem).


Linearno je, naravno, sporije ali ne zahteva nikakav poseban niz i može se koristiti ako niz nema mnogo elemenata. Provera da li se u datom nizu a sa na elemenata pojavljuje dati broj x radi se ovako

nadjen=0;
for(i=0; i<na; i++)
   if(a[i]==x)
   {
      nadjen = 1;
      break;
   }

Rezultat: ako posle gornjeg dela programa promenljiva nadjen ima vrednost 0 element nije pronadjen a ako ima vrednost 1  onda je pronadjen.
Sličan postupak može se koristiti i za određivanje pozicije pojavljivanja traženog elemenenta u nizu (umesto "nadjen =1" treba stavitit "nadjen = i"), ili broj pojavljivanja traženog elementa u nizu (umesto "nadjen = 1" treba staviti "nadjen++")

Binarno zahteva da niz bude sortiran rastuće (od manjih ka većim) ili opadajuće (od većih ka manjim) i sastoji se u tome da se traženi element uporedjuje da elementom koji je približno u sredini datog niza. Može se desiti jedna od tri situacije

  • traženi element je isti kao taj član niza koji analiziramo => element je pronadjen i postupak se završava
  • traženi element je manji od člana niza koji analiziramo => traženje elementa nastavljamo u prvoj polovini niza (ako je sortiran rastuće tj. drugoj ako je sortiran opadajuće)
  • traženi element je veći od člana niza koji analiziramo => traženje elementa nastavljamo u drugoj polovini niza (ako je sortiran rastuće tj. prvojoj ako je sortiran opadajuće)
Ako na kraju dođemo do niza koji nema elemenata onda je postupak okončan i element nije pronadjen. Ovakav način je mnogo brži. Program koji traži dati element x binarno u nizu a sa na elemenata (koji je sortiran rastuće) izgleda ovako:

nadjen=0;

pocetak=0;
kraj=na-1;
while(pocetak<=kraj)
{
    sredina=(pocetak+kraj)/2;
    if(a[sredina]==x)
    {
        nadjen=1;
        break;
    }
    if(a[sredina]<x)
        pocetak=sredina+1;
    else
        kraj=sredina-1;
}

Traženje jednog niza u drugom je komplikovanije naročito ako su nizovi dugački (zbog velikog broja provera koje treba obaviti da bi se došlo do rešenja). Klasično rešenje: ako tražimo niz b u nizu a onda tražimo prvo slovo od b u a pa kada ga nadjemo pokušavamo da nađemo i ostala. Ako uspemo - postupak je završen. Ako ne uspemo tražimo sledeću pojavu prvog slova b u a... Ako stignemo do kraja a onda niz b nije pronadjen. Rezultat je pozija u a na kojoj je nadjen niz b. Program koji koristi ovaj algoritam izgleda ovako:


for(i=0; i<na; i++)
{
   if(a[i]==b[0])
      for(j=1; j<nb; j++)
          if(a[i+j]!=b[j]) break;
      if(j==nb)
          printf("%d", i);
}

Ovaj program može da ima dugo vreme izvršavanja jer se prilikom neuspeha vraća na mesto od kojeg je počeo pa pokušava sa sledećim što je vrlo često nepotrebno. Algoritam KMP (Knut-Moris-Prat) predlaže bolje rešenje - pre traženja konstruisati pomoćni niz (kmpNext) koji sadrži informaciju na kojoj poziciji se nalazi sledeća pojava slova do koga smo stigli. 


Sortiranje ćemo ipak ostaviti za sledeći članak.

Thursday, November 17, 2016

Takmičnje MATF 2016++

U decembru će na Matematičkom Fakultetu u Beogradu biti organizovano timsko takmičenje iz programiranja. Timovi moraju imati 3 člana. Takmičenje traje 4 sata i članovi tima rešavaju zajedno date algoritamske zadatke na jednom računaru. Nema korišćenja literature ili Interneta. Dozvoljeni programski jezici su Pascal, C, C++ i Java. Rešava se ukupno 8 zadataka.

Prijavite tim kod prof. Varnice. Potrebno je ime tima i imena članova.

Istovremeno sa ovim takmičenjem održava se i pojedinačno takmičenje iz matematike ali samo za učenike četvrtog razreda. Rešava se 6 zadataka za 3 sata.

Učenici zainteresovani za ovo takmičenje takođe treba da se prijave kod prof. Varnice.

Rok za prijavljivanje je 27. novembar 2016 a takmičenje se održava 10. decembra.

Organizator obećava vredne nagrade za pobednike.

Prošlogodišnje zadatke možete pogledati ovde.

Wednesday, November 16, 2016

Osnovni algoritmi za rad sa nizovima

Prilikom rada sa nizovima potrebno je poznavanje nekih standardnih postupaka (algoritama). To su:

  • prebrojavanje elemenata niza koji ispunjavaju neki uslov
  • sabiranje ili množenje elemenata niza koji ispunjavaju neki uslov (ili svih)
  • izdvajanje elemenata niza koji ispunjavaju neki uslov u drugi niz
  • pretraživanje niza
  • promena redolsleda elemenata niza

Prvi razlog je što potrebni prilikom rešavanja drugih zadataka a drugi jer se u njihovim rešenjima kriju zanimljive ideje koje se mogu iskoristiti u drugim situacijama.

Osnovni princip vecine ovih postupaka je da se analizira jedan po jedan element niza. U nekim slučajevima anliziraju se dva po dva elementa.

PREBROJAVANJE elemenata koji ispunjavaju neki uslov svodi se na uporedjivanje elementa i beleženje broja situacija kada je uslov bio ispunjen.

Dakle, ako želimo da prebrojimo pozitivne elemente niza x koji ima n elemenata treba napisati ovakav program (programeri kažu i "kod")

broj = 0;
for(i=0; i<n; i++)
   if(x[i]>0)
      broj++;

Zamenom uslova u if naredbi dobijamo program koji broji elemente koji ispunjavaju taj drugačiji uslov.

SABIRANJE svih elemenata niza x koji ima n elemenata svodi se na dodavanje jednog po jednog elementa ukupnom zbiru. Evo programa (tj. koda)

zbir = 0;
for(i=0; i<n; i++)
   zbir += x[i];

Ako želimo da dobijeo zbir npr. samo parnih brojeva onda u gornji program treba dodati if naredbu. Dobija se ovakav program:

zbir = 0;
for(i=0; i<n; i++)
   if(x[i] % 2 == 0)
      zbir += x[i];

Ako je potrebno pomnožiti sve elemente niza onda je ideja slična - množenje proizvoda jednim po jednim elementom. Evo programa

proizvod = 1;
for(i=0; i<n; i++)
   proizvod *= x[i];

Naravno i ovde je moguće dodati uslov tako da se množe samo elementi koji ispunjavaju taj uslov. Ovo je vrlo slično sabiranju pa ostavljam čitaocu da pokuša sam. Ako bude nedoumica pišite kao komentar.

Treba imati na umu da zbir, a naročito proizvod, pozitivnih brojeva može da bude jako veliki broj pa ako su sabirci (činioci) tipa int onda zbir (proizvod) mora biti deklarisan kao long a nekada ni to nije dovoljno pa se moraju osmisliti posebni algoritmi za rad sa tim brojevima (rad sa velikim brojevim je tema o kojoj ćemo govoriti posebno).

O izdvajanju elemenata, pretraživanju i promeni redosleda (sortiranju) govorićemo u sledećem članku.

Oblasti za okružno takmičenje

Na okružnom takmičenju potrebno je poznavati
  • Jednostavne strukture podataka (slogovi, nizovi, stringovi, matrice, liste, skupovi)
  • Jednostavni matematički postupci (sumiranja, brojni sistemi, prosti brojevi, Euklidov algoritam)
  • Rekurzija, bektrek, kombinatorna prebrajanja
  • Operacije sa velikim brojevima
  • Elementarni algoritmi za sortiranje (selection sort, insertion sort, bubble sort, counting sort)
  • Predstavljanje osnovnih geometrijskih objekta (tačke, duži, prave, kruznice), jednostavni postupci nad njima (nalaženje preseka, udaljenosti, uglova), analitička geometrija
Ovo ćemo sve obraditi u narednim sedmicama - nešto kroz blog nešto na času sekcije.

Saturday, November 12, 2016

Eratostenovo sito za odredjivanje svih prostih brojeva manjih od datog broja

Prosti brojevi su često korisni za rešavanje različitih tipova zadataka pa je dobro imati algoritam koji može da za kratko vreme generiše sve takve brojeve manje od neke granice.

Takav algoritam pripisuje se Eratostenu (3 v. p.n.e.) i zove se Eratostenovo sito. Princip je sledeći:
- zapisati sve prirodne brojeve od 2 do gornje granice
- uzeti prvi nezaokružen broj, zaokružiti ga i precrtati sve brojeve koji su njime deljivi
- ponavljati gornji korak dok ne ostane nijedan nezaokružen  i neprecrtan.
- zaokruženi brojevi su rešenje zadatka - prosti manji od date gornje granice

PROGRAM u C-u:

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

#define MAX 1000

int main()
{
    int a;
    int x[MAX];
    int broj;
    int i;
    scanf("%d", &a);
    for(i=2; i<=a; i++)
        x[i]=1;
    broj = 2;
    while(broj<=sqrt(a))
    {
        for(i=broj*broj; i<=a; i+=broj)
            x[i]=0;
        do
            broj++;
        while(x[broj]==0);
    }
    for(i=2; i<=a; i++)
        if(x[i]==1)
            printf("%d ",i);
    return 0;
}

Napomena: Program radi najviše do broja 1000. Zamenom broja 1000 sa većim brojem (u redu #define MAX 1000) možemo povećati tu gornju granicu.

Rešenje u C++:

#include <iostream>
#include <cmath>

using namespace std;

int main()
{
    int a;
    int broj;
    int i;
    cin >> a;
    int x[a+1];
    for(i=2; i<=a; i++)
        x[i]=1;
    broj = 2;
    while(broj<=sqrt(a))
    {
        for(i=broj*broj; i<=a; i+=broj)
            x[i]=0;
        do
            broj++;
        while(x[broj]==0);
    }
    for(i=2; i<=a; i++)
        if(x[i]==1)
            cout << i << " ";
    return 0;
}

Euklidov algoritam za određivanje NZD

Ponekad je korisno odrediti NZD dva prirodna broja. Postupak koji je pokazan u osnovnoj školi je ispravan ali nedovoljno brz ako je reč o jako velikim brojevima.

Postupak koji se pripisuje Euklidu (4 v.pn.e.) zasniva se na činjenici da ako je a>b važi
NZD(a,b) = NZD(b,a mod b), gde je a mod b ostatak pri celobrojnom deljenju a sa b. Postupak se završava kada je b=0 i rešenje je broj a.

Ilustracija: NZD(1240, 860) = NZD(860, 380) = NZD(380, 100) = NZD(100, 80) = NZD(80, 20) = NZD(20, 0) = 20

PROGRAM u C-u:

#include <stdio.h>

int main()
{
    int a,b;
    scanf("%d %d", &a, &b);
    if(a<b)
    {
        int x=a;
        a=b;
        b=x;
    }
    while(b)
    {
        int ostatak = a%b;
        a = b;
        b = ostatak;
    }
    printf("%d",a);
    return 0;
}

Pošto je neke operacije jednostavnije opisati u C++ evo rešenja i u tom jeziku:

#include <iostream>

using namespace std;

int main()
{
    int a,b;
    cin >> a >> b;
    if(a>b)
    {
        int x=a;
        a=b;
        b=x;
    }
    while(b)
    {
        int ostatak = a%b;
        a = b;
        b = ostatak;
    }
    cout << a;
    return 0;

}

Napomenimo da se NZS može računati na osnovu svojstva da je proizvod NZD i NZS za dva broja isti kao i proizvod tih brojeva.

Ilustracija: NZD(6,8) x NZS(6,8) = 2 x 24 = 48 = 6 x 8

Nizovi

Često u zadacima nije dovoljno korišćenje nekoliko promenljivih nego su potrebne stotine, hiljade ili čak milioni promenljivih da bi se došlo do rešenja. U tom slučaju važno je koristiti složenije strukture podataka kao što su nizovi i stringovi (ima ih još, ali o tome ćemo kasnije)

Niz je grupa podataka istog tipa (npr. svi u grupi su celi brojevi). Da bi ga koristili prvo treba deklarisati (najaviti da ćemo koristiti). Primer: u programu radimo sa najviše 100  celih brojeva:

int a[100];

Ovde piše da ćemo raditi sa nizom koji se zove a i ima 100 celih brojeva. Reč je o 100 celobrojnih promenljivih koje se koriste tako što se navede ime niza i redni broj člana. Pri tom treba voditi računa da redni brojevi idu od 0. Primeri

a[0]     je 1. član niza
a[1]     je 2. član niza
a[2]     je 3. član niza
...
a[99]     je 100. član niza

Pošto vrlo često želimo da pregledamo, proverimo, učitamo, ispišemo, ... (jednom rečju obradimo) sve članove niza, često se niz koristi sa naredbama za ponavljanje. Primeri:

  • Učitavanje n elemenata niza a
for( i = 0; i < n; i++ )
   scanf("%d", &a[i]);

  • Sabiranje n elemenata niza a
s = 0;
for( i = 0; i < n; i++ )
   s = s + a[i];

  • Ispisivanje n elemenata niza a unazad (od posledenjeg do prvog)
for( i = n - 1; i >= 0; i-- )
   printf("%d ", a[i]);

  • Određivanje najmanjeg elementa u nizu
min = a[0];
for( i = 1; i < n; i++ )
   if( a[i] < min )
      min = a[i];

O složenijim algoritmima za obradu nizova biće više reči kasnije.

Ako niz sadrži znakove onda je reč o stringu.  I o ovome će kasnije biti više reči - kada budemo govorili o algoritmima za rad sa tekstom.


Razgranati i ciklični programi

Osim do sada rečenog u programiranju se koriste joše dve tipa veoma korisnih elelmentarnih koraka (ove korake programeri zovu komande ili naredbe):

  • naredba provere uslova   if(uslov) naredba1 else naredba2;
  • naredba ponavljanja    for(init; uslov; promena) naredba;
Prva (if) izračunava vrednost izraza uslov i u zavisnosti od rezultata izvršava naredba1 (ako rezultat nije 0) ondnosno naredba2 (ako je rezultat 0)

Druga  (for) izračunava vrednost init izraza, potom proverava da li je vrednost uslova različita od 0. Ako nije - prelazi se na sledeću naredbu (posle for). Ako jeste izvršava se naredba pa promena i opet se proverava uslov. AKo nije 0 opet se radi naredba - promena - provera uslova. I tako sve dok uslov ne postane 0.

Osim for postoje još i 
while(uslov) 
   naredba;  
i     
do 
   naredba 
while(uslov); 

U obe varijante proverava se da li je vrednost uslova različita od 0 pa se onda izvrši naredba pa opet uslov pa opet naredba i tako sve dok uslov ne bude imao vrednost 0. Jedina razlika je što kod prvog oblika prvo proverava uslov pa se izvršava naredba a kod drugog se prvo izvršava naredba pa se proverava uslov.

Ovde vredi pomenuti da postoje još neke naredbe koje utiču na izvršavanje naredbi ponavljanja (programeri često koriste i izraz naredbe ciklusa) kao što su break i continue ali to možete i sami pronaći u literaturi. Ako vam je lakše postavljajte pitanje ovde u obliku komentara pa ću objašnjavati.

Wednesday, November 9, 2016

Takmičenje iz programiranja za devojke

U svetu programera primetna je razlika između polova - žena je mnogo manje od muškaraca. U cilju izjednačavanja polova jedan od sajtova koji omogućava online takmičenja u programiranju organizuje takmičenje isključivo za ženski pol - CODE LIKE A WOMEN.

Takmičenje traje od subote 19. novembra do nedelje 20. novembra. Detalje ovog takmičenja (i prijavljivanje za njega) možete pogledati ovde. Proširite ovu informaciju ženskim osobama koje su dobre u programiranju.

Za momke predlažem da pogledaju isti ovaj sajt jer na njemu ima puno zanimljivih zadataka različite težine koji su dobri za vežbu. Na njemu se možete i takmičiti (naravno ne na gore pomenutom takmičenju nego na nekom drugom). Pogledajte link.

Srećno!

Tuesday, November 8, 2016

Takmičenje DABAR

"Dabar" je međunarodno takmičenje iz računarske i informatičke pismenosti.

Održava se u dva kruga - školsko i državno.

Školsko će biti održano između 14. i 18. novembra.

Najbolji učesnici školskog takmičenja biće pozvani na državno koje će biti održano 25. 2. 2017.

Detaljan pravilnik takmičenja možete pogledati ovde.

Primer zadataka za 1. i 2. razred možete pogledati ovde a za 3. i 4. ovde.

Sunday, November 6, 2016

Zadaci u kojima se koristi matematička formula

U nekim zadacima problem je programerski jednostavan:
  • od korisnika se preuzmu podaci (kažemo "učitaju se ulazni podaci")
  • primeni se jedna ili više matematičkih formula i 
  • rezultat se prikaže korisniku (kažemo "prikažu se izlazni podaci")
Pogledajte primer zadatka.

Tekst zadatka

Sportista se na početku treninga zagreva tako što trči po ivicama pravougaonog terena dužine d i širine s. Napisati program kojim se određuje koliko metara pretrči sportista dok jednom obiđe teren.

Ulaz

U prvoj liniji standardnog ulaza se nalazi celobrojna vrednost d, a u sledećoj liniji celobrojna vrednost s (0<d1000<s100) koje redom predstavljaju dužinu i širinu terena izraženu u metrima.

Izlaz

Jedan ceo broj koji predstavlja broj metara koje pretrči sportista dok jednom obiđe teren.

Primer

Ulaz

50 
25

Izlaz

150 

REŠENJE

Naš program treba da prihvati ("učita") vrednosti promenljivih d i s, izračuna koliko metara pretrči i prikaže taj broj.


1) Za učitavanje koristimo komandu scanf u sledećem obliku
scanf("%d %d",&d, &s);
Ova komanda saopštava računaru da treba da učita dva cela broja (%d označava jedan ceo broj) i da njihove vrednosti dodeli promenljivima d i s, redom.
Napomena: obavezno je korišćenje znaka & (ampersand) pre imena promenljivih da bi program bio pravilno shvaćen. Kasnije ćemo objasniti zašto.

2) Izračunavanje u ovom primeru je jednostavno i svodi se na primenu formule za obim pravougaonika
P = 2 * (d + s);
Napomena: * je oznaka za operaciju množenja u C-u.

3) Prikaz rezultata se svodi na prikaz vrednosti promenljive P što možemo postići printf komandom koja ima sledeći oblik
print("%d", P);

Da bi program bio kompletan moramo dodati uvodni deo i deklaracije promenljivih u glavni deo. Kompletan program izgleda ovako:

#include <stdio.h>

int main() 

   int d,s,P; 
   scanf("%d %d", &d, &s); 
   P = 2*(d+s); 
   printf("%d",P); 
   return 0; 
}

Koristeći do sada rečeno pokušajte da rešite ove zadatke.

Thursday, November 3, 2016

Programski jezik C

Program u programskom jeziku C ima sledeću strukturu


Ovaj primer predstavlja prostiji C program koji ima samo dva dela:
- UVODNI DEO (opisivanje biblioteka funkcija koje ćemo u glavnom delu koristiti) i
- GLAVNI DEO - glavnu funkciju (main)

Izmedju ova dva dela može biti deo u kome opisujemo pomoćne podatke i funkcije koji će biti korišćeni u glavnom delu. Ovo ćemo pokazati kasnije.

UVODNI DEO sadrži (između ostalog) jedan ili više redova oblika

#include <abc>

gde je abc naziv datoteke (fajla) koji sadrži opis funkcija koje možemo (ali nismo obavezni) da koristimo u glavnom delu. Nekoliko primera:

  • stdio.h  - ako koristimo funkcije za učitavanje i prikaz podataka. U ovom primeru koritimo funkcije scanf za učitavanje podataka i printf za prikaz rezultata. 
  • math.h  - ako koristimo složenije matematičke funkcije (stepenovanje, korenovanje, logaritmovanje, trigonometrijske funkcije, ...)
  • string.h  - ako koristimo funkcije koje obradjuju stringove (nizove znakova)
  • ...
GLAVNI DEO je centralno mesto našeg programa - tu se navode koraci uputstva po kome računar radi. Ovaj deo započinjemo deklaracijom promenljivih koje ćemo koristiti - onih koje unosi korisnik našeg programa, pomoćne promenljive (za međurezultate) i rezultata izračunavanja.

Deklaracija podrazumeva navođenje tipa podataka i naziva promenljive. Tipovi koje ćemo za sada koristiti su celi brojevi (int, short, long, ...) i decimalni brojevi (float, double, long double, ...). Ime promenljive je bilo koji niz znakova koji počinje slovom a sadrži slova, cifre i znak za podvlačenje (_).

Primeri:
int sumaBr;
double prosek_ocena;
long a1,a2,a3;

Svaki korak se završava znakom ; 

Nakon deklaracija slede koraci koje treba uraditi da bi se rešio postavljeni problem. U programskom jeziku C možemo koristiti sledeće elementarne korake:
  • učitavanje (preuzimanje) datih podatka (ulazni podaci) - printf
  • prikazivanje rezultata izračunavanja - scanf
  • izračunavanje vrednosti izraza i čuvanje vrednosti u nekoj od promenljivih - prom=izraz
  • provera ispunjenosti uslova - if
  • ponavljanje grupe koraka do ostvarenja datog uslova - for, while i do
U sledećem članku objasnićemo detalje prve tri od opisanih komandi što to će biti dovoljno za rešavanje jednostavnijih zadataka. Zatim prelazimo na složenije komandi i složenije zadatke.