Panta unei drepte — cât de abruptă urcă

Mediu~15 min9 pași

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 / dx

Adică „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ă, dx este 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 * dx1

Această 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 == 0

Tot 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 = 4
  • dx = x2 - x1 = 3 - 1 = 2
  • m = 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 paralele

Aceeaș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;
}
Observația-cheie

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.

Greșeli frecvente
  • Împarți la 0 la verticală: aplici dy / dx orb, dar când dx = 0 dreapta 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.0 poate da fals din cauza rotunjirii. Pe coordonate întregi, compară prin dy1 * dx2 == dy2 * dx1.
  • Perpendicular calculat greșit: m1 * m2 == -1 cade la verticală (panta lipsă). Folosește dx1 * dx2 + dy1 * dy2 == 0, care merge mereu.
  • Overflow la înmulțirea încrucișată: pentru coordonate mari, dy1 * dx2 poate depăși int. Ține diferențele și produsele în long long.
4
2
10
5
20
20
dy1
dx1
dy2
dx2
dy1*dx2
dy2*dx1
Test de paralelism prin înmulțire încrucișată: dy1*dx2 = 20 este egal cu dy2*dx1 = 20, deci dreptele sunt paralele.

Recapitulare

  • Panta m = dy / dx mă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 în long long ca să eviți overflow-ul.

Întrebarea 1 / 3

Care este panta dreptei care trece prin punctele (1, 1) și (3, 5)?