Problem z zadaniem C++ bez użycia bibliotek

0

Mam problem z pewnym zadaniem.
Rozwiązanie ma być napisane w języku C++, bez użycia jakichkolwiek zewnętrznych bibliotek, z wyjątkiem <iostream>. W dużym uproszczeniu, na wejściu dostajemy współrzędne (liczby całkowite) kilku punktów, a na wyjściu program powinien podać współrzędne szukanego punktu B, w postaci ułamków zwykłych (np. x = 8/3, y = -3/1).

W moim rozwiązaniu doszedłem do momentu, w którym mam:

  • Znane współrzędne (liczby całkowite, podane na wejściu) punktów A, C oraz D.
  • Znane pole trójkąta ABC (w postaci ułamka prostego)
  • Nieznane współrzędne punktu B
  • Wiadomo, że punkty B, C i D leżą na jednej prostej oraz, że suma długości odcinków BC i BD jest równa długości odcinka CD (B znajduje się pomiędzy C i D)

W jaki sposób, mając powyższe dane, znaleźć dokładne współrzędne punktu B? Mój początkowy zamysł polegał na dzieleniu odcinka CD rekurencyjnie, na połowy, tak długo, aż kolejny wyznaczony środek (nazwijmy go E) okaże się być szukanym punktem B (pole trójkąta AEC bedzie się równało znanemu polu ABC). W praktyce okazało się, że podczas dzielenia odcinka licznik/mianownik ułamka rosną do rozmiarów przekraczających zakres typu long long int i program się wysypuje. Wynik musi być w stu procentach precyzycyjny, dlatego odpada wszelkie liczenie pierwiastków czy działania na liczbach zmiennoprzecinkowych. Utknąłem w tym miejscu i proszę o pomoc.

Na potrzeby rozwiązania stworzyłem klasy:

Ułamek:

class Fraction 
{
private:
	long long n;
	long long d;
public:
	Fraction(long long a, long long b);
	Fraction() {}
	void Reduce();
	void Print();
	long long GetN();
	long long GetD();
	Fraction operator*(const Fraction& f);
	Fraction operator/(const Fraction& f);
	Fraction operator+(const Fraction& f);
	Fraction operator-(const Fraction& f);
	bool operator>(const Fraction& f);
	bool operator!=(const Fraction& f);
};

Punkt:

class Point
{
private:
	Fraction x;
	Fraction y;
public:
	Point(int xn, int yn, int xd = 1, int yd = 1);
	Point() {}
	void Print();
	Fraction GetX();
	Fraction GetY();
};

Definicje metod i konstruktorów:

//Konstruktory Fraction


Fraction::Fraction(long long a, long long b) {
	if (a == 0) {
		this->n = 0;
		this->d = 1;
	}
	else {
		this->n = a;
		this->d = b;
	}
	Reduce();
}


//Przeciazone operatory Fraction

//Przeciazony operator *
Fraction Fraction::operator*(const Fraction& f) {
	long long a = this->n * f.n;
	long long b = this->d * f.d;
	return Fraction(a, b);
}

//Przeciazony operator /
Fraction Fraction::operator/(const Fraction& f) {
	long long a = this->n * f.d;
	long long b = this->d * f.n;
	if (f.n < 0) {
		b = -b;
		a = -a;
	}
	return Fraction(a, b);
}

//Przeciazony operator +
Fraction Fraction::operator+(const Fraction& f) {
	long long a = this->n * f.d + f.n * this->d;
	long long b = this->d * f.d;
	return Fraction(a, b);
}

//Przeciazony operator -
Fraction Fraction::operator-(const Fraction& f) {
	long long a = this->n * f.d - f.n * this->d;
	long long b = this->d * f.d;
	return Fraction(a, b);
}

//Przeciazony operator >
bool Fraction::operator>(const Fraction& f) {
	long long a, b;

	a = this->n * f.d;
	b = f.n * this->d;

	if (a > b) {
		return true;
	}
	else {
		return false;
	}
}

//Przeciazony operator !=
bool Fraction::operator!=(const Fraction& f) {
	if (this->n == f.n && this->d == f.d) {
		return false;
	}
	else {
		return true;
	}
}


