De ce contează?
Mergi pe un deal. La fiecare pas înainte urci puțin. Pe o pantă lină faci un pas mare înainte și urci doar un pic; pe una abruptă urci mult la fiecare pas. „Panta” este exact acest raport: cât urci pe cât înaintezi. O dreaptă din planul cartezian are aceeași idee — doar că o măsori cu coordonate.
Ce este panta
Ai două puncte, A(x1, y1) și B(x2, y2). Cât urci de la A la B este
dy = y2 - y1 (diferența pe verticală). Cât mergi la dreapta este
dx = x2 - x1 (diferența pe orizontală). Panta este raportul lor:
m = (y2 - y1) / (x2 - x1) = dy / dxAdică „cât urci pe cât mergi la dreapta”. Dacă m este 2, urci 2 unități pentru
fiecare unitate înainte. Dacă m este negativă, dreapta coboară; dacă m este 0,
dreapta este orizontală (nu urci deloc).
Problema verticalei
Ce se întâmplă când cele două puncte au aceeași coordonată x? Atunci
dx = x2 - x1 = 0, iar formula cere o împărțire la 0. Dreapta este verticală
și panta ei nu este definită — urci, dar nu înaintezi deloc la dreapta. În cod,
o împărțire la 0 e fie o eroare, fie un rezultat „infinit” cu care nu poți compara
corect. Asta e prima capcană pe care vrei să o eviți.
De ce eviți împărțirea
Presupune că vrei să afli dacă două drepte sunt paralele, adică au aceeași
pantă. Tentația e să compari dy1 / dx1 == dy2 / dx2. Două probleme:
- dacă vreuna dintre drepte e verticală,
dxeste 0 și împarți la 0; - pe numere reale,
/produce valori cu virgulă mobilă, iar comparația cu==între ele e nesigură (erori de rotunjire).
Soluția: înmulțire încrucișată. Pornești de la egalitatea rapoartelor
dy1 / dx1 == dy2 / dx2și înmulțești ambele părți cu dx1 * dx2. Numitorii dispar:
dy1 * dx2 == dy2 * dx1Această formă merge și pentru drepte verticale (când vreun dx e 0), folosește
doar întregi și o singură comparație. E motivul pentru care, în algoritmică pe
coordonate întregi, preferi mereu înmulțirea încrucișată în locul împărțirii.
Pentru perpendiculare, condiția clasică m1 * m2 == -1 aduce iar împărțirea.
Cu vectorii direcție (dx1, dy1) și (dx2, dy2) o rescrii ca produs scalar zero:
dx1 * dx2 + dy1 * dy2 == 0Tot numai înmulțiri și adunare de întregi, fără caz special pentru verticală.
Exemplu pas cu pas
Fie A(1, 1) și B(3, 5). Calculăm:
dy = y2 - y1 = 5 - 1 = 4dx = x2 - x1 = 3 - 1 = 2m = dy / dx = 4 / 2 = 2
Deci urci 4 cât mergi 2 la dreapta — panta este 2.
Acum testăm paralelismul fără împărțire. Prima dreaptă are dy1 = 4, dx1 = 2.
A doua dreaptă trece prin C(0, 0) și D(5, 10): dy2 = 10, dx2 = 5.
Verificăm:
dy1 * dx2 == dy2 * dx1
4 * 5 == 10 * 2
20 == 20 -> adevarat, deci sunt paraleleAceeași pantă (4/2 = 2 și 10/5 = 2), confirmată doar cu înmulțiri.
Implementare C++
#include <iostream>
using namespace std;
// panta ca numar real, DOAR pentru cazul general (dx != 0)
double panta(long long x1, long long y1, long long x2, long long y2) {
long long dx = x2 - x1;
long long dy = y2 - y1;
// dx == 0 inseamna dreapta verticala: panta nedefinita
return (double)dy / dx;
}
// paralelism cu intregi, fara impartire (merge si pentru verticala)
bool paralele(long long x1, long long y1, long long x2, long long y2,
long long x3, long long y3, long long x4, long long y4) {
long long dx1 = x2 - x1, dy1 = y2 - y1;
long long dx2 = x4 - x3, dy2 = y4 - y3;
// dy1/dx1 == dy2/dx2 rescris prin inmultire incrucisata
return dy1 * dx2 == dy2 * dx1;
}
// perpendicularitate: produs scalar al vectorilor directie == 0
bool perpendiculare(long long x1, long long y1, long long x2, long long y2,
long long x3, long long y3, long long x4, long long y4) {
long long dx1 = x2 - x1, dy1 = y2 - y1;
long long dx2 = x4 - x3, dy2 = y4 - y3;
return dx1 * dx2 + dy1 * dy2 == 0;
}
int main() {
// A(1,1) B(3,5): panta = 2
cout << panta(1, 1, 3, 5) << "\n"; // 2
// AB prin (1,1)-(3,5) si CD prin (0,0)-(5,10): paralele
cout << paralele(1, 1, 3, 5, 0, 0, 5, 10) << "\n"; // 1 (adevarat)
// AB orizontala (0,0)-(4,0) si CD verticala (2,1)-(2,9): perpendiculare
cout << perpendiculare(0, 0, 4, 0, 2, 1, 2, 9) << "\n"; // 1 (adevarat)
return 0;
}O dreaptă verticală are dx = 0, deci panta nu este definită — nu o calcula
ca număr real, ai împărți la 0. Pentru paralelism și perpendicularitate folosește
formele cu înmulțire încrucișată / produs scalar: ele tratează verticala fără
niciun caz special.
- Împarți la 0 la verticală: aplici
dy / dxorb, dar cânddx = 0dreapta e verticală și panta nu există. Testeazădx == 0înainte sau evită complet împărțirea. - Compari pante cu
float/double:4.0/2.0 == 10.0/5.0poate da fals din cauza rotunjirii. Pe coordonate întregi, compară prindy1 * dx2 == dy2 * dx1. - Perpendicular calculat greșit:
m1 * m2 == -1cade la verticală (panta lipsă). Foloseștedx1 * dx2 + dy1 * dy2 == 0, care merge mereu. - Overflow la înmulțirea încrucișată: pentru coordonate mari,
dy1 * dx2poate depășiint. Ține diferențele și produsele înlong long.
4 | 2 | 10 | 5 | 20 | 20 |
dy1 | dx1 | dy2 | dx2 | dy1*dx2 | dy2*dx1 |
Recapitulare
- Panta
m = dy / dxmăsoară cât urci pe cât mergi la dreapta; verticala (dx = 0) are panta nedefinită. - Paralelism fără împărțire:
dy1 * dx2 == dy2 * dx1— doar înmulțiri de întregi, sigur și pentru verticală. - Perpendicularitate: produs scalar al vectorilor direcție
dx1 * dx2 + dy1 * dy2 == 0; ține totul înlong longca să eviți overflow-ul.