Készíts racionális szám osztályt! Próbáld meg egyedül, s ha nem megy, belenézhetsz az előadáson készült jegyzetedbe, ill. a tárgy oldalán lévő segédletbe is. A megvalósítandó funkciók (ami lehet, legyen konstans):
- A számláló és a nevező egész szám.
- Három konstruktor szükséges: paraméter nélküli (=alapértelmezett=default), ami 0-ra állítja a számot, egyparaméteres, amely egy egész számot kap, ennyi lesz a szám értéke (azaz n/1), valamint kétparaméteres: a két egész szám a számláló és a nevező. Ha ügyes vagy, megvalósíthatod a három konstruktort egy függvénnyel. Hogyan lehetséges ez? A kétparaméteres konstruktor dobjon domain_error kivételt, ha 0 értéket kap nevezőként.
- Bónusz feladat: egy double és egy egész paramétert kapó konstruktor, az egész adja meg, hogy hány tizedes jegyet vegyen figyelembe a double értékből a konstruktor. Használj kerekítést a double=>racionális konverziónál! (Pl. ha 3.1415926 a bemenő érték és 4 tizedesjegyet kérünk, akkor a tárolt racionális érték egyszerűsítés előtt 31416/10000).
- Egyszerűsítő függvény: írj függvényt, amely egyszerűsíti a törtet. Pl. 2/6=>1/3. (Ehhez a számláló és a nevező legnagyobb közös osztóját kell kiszámítani.) Az egyszerűsítés után a nevező nem lehet negatív szám (pl. 2/-5 helyett -2/5). Az egyszerűsítő függvény nem kell, hogy osztályon kívülről elérhető legyen.
- + operátor, amely kiszámítja két racionális szám összegét. Az operátort megvalósíthatod globális függvényként vagy tagfüggvényként. Figyelj arra, hogy a + operátor nem változtatja meg a paraméterei értékét, azaz A = B + C esetben B és C változatlan marad, csak A változik.
- += operátor, amely egy racionális számhoz hozzáad egy másikat. Az a += b jelentése a = a + b.
- / operátort, amely két racionális szám hányadosát adja. 0-val való osztás esetén ez is kivételt dob.
- == és != operátort, amely összehasonlít két racionális számot.
- konverziós operátort, amely double típusú értékké konvertálja a racionális számot.
Írj main függvényt, amelyben kipróbálod az osztály minden funkcióját!
Tipp: Az összeadáshoz a két törtet közös nevezőre kell hozni. Bár a legegyszerűbb, ha nevezőként a két bemenő tört nevezőjének szorzatát választjuk, ez nagy nevezőknél hibát okozhat az egész típusok véges értékkészlete miatt (pl. ha 1/1000 és 1/10000 összegét kell kiszámítanunk, a két nevező szorzata 10000000 lesz, miközben 10000 is elég lenne), ezért célszerű a két nevező legkisebb közös többszörösével dolgozni. A LKKT kifejezhető a LNKO-val: LKKT = A/LNKO*B. Mi lesz ekkor a két számlálóval?
Tipp: Az egyes műveletek után a racionális szám mindig a lehető legegyszerűbb alakú legyen. Az egyszerűsítés számításigényes, csak akkor használd, ha szükséges!
Tipp: A LNKO kiszámítására legegyszerűbb brute force módszert használni, legalább ezt ZH-ban is tudni kell. Jóval hatékonyabb azonban az euklideszi algoritmus: Ezt nem kell tudni fejből (de ha tudod, az nem baj:), de könnyen találsz a neten akár C nyelvű megvalósítást is.
A múlt heti 2D vektor osztályt egészítsd ki konstruktorral, + operátorral, += operátorral, valamint skalárral (double) szorzó * operátorral! A vektort skalárral balról és jobbról is lehessen szorozni! Ezek segítségével számítsd ki, hol csapódik a földbe egy ferdén elhalyított tárgy (a súrlódást figyelmen kívül hagyjuk).
- A gyosulás (a), sebesség (v) és hely (p) vektor objektumok.
- A gyorsulás kezdetben (0,-g), a sebesség a felhasználó által megadott r és phi, a hely (0,0).
- A számítás Euler integrálással történhet. A gyorsulás végig változatlan, hely: p += v*dt, a sebesség: v +=a*dt. A dt végig állandó, a felhasználó adja meg. A becsapódás akkor történik meg, amikor p.y<=0 lesz.
Egészítsd ki a racionális szám osztályt értelmes műveletekkel! Legyen ~ (tilde) operátor, amely a reciprokot adja, - előjel operátor (egyoperandusú), és a kimaradt alapműveletek.
| 1. feladat |
#include <cstdio>
#include <stdexcept>
class Rac{
int sz, n;
int lnko(int a, int b)const;
void egyszerusit();
public:
Rac(int sz = 0, int n = 1){
this->sz = sz; this->n = n;
if (n == 0)
throw std::domain_error("Rac::Rac: 0 a nevezoben");
egyszerusit();
}
Rac(double szam, int tizedes){
bool neg = false;
if (szam < 0)
szam = -szam, neg = true;
if (tizedes < 0)
throw std::domain_error("Rac::Rac: tizedes jegyek szama<0");
n = 1;
for (; tizedes > 0; tizedes--)
n *= 10;
sz = (int)(szam*n + 0.5);
sz = neg ? -sz : sz;
egyszerusit();
}
void print()const{
printf("(%d/%d)\n", sz, n);
}
Rac operator+(const Rac &b)const;
Rac& operator+=(const Rac&);
Rac operator/(const Rac& b);
bool operator==(const Rac& b){ return sz == b.sz && n == b.n; }
bool operator!=(const Rac&b){ return !(*this == b); }
operator double()const{ return (double)sz / n; }
void set(int sz, int n = 1){
this->sz = sz, this->n = n;
if (n == 0)
throw std::domain_error("Rac::set: 0 a nevezoben");
egyszerusit();
}
};
int Rac::lnko(int a, int b)const{
a = a < 0 ? -a : a;
b = b < 0 ? -b : b;
while (b>0){
int temp = b;
b = a % b;
a = temp;
}
return a;
}
void Rac::egyszerusit(){
int neg = sz < 0 ? -1 : 1;
neg *= n < 0 ? -1 : 1;
sz = sz < 0 ? -sz : sz;
n = n < 0 ? -n : n;
int oszto = lnko(sz, n);
if (oszto != 0)
sz /= oszto, n /= oszto;
sz *= neg;
}
inline Rac Rac::operator+(const Rac &b)const{
int oszto = lnko(n, b.n);
return Rac(b.n/oszto*sz + n/oszto*b.sz, n/oszto*b.n); // konstruktor: egyszerűsít is
}
inline Rac& Rac::operator+=(const Rac& b){
*this = *this + b; //op+ majd egyszerűsít
return *this;
}
inline Rac Rac::operator/(const Rac& b){
if (b.sz == 0)
throw std::domain_error("Rac::operator/: osztas 0-val");
int oszto1 = lnko(sz, b.sz);
int oszto2 = lnko(n, b.n);
return Rac((sz / oszto1)*(b.n / oszto2), (n / oszto2)*(b.sz / oszto1));
}
int main(){
try{
Rac a(2, 6);
a.print();
Rac b(-3.1415926, 3);
b.print();
printf("%g\n", (double)b); // expicit meg kell adni a konverziót, mert a printf nem ismeri fel
double x;
x = b; // automatikusan létrejön a konverzió
printf("%g\n", x);
Rac c(2, 3);
Rac d = a + c;
d.print();
a.set(1, 1000);
b.set(1, 10000);
d = a + b;
d.print();
d += Rac(1, 10000); // ideiglenes objektum a referencia erre mutat, e sor után megszűnik
d.print();
a.set(-21, 10);
b.set(9, 25);
d = a / b;
d.print();
if (a == b)
printf("a==b\n");
else
printf("a!=b\n");
a = b;
if (a == b)
printf("a==b\n");
else
printf("a!=b\n");
Rac(1, 0);
}
catch (const std::domain_error &d){
printf("Domain error:\n\t%s\n", d.what());
}
return 0;
} |
| 2. feladat |
#include <cstdio>
#include <cmath>
class vec2D{
double x, y;
public:
vec2D(double x = 0, double y = 0) :x(x), y(y){}
double getX()const{ return x; }
double getY()const{ return y; }
double getR()const{ return sqrt(x*x + y*y); }
double getPhi()const{ return atan2(y, x); }
void setXY(double x, double y){ this->x = x; this->y = y; }
void setRPhi(double r, double phi){ x = r*cos(phi); y = r*sin(phi); }
vec2D operator+(const vec2D &v)const{ return vec2D(getX() + v.getX(), getY() + v.getY()); }
const vec2D& operator+=(const vec2D &v){ setXY(getX() + v.getX(), getY() + v.getY()); return *this; }
vec2D operator*(double d)const{ return vec2D(getX()*d, getY()*d); }
};
inline vec2D operator*(double d, const vec2D &v){
return v*d;
}
int main(){
vec2D a(0, -9.81), v, p;
double r, phi, dt;
const double pi = acos(0)*2;
printf("v0= "); scanf("%lg", &r);
printf("szog= "); scanf("%lg", &phi);
phi *= pi / 180;
v.setRPhi(r, phi);
printf("dt= "); scanf("%lg", &dt);
do{
p += v*dt;
v += a*dt;
} while (p.getY() > 0);
printf("Becsapodas: %g\n", p.getX());
return 0;
} |