//Metody Fraction

//Metoda skracajaca ulamek
void Fraction::Reduce() {
	long long a, aa;
	long long b, bb;
	
	this->n < 0 ? aa = -(this->n) : aa = this->n;
	bb = this->d;

	while (bb != 0) {
		a = aa;
		b = bb;

		aa = bb;
		bb = a % b;
	}

	this->n = this->n / b;
	this->d = this->d / b;
}

//Metoda wypisujaca ulamek
void Fraction::Print() {
	cout << this->n << "/" << this->d << endl;
}

//getter n
long long Fraction::GetN() {
	return this->n;
}

//getter d
long long Fraction::GetD() {
	return this->d;
}

//Konstruktory Point


Point::Point(int xn, int yn, int xd, int yd) {
	this->x = Fraction(xn, xd);
	this->y = Fraction(yn, yd);
}



//Metody Point

//Metoda wypisująca punkt
void Point::Print() {
	cout << "X: "; 
	this->x.Print();
	cout << "\nY: ";
	this->y.Print();
}

Fraction Point::GetX() {
	return this->x;
}

Fraction Point::GetY() {
	return this->y;
}
3

IMG_20220109_154532.jpg

Pole RCST składa się z trójkąta ACB oraz trzech innych w rogach. Ustal sobie, że B=(x,y). Wtedy pierwsza współrzędna T oraz S są równe x. Ułóż z tej relacji, że pole kwadratu to suma pół tych 3 trójkątów równanie. A potem ułóż drugie równanie, na to że B leży na prostej CD.

0

@enedil: O ile dobrze zrozumiałem, ma z tego wyjść układ równań. Umiem to rozwiązać na kartce, ale jak to zaprogramować w c++?

1

@Tssuf: rozpisz sobie na kartce, tak żeby zostało ci coś postaci

k*y = l*x + m

, gdzie k, l, m zależą od danych wejściowych, takich jak współrzędne A, C, oraz pole ABC - w obu tych równaniach powinieneś dotrzeć do takiej postaci.

Jak już to masz, to napisz program, który by robił to samo co ty na kartce, gdy masz dwa takie równania liniowe (z tego co pamiętam, takie rzeczy się najpóźniej w gimnazjum rozwiązywało, więc chyba powinieneś wiedzieć jak to liczyć).

1
d.x=2*b.x-c.x;
d.y=2*b.y-c.y;

Jak dodasz konstruktor:

    Point(const &Fraction x,const &Fraction y):x(x),y(y) {}

to:

Point d(b.GetX()+b.GetX()-c.GetX(),b.GetY()+b.GetY()-c.GetY());
0

Wielkie dzięki za podpowiedzi. Na razie muszę się zająć czymś innym, ale jak tylko znajdę czas to do tego wrócę i dam znać czy działa.

0

@enedil: Ciągle nie rozumiem jak to sprowadzić do równań liniowych. Pierwsza relacja wygląda u mnie tak:
tri.jpg![]
Pole ABC z wektorów i wyznacznika.

0

@Tssuf: pole ABC masz dane, pochodzi z wejścia do programu. Rx, Ry, Sy, Ty możesz sobie wyliczyć z położenia punktów A i C.

Musisz sobie jeszcze poradzić z wartościami bezwzględnymi - po prostu rozważ przypadki dla każdego możliwego znaku.

0

@_13th_Dragon:

d.x=2*b.x-c.x;
d.y=2*b.y-c.y;

Skąd to się wzięło?

0

Z geometrii i matematyki?
Z podobieństwa trójkątów:
c.y-b.y = b.y-d.y
b.x-c.x = d.x-b.x
dalej zwykła matematyka.

0

@enedil: Ay = Ry, Cx = Rx; itp. Przepraszam, ale mimo to, ciągle nie widzę, jak można uprościć to równanie do liniowego.

1

@_13th_Dragon: A to nie powinno być:

(c.y - b.y) / (c.y - d.y) = (c.x - b.x) / (c.x - d.x)

?

0

@_13th_Dragon: nikt nie powiedział że B jest w połowie CD

0
enedil napisał(a):

@_13th_Dragon: nikt nie powiedział że B jest w połowie CD

