|
3 | 3 | #include <stdexcept> |
4 | 4 | #include <cmath> |
5 | 5 | #include <set> |
| 6 | +#include <tuple> |
6 | 7 |
|
7 | 8 |
|
8 | 9 | ContinuedFraction::ContinuedFraction() |
@@ -52,7 +53,7 @@ ContinuedFraction ContinuedFraction::FROM_Q_CF(const Rational& rational) { |
52 | 53 |
|
53 | 54 | // p = q, q = остаток |
54 | 55 | p = Integer(q.as_string()); |
55 | | - q = Natural(remainder.ABS_Z_N().as_string()); |
| 56 | + q = remainder.ABS_Z_N(); |
56 | 57 | } |
57 | 58 |
|
58 | 59 | return ContinuedFraction(coeffs); |
@@ -221,29 +222,50 @@ Rational ContinuedFraction::APPROX_CF_Q(const Natural& max_denominator) const { |
221 | 222 |
|
222 | 223 | // CF-6 | Сравнение двух цепных дробей |
223 | 224 | int ContinuedFraction::COM_CF_D(const ContinuedFraction& other) const { |
224 | | - size_t min_len = std::min(coefficients_.size(), other.coefficients_.size()); |
| 225 | + // нормализация: убираем завершающие 1 ([..., a, 1] -> [..., a+1]) |
| 226 | + auto normalize = [](std::vector<Integer> coeffs) { |
| 227 | + while (coeffs.size() > 1 && coeffs.back().COM_ZZ_D(Integer("1")) == 0) { |
| 228 | + coeffs.pop_back(); |
| 229 | + coeffs.back() = coeffs.back().ADD_ZZ_Z(Integer("1")); |
| 230 | + } |
| 231 | + return coeffs; |
| 232 | + }; |
| 233 | + |
| 234 | + std::vector<Integer> a = (period_start_ < 0) ? normalize(coefficients_) : coefficients_; |
| 235 | + std::vector<Integer> b = (other.period_start_ < 0) ? normalize(other.coefficients_) : other.coefficients_; |
| 236 | + |
| 237 | + size_t min_len = std::min(a.size(), b.size()); |
225 | 238 |
|
226 | 239 | for (size_t i = 0; i < min_len; ++i) { |
227 | | - int cmp = coefficients_[i].COM_ZZ_D(other.coefficients_[i]); |
| 240 | + int cmp = a[i].COM_ZZ_D(b[i]); |
228 | 241 | if (cmp != 0) { |
229 | 242 | // Для чётных позиций: больший коэффициент = большая дробь |
230 | 243 | // Для нечётных позиций: больший коэффициент = меньшая дробь |
| 244 | + // COM_ZZ_D возвращает: 1 если this > other, -1 если this < other |
231 | 245 | if (i % 2 == 0) { |
232 | | - return (cmp == 2) ? 1 : -1; |
| 246 | + return cmp; // на чётной позиции порядок прямой |
233 | 247 | } else { |
234 | | - return (cmp == 2) ? -1 : 1; |
| 248 | + return -cmp; // на нечётной позиции порядок обратный |
235 | 249 | } |
236 | 250 | } |
237 | 251 | } |
238 | 252 |
|
239 | | - if (coefficients_.size() == other.coefficients_.size()) { |
| 253 | + if (a.size() == b.size()) { |
| 254 | + // Для периодических дробей также сравниваем начало периода |
| 255 | + if (period_start_ >= 0 && other.period_start_ >= 0) { |
| 256 | + if (period_start_ != other.period_start_) { |
| 257 | + return (period_start_ < other.period_start_) ? -1 : 1; |
| 258 | + } |
| 259 | + } |
240 | 260 | return 0; |
241 | 261 | } |
242 | 262 |
|
243 | | - if (coefficients_.size() > other.coefficients_.size()) { |
244 | | - return (min_len % 2 == 0) ? 1 : -1; |
245 | | - } else { |
| 263 | + // Более короткая дробь эквивалентна дроби с "бесконечностью" на позиции min_len |
| 264 | + if (a.size() > b.size()) { |
246 | 265 | return (min_len % 2 == 0) ? -1 : 1; |
| 266 | + } else { |
| 267 | + // other длиннее, this короче (this имеет "∞" на позиции min_len) |
| 268 | + return (min_len % 2 == 0) ? 1 : -1; |
247 | 269 | } |
248 | 270 | } |
249 | 271 |
|
@@ -464,6 +486,202 @@ std::vector<Integer> ContinuedFraction::GET_APERIODIC_CF() const { |
464 | 486 | return aperiodic; |
465 | 487 | } |
466 | 488 |
|
| 489 | +// CF-13 | Преобразование периодической цепной дроби в квадратичную иррациональность |
| 490 | +// Возвращает (a, D, c) такие что дробь = (a + sqrt(D)) / c |
| 491 | +std::tuple<Integer, Natural, Integer> ContinuedFraction::TO_CF_QUAD() const { |
| 492 | + // Если дробь не периодическая, возвращаем рациональное число |
| 493 | + if (!IS_PERIODIC_CF()) { |
| 494 | + Rational r = TO_CF_Q(); |
| 495 | + return {r.numerator(), Natural("0"), Integer(r.denominator().as_string())}; |
| 496 | + } |
| 497 | + |
| 498 | + // Получаем период и непериодическую часть |
| 499 | + std::vector<Integer> period = GET_PERIOD_CF(); |
| 500 | + std::vector<Integer> aperiodic = GET_APERIODIC_CF(); |
| 501 | + |
| 502 | + if (period.empty()) { |
| 503 | + Rational r = TO_CF_Q(); |
| 504 | + return {r.numerator(), Natural("0"), Integer(r.denominator().as_string())}; |
| 505 | + } |
| 506 | + |
| 507 | + // Шаг 1: Вычисляем конвергенты периода [b1; b2, ..., bk] |
| 508 | + // Для уравнения x = (p_k + p_{k-1} * x) / (q_k + q_{k-1} * x) |
| 509 | + // где x = [b1; b2, ..., bk, x] - чисто периодическая часть |
| 510 | + |
| 511 | + Integer p_prev = Integer("1"); // p_{-1} = 1 |
| 512 | + Integer q_prev = Integer("0"); // q_{-1} = 0 |
| 513 | + Integer p_curr = period[0]; // p_0 = b_1 |
| 514 | + Integer q_curr = Integer("1"); // q_0 = 1 |
| 515 | + |
| 516 | + for (size_t i = 1; i < period.size(); ++i) { |
| 517 | + Integer p_new = period[i].MUL_ZZ_Z(p_curr).ADD_ZZ_Z(p_prev); |
| 518 | + Integer q_new = period[i].MUL_ZZ_Z(q_curr).ADD_ZZ_Z(q_prev); |
| 519 | + |
| 520 | + p_prev = p_curr; |
| 521 | + q_prev = q_curr; |
| 522 | + p_curr = p_new; |
| 523 | + q_curr = q_new; |
| 524 | + } |
| 525 | + |
| 526 | + // p_k = p_curr, p_{k-1} = p_prev |
| 527 | + // q_k = q_curr, q_{k-1} = q_prev |
| 528 | + |
| 529 | + // Шаг 2: Решаем квадратное уравнение для чисто периодической части |
| 530 | + // x = [b1; b2, ..., bk, x] |
| 531 | + // x = b1 + 1/(b2 + 1/(...+ 1/(bk + 1/x))) |
| 532 | + // Используя конвергенты: x = (p_k * x + p_{k-1}) / (q_k * x + q_{k-1}) |
| 533 | + // => x * (q_k * x + q_{k-1}) = p_k * x + p_{k-1} |
| 534 | + // => q_k * x^2 + q_{k-1} * x = p_k * x + p_{k-1} |
| 535 | + // => q_k * x^2 + (q_{k-1} - p_k) * x - p_{k-1} = 0 |
| 536 | + |
| 537 | + Integer coef_a = q_curr; // q_k |
| 538 | + Integer coef_b = q_prev.SUB_ZZ_Z(p_curr); // q_{k-1} - p_k |
| 539 | + Integer coef_c = p_prev.MUL_ZM_Z(); // -p_{k-1} |
| 540 | + |
| 541 | + // D = b^2 - 4ac = (q_{k-1} - p_k)^2 + 4 * q_k * p_{k-1} |
| 542 | + Integer b_sq = coef_b.MUL_ZZ_Z(coef_b); |
| 543 | + Integer four = Integer("4"); |
| 544 | + Integer four_ac = four.MUL_ZZ_Z(coef_a).MUL_ZZ_Z(p_prev); |
| 545 | + Integer D_int = b_sq.ADD_ZZ_Z(four_ac); |
| 546 | + |
| 547 | + // D должен быть неотрицательным |
| 548 | + if (D_int.SGN_Z_D() < 0) { |
| 549 | + Rational r = TO_CF_Q(); |
| 550 | + return {r.numerator(), Natural("0"), Integer(r.denominator().as_string())}; |
| 551 | + } |
| 552 | + |
| 553 | + Natural D_nat = D_int.ABS_Z_N(); |
| 554 | + |
| 555 | + // x = (-b + sqrt(D)) / (2a) = (p_k - q_{k-1} + sqrt(D)) / (2 * q_k) |
| 556 | + Integer x_num = coef_b.MUL_ZM_Z(); // -coef_b = p_k - q_{k-1} |
| 557 | + Integer x_den = Integer("2").MUL_ZZ_Z(coef_a); // 2 * q_k |
| 558 | + |
| 559 | + // Если нет непериодической части, результат = x |
| 560 | + if (aperiodic.empty()) { |
| 561 | + // Нормализуем знак знаменателя |
| 562 | + if (x_den.SGN_Z_D() < 0) { |
| 563 | + x_num = x_num.MUL_ZM_Z(); |
| 564 | + x_den = x_den.MUL_ZM_Z(); |
| 565 | + } |
| 566 | + return {x_num, D_nat, x_den}; |
| 567 | + } |
| 568 | + |
| 569 | + // Шаг 3: Учитываем непериодическую часть |
| 570 | + // result = [a0; a1, ..., a_{m-1}, x] |
| 571 | + // result = (P_{m-1} * x + P_{m-2}) / (Q_{m-1} * x + Q_{m-2}) |
| 572 | + // где P_i/Q_i - конвергенты непериодической части |
| 573 | + |
| 574 | + Integer P_prev = Integer("1"); // P_{-1} = 1 |
| 575 | + Integer Q_prev = Integer("0"); // Q_{-1} = 0 |
| 576 | + Integer P_curr = aperiodic[0]; // P_0 = a_0 |
| 577 | + Integer Q_curr = Integer("1"); // Q_0 = 1 |
| 578 | + |
| 579 | + for (size_t i = 1; i < aperiodic.size(); ++i) { |
| 580 | + Integer P_new = aperiodic[i].MUL_ZZ_Z(P_curr).ADD_ZZ_Z(P_prev); |
| 581 | + Integer Q_new = aperiodic[i].MUL_ZZ_Z(Q_curr).ADD_ZZ_Z(Q_prev); |
| 582 | + |
| 583 | + P_prev = P_curr; |
| 584 | + Q_prev = Q_curr; |
| 585 | + P_curr = P_new; |
| 586 | + Q_curr = Q_new; |
| 587 | + } |
| 588 | + |
| 589 | + // P_{m-1} = P_curr, P_{m-2} = P_prev |
| 590 | + // Q_{m-1} = Q_curr, Q_{m-2} = Q_prev |
| 591 | + |
| 592 | + // result = (P_{m-1} * x + P_{m-2}) / (Q_{m-1} * x + Q_{m-2}) |
| 593 | + // где x = (x_num + sqrt(D)) / x_den |
| 594 | + // |
| 595 | + // Подставляем: |
| 596 | + // числитель = P_{m-1} * (x_num + sqrt(D)) / x_den + P_{m-2} |
| 597 | + // = (P_{m-1} * x_num + P_{m-2} * x_den + P_{m-1} * sqrt(D)) / x_den |
| 598 | + // = (A + B * sqrt(D)) / x_den |
| 599 | + // где A = P_{m-1} * x_num + P_{m-2} * x_den, B = P_{m-1} |
| 600 | + // |
| 601 | + // знаменатель = Q_{m-1} * (x_num + sqrt(D)) / x_den + Q_{m-2} |
| 602 | + // = (Q_{m-1} * x_num + Q_{m-2} * x_den + Q_{m-1} * sqrt(D)) / x_den |
| 603 | + // = (C + E * sqrt(D)) / x_den |
| 604 | + // где C = Q_{m-1} * x_num + Q_{m-2} * x_den, E = Q_{m-1} |
| 605 | + // |
| 606 | + // result = (A + B * sqrt(D)) / (C + E * sqrt(D)) |
| 607 | + // |
| 608 | + // Рационализируем знаменатель: |
| 609 | + // result = (A + B * sqrt(D)) * (C - E * sqrt(D)) / ((C + E * sqrt(D)) * (C - E * sqrt(D))) |
| 610 | + // = (A*C - B*E*D + (B*C - A*E) * sqrt(D)) / (C^2 - E^2 * D) |
| 611 | + |
| 612 | + Integer A = P_curr.MUL_ZZ_Z(x_num).ADD_ZZ_Z(P_prev.MUL_ZZ_Z(x_den)); |
| 613 | + Integer B = P_curr; |
| 614 | + Integer C = Q_curr.MUL_ZZ_Z(x_num).ADD_ZZ_Z(Q_prev.MUL_ZZ_Z(x_den)); |
| 615 | + Integer E = Q_curr; |
| 616 | + |
| 617 | + // числитель: (A*C - B*E*D) + (B*C - A*E) * sqrt(D) |
| 618 | + Integer D_as_int = Integer(D_nat.as_string()); |
| 619 | + Integer AC = A.MUL_ZZ_Z(C); |
| 620 | + Integer BED = B.MUL_ZZ_Z(E).MUL_ZZ_Z(D_as_int); |
| 621 | + Integer result_const = AC.SUB_ZZ_Z(BED); // A*C - B*E*D |
| 622 | + |
| 623 | + Integer BC = B.MUL_ZZ_Z(C); |
| 624 | + Integer AE = A.MUL_ZZ_Z(E); |
| 625 | + Integer result_sqrt_coef = BC.SUB_ZZ_Z(AE); // B*C - A*E |
| 626 | + |
| 627 | + // знаменатель: C^2 - E^2 * D |
| 628 | + Integer C_sq = C.MUL_ZZ_Z(C); |
| 629 | + Integer E_sq_D = E.MUL_ZZ_Z(E).MUL_ZZ_Z(D_as_int); |
| 630 | + Integer result_den = C_sq.SUB_ZZ_Z(E_sq_D); |
| 631 | + |
| 632 | + // Результат: (result_const + result_sqrt_coef * sqrt(D)) / result_den |
| 633 | + |
| 634 | + // Упрощаем: делим всё на result_sqrt_coef (если он не 0) |
| 635 | + if (result_sqrt_coef.SGN_Z_D() == 0) { |
| 636 | + // Нет sqrt(D) в результате - это рациональное число |
| 637 | + if (result_den.SGN_Z_D() < 0) { |
| 638 | + result_const = result_const.MUL_ZM_Z(); |
| 639 | + result_den = result_den.MUL_ZM_Z(); |
| 640 | + } |
| 641 | + // Сокращаем |
| 642 | + Natural gcd = result_const.ABS_Z_N().GCF_NN_N(result_den.ABS_Z_N()); |
| 643 | + if (gcd.NZER_N_B() && gcd.COM_NN_D(Natural("1")) != 0) { |
| 644 | + result_const = result_const.DIV_ZZ_Z(Integer(gcd.as_string())); |
| 645 | + result_den = result_den.DIV_ZZ_Z(Integer(gcd.as_string())); |
| 646 | + } |
| 647 | + return {result_const, Natural("0"), result_den}; |
| 648 | + } |
| 649 | + |
| 650 | + // Находим НОД всех трёх коэффициентов |
| 651 | + Natural abs_const = result_const.ABS_Z_N(); |
| 652 | + Natural abs_coef = result_sqrt_coef.ABS_Z_N(); |
| 653 | + Natural abs_den = result_den.ABS_Z_N(); |
| 654 | + |
| 655 | + Natural gcd = abs_const.GCF_NN_N(abs_coef); |
| 656 | + gcd = gcd.GCF_NN_N(abs_den); |
| 657 | + |
| 658 | + if (gcd.NZER_N_B() && gcd.COM_NN_D(Natural("1")) != 0) { |
| 659 | + result_const = result_const.DIV_ZZ_Z(Integer(gcd.as_string())); |
| 660 | + result_sqrt_coef = result_sqrt_coef.DIV_ZZ_Z(Integer(gcd.as_string())); |
| 661 | + result_den = result_den.DIV_ZZ_Z(Integer(gcd.as_string())); |
| 662 | + } |
| 663 | + |
| 664 | + // Нормализуем знак знаменателя |
| 665 | + if (result_den.SGN_Z_D() < 0) { |
| 666 | + result_const = result_const.MUL_ZM_Z(); |
| 667 | + result_sqrt_coef = result_sqrt_coef.MUL_ZM_Z(); |
| 668 | + result_den = result_den.MUL_ZM_Z(); |
| 669 | + } |
| 670 | + |
| 671 | + // Если коэффициент при sqrt(D) отрицательный, меняем знаки |
| 672 | + if (result_sqrt_coef.SGN_Z_D() < 0) { |
| 673 | + result_const = result_const.MUL_ZM_Z(); |
| 674 | + result_sqrt_coef = result_sqrt_coef.MUL_ZM_Z(); |
| 675 | + result_den = result_den.MUL_ZM_Z(); |
| 676 | + } |
| 677 | + |
| 678 | + // D_new = k^2 * D, где k = result_sqrt_coef |
| 679 | + Natural k_nat = result_sqrt_coef.ABS_Z_N(); |
| 680 | + Natural D_new = k_nat.MUL_NN_N(k_nat).MUL_NN_N(D_nat); |
| 681 | + |
| 682 | + return {result_const, D_new, result_den}; |
| 683 | +} |
| 684 | + |
467 | 685 | // Строковое представление |
468 | 686 | std::string ContinuedFraction::as_string() const { |
469 | 687 | if (coefficients_.empty()) { |
|
0 commit comments