Sunteți pe pagina 1din 7

Programarea Calculatoarelor: Probleme rezolvate

Problema 1a Definim Ank = n * An-1k-1 pentru 0 < k = n. Completati relatia de re


curenta, pentru 0 = k = n, stiind ca Ank e numarul de aranjamente a k elemente s
electate din n. Scrieti o functie care calculeaza Ank si tipariti valoarea A53.
Problema cere doar completarea recurentei date cu un caz de baza: diferenta dint
re domeniul de valori pentru care relatia e data si pentru care se cere completa
ta e k = 0. Valoarea pentru cazul de baza o obtinem din definitia n cuvinte a ter
menului: exista un singur mod n care putem alege 0 elemente din n. Cu aceasta, fu
nctia si programul devin:
#include <stdio.h>
unsigned A(unsigned n, unsigned k)
{
return k == 0 ? 1 : n * A(n-1, k-1);
}
int main(void)
{
printf("%u\n", A(5, 3));
return 0;
}
Problema 1b Definim cos x = 2 * cos2 x/2 - 1 iar pentru x < 0.01 (n radiani) apro
ximam cos x cu 1 - x2/2. Scrieti o functie care calculeaza cosinusul dupa relati
ile de mai sus. Folositi-o pentru a calcula cos p/3.
Folosim un alt nume pentru functia cosinus, pentru a evita confuzia cu functia s
tandard de biblioteca cos. Recursivitatea se transcrie direct din relatia data n
enunt. S-ar putea scrie:
double cos2(double x)
{
return x < 0.01 ? 1 - x*x/2 : 2*cos2(x/2)*cos2(x/2) - 1;
}
Aceasta varianta calculeaza de doua ori cos2(x/2), ceea ce prin repetitie recurs
iva devine deosebit de ineficient. Folosim o variabila pentru a retine aceasta v
aloare. n plus, rescriem functia cu cazul de baza pentru x < 0.01 n modul, ea func
tionnd astfel si pentru orice numar real:
#include <math.h>
#include <stdio.h>
double cos2(double x)
{
if (fabs(x) < 0.01) return 1 - x*x/2;
else {
double c = cos2(x/2);
return 2*c*c - 1;
}
}
int main(void)
{
printf("%f\n", cos2(3.1415926/3));
return 0;
}
Problema 2 Scrieti o functie care calculeaza prin aproximari succesive expresia
1(x + 1/(x + 1(... + 1/x))) pna cnd diferenta a doua aproximari succesive e cel mu
lt 10-6. Calculati si tipariti o aproximatie a expresiei pentru x = 4.

n problema se da un termen infinit (cu un numar infinit de


nspirat din problema cu fractii continue din laboratorul 3.
ar o structura recursiva: el e de forma 1/(x + ...) unde n
nul nsusi. Mai riguros putem scrie: t1=1/x, tn=1/(x+tn-1),
r pentru n > 0 daca consideram t0 = 0.

aparitii ale lui x), i


Termenul are n mod cl
loc de ... apare terme
pentru n > 1, sau chia

Deci, pentru a rezolva problema vom calcula succesiv termenul urmator pornind de
la termenul curent, ca aproximatii pentru termenul infinit. Daca diferenta dint
re cei doi nu depaseste precizia ceruta, procesul se opreste si returnam aproxim
atia curenta, altfel continuam cu calculul nca unui termen. Daca scriem solutia r
ecursiv, avem nevoie de termenul (aproximatia) curent(a) ca parametru. Valoarea
x nu se modifica, ea ar putea fi un parametru sau chiar o constanta n program.
double limrec(double x, double ultim) // ultimul termen calculat
{
double urm = 1 / (x + ultim); // termenul (aproximarea) urmatoare
return fabs(urm-ultim) <= 1e-6 ? urm : limrec(x, urm);
}
Functia va fi apelata initial cu t0 = 0 ca valoare pentru aproximatia curenta ul
tim: limrec(4, 0).
Solutia iterativa urmeaza aceiasi pasi, fiind doar exprimata cu elemente sintact
ice diferite. Valoarea ultim devine si ea variabila locala n functie, iar fiecare
iteratie a ciclului avanseaza cu un termen, actualiznd ultim = urm, ceea ce n caz
ul recursivitatii se facea prin transmiterea de parametru. Ciclul se continua att
a timp ct diferenta n valoare absoluta dintre ultimii doi termeni depaseste preciz
ia ceruta.
double limit(double x)
{
double ultim, urm = 0;
do {
ultim = urm;
urm = 1/(x + ultim);
} while (fabs(urm-ultim) > 1e-6);
return urm;
}
Analiznd matematic problema putem gasi usor solutia exacta, nlocuind n recurenta si
termenul curent si cel urmator cu limita: lim = 1/(x + lim), o ecuatie de gradu
l II n lim. Pentru x > 0 ne intereseaza solutia pozitiva (-x + sqrt(x2+4))/2. Put
em remarca de asemenea ca termenii sirului alterneaza n jurul limitei. Aceste obs
ervatii nu sunt necesare pentru a rezolva problema prin program; putem sa le fol
osim nsa pentru o mai buna ntelegere si pentru a ne verifica.
Problema 3a Cititi un text pna la sfrsitul intrarii si afisati numai caracterele a
lfanumerice si de spatiu alb.
Problema e de tipul celor mai simple prelucrari de texte: caracterele se citesc
independent si se prelucreaza independent: ce facem cu un caracter depinde doar
de tipul sau, nu si de unde se afla el n sirul de intrare, de caracterele dinaint
ea lui sau de dupa el.
ncepem cu un ciclu simplu care citeste toate caracterele pna la sfrsit de fisier fa
ra sa faca nimic cu ele.
int c;
while ((c = getchar()) != EOF);
Acest tipar cu atribuire si test n conditie este util cnd nainte de fiecare iterati
e din ciclu (inclusiv prima) trebuie sa citim un nou caracter pentru a putea sal testam.
ATENTIE! Nu uitam nsa parantezele n jurul expresiei (c = getchar()). Ele sunt nece
sare pentru a efectua nti atribuirea si apoi a testa rezultatul atribuit, si nu in
vers (ceea ce ar atribui lui c valoarea adevarat (1) sau fals (0) a testului)!