"Wiadomo, że punkty B, C i D leżą na jednej prostej oraz, że suma długości odcinków BC i BD jest równa długości odcinka CD (B znajduje się pomiędzy C i D)"

0

IMG_20220112_222539.jpg

Tylko trzeba rozważyć te przypadki, co się dzieje jak jest inna konfiguracja.

0

Nie bardzo rozumiem (czytać wcale jak zawsze) jaki wpływ ma pozycja punktu A do pozycji punktu D, może monie ktoś oświecić?

0

@enedil: Czegoś tu nie rozumiem. Dlaczego liczysz to tak, jakby punkt C znajdował się w początku układu współrzędnych (punkcie 0,0)? Tzn. zamiast odjąć jedną współrzędną od drugiej, żeby otrzymać długość boku, zwyczajnie bierzesz samą współrzędną ( czyli np. pole dolnego trójkąta to

 x * y

zamiast

 |Cx - x| * |Cy - y|

)? To tak się da?

0

@Tssuf: dla prostoty obliczeń, uznałem, że C=(0, 0), i odjąłem od wszystkich punktów współrzędne C.

0

@enedil: Czy dobrze rozumiem, że można sobie ten punkt C ustalić jako (0, 0) (oraz od wszystkich pozostałych punktów odjąć C.x i C.y), policzyć współrzędne punktu B, i na końcu tylko z powrotem dodać te przesunięcie C i wynik będzie poprawny? Czy to tylko do uproszczenia zapisu i muszę w obliczeniach podstawić te wszystkie różnice pod wartościami bezwzględnymi?
Jeśli chodzi o przypadki to są 3? Czyli każdy z wierzchołków ABC może być w rogu kwadratu?

0

@enedil: Niestety, rozwiązanie nie zadziała dla każdego przypadku. Przykład:
Triangle2.png![]
Na oko widać, że nie da się utworzyć tego czworokąta dla takiego trójkąta.

1

Masz dwa równania:
powierzchnia trójkąta: A-B-C = P
powierzchnia trójkąta: B-C-D = 0
stąd:
screenshot-20220114120823.png
Druga para rozwiązań pod warunkiem że przeniesiesz C do (0,0)
Trzecia para kiedy rozwiązań pod warunkiem że przeniesiesz A do (0,0)

W poprzedniej wersji postu była pomyłka w formule /2 jakoś mi wewnątrz Abs się wlazło.

0

@_13th_Dragon: Wielkie dzięki. Wydaje się, że to powinno rozwiązać problem dla każdego przypadku. Biorę się za implementację i testowanie i dam znać jak skończę i będę pewny że działa.

3

Strasznie przekombinowane.
Wystarczy zauważyć, że długość wektora \vec{CB} jest proporcjonalna do pola S.
Ergo wzór na ten wektor to:
\vec{CB} = \vec{CD} \frac{2\cdot S}{|\vec{CA} \times \vec{CD}|}
Wystarczy zaimplementować algebrę 2D oraz ułamki rzeczywiste połączyć ze wzorem i gotowe.

Dowód, że wzór jest poprawny:
\vec{CB} = \vec{CD} \frac{2\cdot S}{|\vec{CA} \times \vec{CD}|} ;;;; / \vec{CA} \times \<br> \vec{CA} \times \vec{CB} = \vec{CA} \times \vec{CD} \frac{2\cdot S}{|\vec{CA} \times \vec{CD}|} ;;;;  / abs\<br> |\vec{CA} \times \vec{CB}| = \left|\vec{CA} \times \vec{CD} \frac{2\cdot S}{|\vec{CA} \times \vec{CD}|}\right| \<br> 2 \cdot S = |\vec{CA} \times \vec{CD}| \frac{2\cdot S}{|\vec{CA} \times \vec{CD}|} \<br> L = P

0

@MarekR22: Zaimplementowane i wstępnie przetestowane - działa bez zarzutów. Człowiek się tak głowił, a tu takie proste rozwiązanie.. Bardzo dziękuję za pomoc ;)

0

Początkowo planowałem pełne TDD, ale dyscyplina siadła: https://godbolt.org/z/oqhzvzd8b

1 użytkowników online, w tym zalogowanych: 0, gości: 1