Funzioni matematiche
La libreria cmath
Il linguaggio C++ mette a disposizione la libreria <cmath> per svolgere numerose funzioni matematiche.
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
cout << pow(2,3) << endl; // 2 ^ 3
cout << sqrt(2) << endl; // radice quadrata
cout << cbrt(2) << endl; // radice cubica
cout << sin(3.14159) << endl;
cout << cos(3.14159) << endl;
cout << tan(3.14159) << endl;
cout << exp(2) << endl;
cout << log(10) << endl;
cout << ceil(2.3) << endl; // più piccolo intero maggiore (per eccesso)
cout << floor(2.3) << endl; // più grande intero minore (per difetto)
cout << round(2.3) << endl; // intero più vicino (per difetto o eccesso)
cout << abs(-2) << endl; // valore assoluto
return 0;
}
E’ possibile usare anche costanti matematiche, aggiungendo una direttiva define come la seguente:
#define _USE_MATH_DEFINES
#include <cmath>
using namespace std;
int main()
{
cout << M_E << endl; // numero di Eulero
cout << M_PI << endl; // pi greco
cout << M_PI_2 << endl; // pi greco / 2
cout << M_SQRT2) << endl; // radice di 2
return 0;
}
Casting
Il casting consiste in una operazione che consente di trasformare un tipo di dato in un altro.
int x = 3;
float y = (float)x;
Il casting è solitamente implicito quindi non è necessario trasformare un tipo di dato in un altro. Una eccezione è con il tipo di dato char:
char c = "A";
cout << c << endl; // A
cout << (int)c << endl; // 65 è il codice di A
Operatori unari
In C++ sono presenti alcuni operatori che permettono di semplificare alcune operazioni semplici di calcolo. Qui l’operatore di postincremento:
int c = 2;
c++; // c viene incrementato di 1
c--; // c viene decrementato di 1
Esiste anche un operatore di preincremento:
int c = 2;
++c; // c viene incrementato di 1
--c; // c viene decrementato di 1
Che differenza c’è? Lo vediamo qui:
int a, b;
int c = 2;
a = ++c; // prima c viene incrementato di 1 e poi viene assegnato => a=2, c=2
b = c++; // prima c viene assegnato e poi viene incrementato di 1 => b=1, c=2
Numeri casuali
E’ possibile generare numeri casuali con un computer?
La risposta breve è: no.
Un computer è per sua natura un sistema deterministico, ovvero è un sistema che dispone di uno stato (la sua memoria) e può ricevere degli input (tramite le periferiche). Se eseguiamo un programma per computer quello che il computer svolge è ricevere un eventuale input, ed eseguire una transizione da uno stato ad un altro e mostrare un eventuale output. Si dice che è deterministico perché viene garantito che a partire dallo stesso stato e dallo stesso input, si raggiungerà sempre lo stesso stato e lo stesso output, qualsiasi sia il computer su cui viene eseguito il programma.
Ad esempio, prendiamo questo programma:
#include <iostream>
using namespace std;
int main()
{
int i = 3;
cin >> j;
int i = i+j;
cout << j;
return 0;
}
E’ un programma che come si può vedere si comporterà allo stesso identico modo purché l’utente inserisca lo stesso valore di j. Ad esempio con j=2 otterremo sempre i=5.
E’ possibile creare un algoritmo che genera una sequenza pseudocasuale di numeri, che a partire da un numero base (detto seme) calcola il numero successivo della sequenza. Qui un esempio: https://onlinegdb.com/aBIk5uF-Ea Tuttavia questa sequenza viene generata sempre nello stesso ordine perché il numero di partenza è sempre lo stesso e sempre le stesse sono le operazioni eseguite.
Per rendere quindi “casuale” ciò di cui abbiamo bisogno è di dare all’algoritmo un numero iniziale (detto seme) sempre diverso in modo da generare, ad ogni esecuzione, una sequenza diversa e quindi essere davvero casuale.
Vediamo come svolgere tutto questo in C/C++.
Come generare numeri casuali in C++
La libreria <cstdlib> del linguaggio C++ include alcune funzioni per la generazione di numeri “random” (cioè “casuali”).
La funzione che andremo ad utilizzare si chiama rand() e funziona nel seguente modo:
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
int i = rand();
cout << i << endl;
return 0;
}
Cerchiamo di capire cosa significa:
- Rand() emette un numero “pseudocasuale” tra 0 e una numero massimo (che dipende dal sistema e si chiama RAND_MAX). Il suo funzionamento è analogo a quello visto nell’esempio sopra.
- Se vogliamo invece un valore tra 0 e N (es. tra 0 e 100) dobbiamo calcolarlo tramite questa semplice operazione:
int i = rand() % N;
Questo perchè il resto della divisione intera di un qualsiasi numero per B è un numero compreso tra 0 e N-1. Ad esempio 3362721 % 100 fa 21, oppure 27264142 % 100 fa 42 (provare con qualsiasi numero a caso e con qualsiasi N).
Tuttavia Se andiamo ad eseguire questo codice su un compilatore (oppure anche online su www.onlinegdb.com) verrà infatti generato sempre lo stesso numero.
Questo perché in un sistema informatico siccome è deterministico non si possono generare numeri realmente casuali quindi ogni volta che viene chiamata la funzione rand() questa restituisce un numero compreso tra 0 ed un numero massimo dipendente dal sistema (detto RAND_MAX). Tuttavia se eseguiamo molte volte questa funzione i numeri che escono non sembrano correlati tra loro e quindi “sembra” che questa sequenza sia casuale.
Tuttavia rimane il problema che ogni volta che si esegue il programma vengono generati sempre gli stessi numeri, perchè come detto sopra l’algoritmo che genera i numeri casuali parte sempre dallo stesso numero iniziale.
Vediamo un esempio in cui genero 10 numeri tra 1 e 100 nper capirlo meglio:
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
for (int j=1; j<10; j++) {
int i = rand() % 100 + 1;
cout << i << ", ";
}
return 0;
}
Se eseguo questo codice otterrò sempre la stessa sequenza, ad esempio esce sempre questa: 84, 87, 78, 16, 94, 36, 87, 93, 50
Per risolvere questo problema e quindi generare ogni volta una nuova sequenza casuale, dobbiamo trovare un meccanismo per generare ad ogni avvio un numero casuale che useremo per inizializzare rand() con un numero sempre diverso ogni volta. Per farlo usiamo l’unico numero che sicuramente cambia, ovvero il tempo. I computer hanno infatti un orologio interno che calcola ad ogni istante un valore chiaamato timestamp, che indica il tempo in millisecondi passato da una data fissa valida per tutti i computer, ovvero l’1/1/1970 00:00 (anno convenzionale della nascita di UNIX).
La funzione che inizializza rand() si chiama srand(), a cui passeremo il valore della funzione di sistema time(NULL) (che emette il timestamp corrente). Siccome varia ad ogni avvio, varierà anche la sequenza generata.
Proviamo quindi a generare di nuovo di valori casuali:
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
srand(time(NULL));
for (int j=1; j<10; j++) {
int i = rand() % 100 + 1;
cout << i << ", ";
}
return 0;
}
Questa volta otterremo una sequenza differente ad ogni esecuzione.