Ciclul de mai sus are corp vid reprezentat de instructiunea vida punct-virgula d
e dupa paranteza conditiei ciclului, deoarece n afara de citire si test (ambele n
conditie) nu face nimic.
ATENTIE! Nu punem neintentionat punct-virgula dupa paranteza conditiei din while
, dect atunci cnd avem nevoie de un ciclu cu corp vid!
Continuam sa construim solutia nlocuind corpul vid al ciclului cu o instructiune
care tipareste caracterul citit, folosind functia putchar:
int c;
while ((c = getchar()) != EOF)
putchar(c);
Avem astfel un ciclu care tipareste toate caracterele citite, pna la sfrsitul intr
arii. Dorim nsa tiparirea doar a anumitor caractere, deci plasam tiparirea ntr-o i
nstructiune if cu conditia dorita:
#include <stdio.h>
int main(void)
{
int c;
while ((c = getchar()) != EOF)
if (isalnum(c) || isspace(c)) putchar(c);
return 0;
}
Daca preferam sa nu citim si testam cu aceeasi conditie complexa, rescriem, n ace
st caz ciclul are doua instructiuni, a doua citeste urmatorul caracter:
int c = getchar(); // primul caracter
while (c != EOF) {
if (isalnum(c) || isspace(c)) putchar(c);
c = getchar(); // urmatorul caracter
}
Problema 3b Cititi un text pna la sfrsitul intrarii si afisati-l transformnd litere
le mari n litere mici si reciproc.
Ca si n problema anterioara, fiecare caracter e prelucrat independent de celelalt
e. Pornim cu acelasi tipar:
while ((c = getchar()) != EOF)
// aici vine prelucrarea caracterului
Ne folosim de functiile islower/isupper pentru test si de functiile tolower/toup
per pentru transformare. Ne avantajeaza faptul ca acestea din urma nu modifica c
aracterele care nu sunt litere, respectiv sunt deja litere mici/mari. De aceea n
prelucrare e suficient un singur test:
putchar(islower(c) ? toupper(c) : tolower(c));
Daca caracterul e litera mica, se tipareste litera mare corespunzatoare; daca nu
(deci e litera mare sau nu e litera), se tipareste rezultatul lui tolower, deci
litera mica, sau caracterul nemodificat. Daca preferam, putem sa folosim if n lo
cul expresiei conditionale:
#include <ctype.h>
#include <stdio.h>
int main(void)
{
int c;
while ((c = getchar()) != EOF)
if (islower(c)) putchar(toupper(c));
else putchar(tolower(c));
return 0;
}
ATENTIE! tolower si toupper sunt functii, deci nu si pot modifica argumentele, ca
re sunt transmise prin valoare. Instructiunea toupper(c); nu are deci nici un ef
ect! Functia returneaza valoarea transformata, dar ea nu e folosita niciunde (pr

in atribuire, n alt calcul, sau prin tiparire), si se pierde. Deci secventa incor
ecta
if (islower(c)) toupper(c); // INCORECT! nu are efect!
else tolower(c);
// INCORECT! nu are efect!
putchar(c);
// tipareste c nemodificat!
va tipari caracterul c nemodificat!
Problema 3c Afisati un text citit pna la sfrsitul intrarii schimbnd literele n succe
sori (A -> B, e ->f, etc.) iar Z -> A, z -> a.
si n aceasta problema, fiecare caracter e prelucrat independent de celelalte. Sol
utia va avea deci din nou structura:
while ((c = getchar()) != EOF)
putchar(transforma(c));
si ramne sa scriem functia transforma, care va trebui definita (sau macar declara
ta) nainte de codul de mai sus (care va apare n main).
Stim ca un caracter e reprezentat prin valoarea codului sau ASCII iar succesorul
sau are codul cu 1 mai mare (indiferent ca e litera mare, mica sau altceva). Ca
racterul fiind un ntreg, se foloseste pur si simplu adunarea: + 1, iar literele Z
si z sunt tratate ca si cazuri speciale:
int transforma(int c)
{
return c == 'Z' ? 'A' : c == 'z' ? 'a' : c + 1;
}
Problema 3d Afisati un text citit pna la sfrsitul intrarii schimbnd fiecare cifra n
cea simetrica fata de 4.5: 3 n 6, 7 n 2, etc.
Problema are structura identica cu cea anterioara. Nu trebuie sa transformam dect
cifrele. Am putea sa tratam fiecare caz individual:
return c == '0' ? '9' : c == '1' ? '8' : c == 2 ? ... si asa mai departe ...
dar solutia e foarte lunga, ineficienta, inestetica, si susceptibila la erori de
neatentie. Cum caracterul c si rezultatul transformarii (sa-l notam cu r) sunt
simetrice fata de mijlocul intervalului ['0', '9'], putem scrie: c + r == '0' +
'9'. Functia de transformare e atunci:
int transforma(int c)
{
return isdigit(c) ? '0' + '9' - c : c;
}
Fiind att de simpla, putem scrie si direct:
#include <ctype.h>
#include <stdio.h>
int main(void)
{
int c;
while ((c = getchar()) != EOF)
putchar(isdigit(c) ? '0' + '9' - c : c);
return 0;
}
Problema 4a Dintr-un text citit pna la sfrsitul intrarii, afisati toate secventele
de cifre, fiecare secventa pe cte o linie. Daca doua secvente de cifre sunt sepa
rate printr-un punct, afisati-le ca atare pe o singura linie.
Rezolvam nti problema mai simpla fara ultima cerinta. Trebuie sa identificam secve
nte de cifre consecutive si sa tiparim un caracter de linie noua dupa fiecare.
Vom privi deci intrarea ca o alternanta de secvente de cifre si secvente de cara
ctere care nu sunt cifre; oricare din cele doua variante se poate afla la nceput
si la sfrsit. Urmatoarea secventa consuma caractere att timp ct nu sunt cifre:
while (!isdigit(c = getchar()) && c != EOF);

La sfrsitul acestei secvente, c e fie o cifra, fie EOF. n acest ultim caz, prelucr
area trebuie ncheiata. Altfel trebuie tiparit caracterul curent (care e cifra) si
citite si tiparite caractere n continuare, ct timp sunt cifre, iar apoi se trece
la rndul urmator. Daca nu s-a ajuns la sfrsitul intrarii, se reia alternanta de la
secventa de mai sus, care consuma caractere ct timp nu sunt cifre. Secventa comp
leta este:
do {
while (!isdigit(c = getchar()) && c != EOF); // cat timp nu e cifra
if (c == EOF) break;
// iese din ciclul do ... exterior
do
// aici sigur e cifra
putchar(c);
// o tiparim
while (isdigit(c = getchar())); // citim, si continuam daca e cifra
putchar('\n');
// gata cifrele
} while (c != EOF);
// daca nu, revenim la caractere ne-cifra
O varianta chiar mai simpla putem scrie daca nu privim caracterele care nu sunt
cifre ca formand o secventa, ci le tratam individual. Codul e structurat atunci
dupa tiparul
while ((c = getchar()) != EOF)
if (isdigit(c))
// trateaza secventa de cifre care ncepe aici
else
// trateaza caracterul care nu e cifra
Cum n acest caz, caracterele care nu sunt cifre se ignora, codul devine:
while ((c = getchar()) != EOF)
if (isdigit(c)) {
do
putchar(c);
while (isdigit(c = getchar()));
putchar('\n');
}
Completam acum codul cu ultima cerinta: de a afisa pe acelasi rnd secventele de f
orma cifre.cifre . Pentru aceasta, la sfrsitul secventei de cifre (n loculul lui p
utchar('\n');) testam daca caracterul e punct, si daca da, mai citim si afisam o
secventa de cifre:
if (c == '.') {
putchar(c);
while (isdigit(c = getchar())) putchar(c);
}
putchar('\n');
Am considerat ca punctul se afiseaza si daca ulterior secventa de cifre e vida (
altfel e necesar un test suplimentar). Daca dorim sa afisam mpreuna mai multe sec
vente de cifre separate prin punct: cifre.cifre.cifre, schimbam ultimul if n whil
e.
Problema 4b Cititi un text pna la sfrsitul intrarii si afisati-l nlocuind orice sec
venta de spatii albe cu un singur spatiu. Exceptie: cnd secventa contine cel puti
n doua caractere de linie noua, e nlocuita cu doua caractere de linie noua.
Ca si n problema anterioara, intrarea e vazuta ca o alternanta: spatii albe si se
cvente care nu contin spatii albe. Secventele "interesante" sunt cele cu spatii
albe; ncepem prelucrnd eventualele caractere care nu sunt spatii albe:
while (!isspace(c = getchar()) && c != EOF)
putchar(c);
Daca dupa acest ciclu c nu este EOF, cu el ncepe sigur o secventa de spatii albe.
Ct timp avem spatii albe le consumam fara a tipari, dar numaram eventualele cara
ctere de linie noua dintre ele:
unsigned cnt = 0;
do
if (c == '\n') ++cnt;
while (isspace(c = getchar()));

n final, decidem ce tiparim (spatiu sau doua linii noi); trebuie tiparit si urmat
orul caracter (deja citit), daca nu e EOF. Programul complet, n versiune usor mod
ificata:
#include <ctype.h>
#include <stdio.h>
int main(void)
{
int c;
while (1) {
// ciclu infinit, iesim cu break
while (!isspace(c = getchar())) {
if (c == EOF) return 0; // gata, iese din main/program
putchar(c);
}
// aici sigur c e spatiu
unsigned cnt = 0;
do
if (c == '\n') ++cnt;
while (isspace(c = getchar()));
if (cnt < 2) putchar(' ');
else printf("\n\n");
if (c == EOF) break; else putchar(c); // caracter ne-spatiu deja citit
}
return 0;
}
Si aici putem da o solutie alternativa fara ciclul pentru caractere care nu sunt
spatii, dupa tiparul:
while ((c = getchar()) != EOF) {
if (isspace(c))
// trateaza secventa de spatii care ncepe aici
// pana se ajunge la un caracter diferit de spatiu
// trateaza caracterul care nu e spatiu (provenind din if sau else)
Programul complet:
#include <ctype.h>
#include <stdio.h>
int main(void)
{
int c;
while ((c = getchar()) != EOF) {
if (isspace(c)) {
unsigned cnt = 0;
do
if (c == '\n') ++cnt;
while (isspace(c = getchar()));
if (cnt < 2) putchar(' ');
else printf("\n\n");
if (c == EOF) break;
}
putchar(c); // caracter ne-spatiu, daca n-a intrat in if sau ultimul din if
}
return 0;
}
Problema 4c Cititi un text pna la sfrsitul intrarii si afisati-l modificat astfel:
dupa orice secventa de spatii albe care contine cel putin doua caractere de lin
ie noua, urmatorul cuvnt ncepe cu litera mare; celelalte cuvinte se scriu integral
cu litere mici.
Si aici, textul de intrare este o alternanta de spatii albe si cuvinte (orice nu
contine spatiu alb). Urmnd strict enuntul, un eventual cuvnt initial nu trebuie m
odificat:

while (!isspace(c = getchar()) && c != EOF)


putchar(c);
Dupa aceasta secventa initiala, parcurgem un ciclu: daca nu s-a ajuns la sfrsitul
intrarii, caracterul e de tip spatiu alb, si acestea trebuie analizate, ca si n
problema anterioara (dar afisate neschimbat). Apoi, trebuie transformat cuvntul c
e urmeaza.
#include <ctype.h>
#include <stdio.h>
int main(void)
{
int c;
while (!isspace(c = getchar()) && c != EOF) // cuvantul initial
putchar(c);
while (c != EOF) {
unsigned cnt = 0;
// caracterul sigur e spatiu
do {
if (c == '\n') ++cnt;
putchar(c);
} while (isspace(c = getchar()));
if (c == EOF) break;
// daca nu, urmeaza un cuvant
if (cnt < 2)
do
putchar(tolower(c));
// toate literele devin mici
while (!isspace(c = getchar()) && c != EOF);
else {
putchar(toupper(c));
// litera initiala mare
while (!isspace(c = getchar()) && c != EOF)
putchar(c);
// celelalte tiparite normal
}
}
return 0;
}

S-ar putea să vă placă și