diff --git a/app/src/ALGORITHM_DOCUMENTATION.md b/app/src/ALGORITHM_DOCUMENTATION.md new file mode 100644 index 0000000..88f12d5 --- /dev/null +++ b/app/src/ALGORITHM_DOCUMENTATION.md @@ -0,0 +1,342 @@ +# Документация алгоритма пошагового решения задач с системами счисления + +## Описание + +Алгоритм для пошагового решения задач с различными системами счисления (2, 8, 16, n-ричные), аналогичный работе Photomath. Показывает каждый шаг решения с подробными объяснениями. + +## Основные возможности + +### 1. Поддерживаемые системы счисления +- **Двоичная (base=2)**: 0, 1 +- **Восьмеричная (base=8)**: 0-7 +- **Десятичная (base=10)**: 0-9 +- **Шестнадцатеричная (base=16)**: 0-9, A-F +- **Любая n-ричная (base=n)**: до base=36 + +### 2. Поддерживаемые операции +- Сложение (+) +- Вычитание (-) +- Преобразование между системами счисления + +## Структура алгоритма + +### Класс `Step` +Представляет один шаг решения: +- `title` - название шага +- `description` - описание что делается +- `visual` - визуальное представление (формулы, вычисления) +- `result` - результат этого шага + +### Класс `StepByStepSolver` +Основной класс для решения задач: + +#### Методы: +- `solve(num1, num2, operation)` - решить задачу +- `add_with_steps(num1, num2)` - сложение с шагами +- `subtract_with_steps(num1, num2)` - вычитание с шагами +- `convert_with_steps(number, target_base)` - конвертация с шагами +- `print_solution(steps)` - вывод решения + +## Алгоритм сложения (подробно) + +### Шаг 1: Анализ условия +``` +Входные данные: +- Число 1 в n-ричной системе +- Число 2 в n-ричной системе +- Основание системы счисления (n) + +Пример: 1011₍₂₎ + 110₍₂₎ +``` + +### Шаг 2: Выравнивание разрядов +``` +Алгоритм: +1. Определить максимальную длину чисел +2. Дополнить короткое число нулями слева + +Пример: + 1011 (уже 4 разряда) ++ 0110 (дополнено до 4 разрядов) +------ +``` + +### Шаг 3: Сложение в столбик (справа налево) +``` +Для каждого разряда i (от младшего к старшему): +1. Взять цифру d1 из первого числа +2. Взять цифру d2 из второго числа +3. Добавить перенос carry из предыдущего разряда +4. Вычислить: total = d1 + d2 + carry +5. Результат в разряде: digit = total mod base +6. Новый перенос: carry = total div base +7. Записать digit в результат + +Пример (двоичная система, base=2): + +Позиция 0 (самая правая): + 1 + 0 + 0(перенос) = 1 + 1 mod 2 = 1 (цифра результата) + 1 div 2 = 0 (новый перенос) + +Позиция 1: + 1 + 1 + 0(перенос) = 2 + 2 mod 2 = 0 (цифра результата) + 2 div 2 = 1 (новый перенос) + +Позиция 2: + 0 + 1 + 1(перенос) = 2 + 2 mod 2 = 0 (цифра результата) + 2 div 2 = 1 (новый перенос) + +Позиция 3: + 1 + 0 + 1(перенос) = 2 + 2 mod 2 = 0 (цифра результата) + 2 div 2 = 1 (новый перенос) + +Финальный перенос: 1 + +Результат: 10001₍₂₎ +``` + +### Шаг 4: Проверка через десятичную систему +``` +Алгоритм проверки: +1. Перевести оба числа в десятичную систему +2. Выполнить операцию в десятичной системе +3. Перевести результат обратно в исходную систему +4. Сравнить с полученным результатом + +Пример: +1011₍₂₎ = 1×2³ + 0×2² + 1×2¹ + 1×2⁰ = 8+0+2+1 = 11₍₁₀₎ +0110₍₂₎ = 0×2³ + 1×2² + 1×2¹ + 0×2⁰ = 0+4+2+0 = 6₍₁₀₎ +11 + 6 = 17₍₁₀₎ +17₍₁₀₎ = 10001₍₂₎ ✓ +``` + +## Алгоритм вычитания (подробно) + +### Шаг 1: Анализ условия +``` +Входные данные: +- Уменьшаемое в n-ричной системе +- Вычитаемое в n-ричной системе +- Основание системы счисления (n) + +Пример: 1101₍₂₎ - 1011₍₂₎ +``` + +### Шаг 2: Выравнивание разрядов +``` +Аналогично сложению - дополнение нулями слева +``` + +### Шаг 3: Вычитание в столбик (справа налево) +``` +Для каждого разряда i (от младшего к старшему): +1. Взять цифру d1 из уменьшаемого +2. Взять цифру d2 из вычитаемого +3. Учесть заем borrow из текущего разряда +4. Если (d1 - borrow) < d2: + - Занять base из следующего разряда + - d1 = d1 + base + - borrow = 1 для следующей итерации +5. Иначе: + - borrow = 0 +6. Результат: digit = d1 - borrow - d2 + +Пример (двоичная система, base=2): + + 1101 +- 1011 +------ + +Позиция 0: + 1 - 1 = 0 (заем не нужен) + +Позиция 1: + 0 - 1 → нужен заем + 0 + 2 - 1 = 1 (занимаем 2) + Новый заем = 1 + +Позиция 2: + 1 - 1(заем) = 0 + 0 - 0 = 0 + +Позиция 3: + 1 - 1 = 0 + +Результат: 0010₍₂₎ = 10₍₂₎ +``` + +### Шаг 4: Проверка +``` +Аналогично сложению - проверка через десятичную систему +``` + +## Алгоритм конвертации между системами + +### Из n-ричной в десятичную +``` +Алгоритм: +1. Для каждой цифры digit на позиции i (справа налево, начиная с 0): + result += digit_value × base^i + +Пример: 2A₍₁₆₎ → 10-ичная + A×16⁰ = 10×1 = 10 + 2×16¹ = 2×16 = 32 + Сумма: 10 + 32 = 42₍₁₀₎ +``` + +### Из десятичной в n-ричную +``` +Алгоритм: +1. Делим число на base, записываем остаток +2. Результат деления становится новым числом +3. Повторяем пока число > 0 +4. Читаем остатки снизу вверх + +Пример: 42₍₁₀₎ → 16-ричная + 42 ÷ 16 = 2, остаток: 10 (A) + 2 ÷ 16 = 0, остаток: 2 + + Читаем снизу вверх: 2A₍₁₆₎ +``` + +## Использование в коде + +### Пример 1: Сложение в двоичной системе +```python +from step_by_step_solver import StepByStepSolver, Operation + +# Создаем решатель для двоичной системы +solver = StepByStepSolver(base=2) + +# Решаем задачу +steps = solver.solve("1011", "110", Operation.ADDITION) + +# Выводим решение +solver.print_solution(steps) +``` + +### Пример 2: Вычитание в восьмеричной системе +```python +solver = StepByStepSolver(base=8) +steps = solver.solve("756", "237", Operation.SUBTRACTION) +solver.print_solution(steps) +``` + +### Пример 3: Получение шагов для UI +```python +solver = StepByStepSolver(base=16) +steps = solver.solve("A5", "3F", Operation.ADDITION) + +# Обработка шагов для отображения в интерфейсе +for i, step in enumerate(steps, 1): + print(f"Шаг {i}: {step.title}") + print(f"Описание: {step.description}") + print(f"Визуализация:\n{step.visual}") + print(f"Результат: {step.result}\n") +``` + +## Интеграция с Flet UI + +### Пример компонента для отображения решения +```python +import flet as ft +from step_by_step_solver import StepByStepSolver, Operation + +class StepByStepView(ft.Column): + def __init__(self, base: int): + super().__init__() + self.solver = StepByStepSolver(base=base) + self.steps_display = ft.Column(scroll=ft.ScrollMode.AUTO) + + def show_solution(self, num1: str, num2: str, operation: Operation): + # Получить шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очистить предыдущее решение + self.steps_display.controls.clear() + + # Добавить каждый шаг в UI + for i, step in enumerate(steps, 1): + step_container = ft.Container( + content=ft.Column([ + ft.Text(f"Шаг {i}: {step.title}", + size=18, weight=ft.FontWeight.BOLD), + ft.Text(step.description, size=14), + ft.Text(step.visual, + font_family="Courier New", size=12) if step.visual else None, + ft.Text(f"➤ {step.result}", + size=14, color="green") if step.result else None, + ]), + bgcolor="#3A1E51", + padding=15, + border_radius=10, + margin=ft.margin.only(bottom=10) + ) + self.steps_display.controls.append(step_container) + + self.update() +``` + +## Расширение алгоритма + +### Добавление новых операций +```python +class Operation(Enum): + ADDITION = "сложение" + SUBTRACTION = "вычитание" + MULTIPLICATION = "умножение" # Новая операция + DIVISION = "деление" # Новая операция +``` + +### Поддержка дробных чисел +```python +def add_fraction_support(self): + # Обработка чисел с дробной частью + # Разделение на целую и дробную части + # Отдельная обработка каждой части + pass +``` + +## Оптимизация и производительность + +### Кэширование результатов +```python +from functools import lru_cache + +@lru_cache(maxsize=128) +def to_decimal_cached(self, number: str) -> int: + return self.to_decimal(number) +``` + +### Ограничение количества шагов +```python +def solve_optimized(self, num1: str, num2: str, operation: Operation, + max_steps: int = 10) -> List[Step]: + steps = self.solve(num1, num2, operation) + return steps[:max_steps] # Ограничение +``` + +## Тестирование + +### Примеры тестовых случаев +```python +def test_binary_addition(): + solver = StepByStepSolver(base=2) + steps = solver.solve("1011", "110", Operation.ADDITION) + assert len(steps) == 4 + assert steps[-1].result == "10001₍₂₎" + +def test_octal_subtraction(): + solver = StepByStepSolver(base=8) + steps = solver.solve("756", "237", Operation.SUBTRACTION) + assert len(steps) == 4 + assert steps[-1].result == "517₍₈₎" +``` + +## Заключение + +Этот алгоритм предоставляет полное пошаговое решение задач с системами счисления, аналогично Photomath. Он может быть легко интегрирован в UI приложения и расширен дополнительными функциями. diff --git a/app/src/README_SOLVER.md b/app/src/README_SOLVER.md new file mode 100644 index 0000000..475ff3d --- /dev/null +++ b/app/src/README_SOLVER.md @@ -0,0 +1,233 @@ +# Калькулятор систем счисления с пошаговым решением + +## Описание + +Интерактивный калькулятор для работы с различными системами счисления (2, 8, 10, 16-ричные) с **пошаговым объяснением решения** аналогично Photomath. + +## Возможности ✨ + +- ✅ Сложение и вычитание в двоичной системе (с дополнительным кодом) +- ✅ Сложение и вычитание в восьмеричной системе +- ✅ Сложение и вычитание в десятичной системе +- ✅ Сложение и вычитание в шестнадцатеричной системе +- 📝 **Пошаговое объяснение каждого решения** +- ✓ Автоматическая проверка результатов +- 🎨 Красивый UI с цветовой подсветкой + +## Быстрый старт + +### Запуск примера алгоритма + +```bash +cd app/src +python3 step_by_step_solver.py +``` + +Вы увидите примеры пошагового решения для разных систем счисления. + +### Использование в коде + +```python +from step_by_step_solver import StepByStepSolver, Operation + +# Создать решатель для двоичной системы +solver = StepByStepSolver(base=2) + +# Решить задачу +steps = solver.solve("1011", "110", Operation.ADDITION) + +# Вывести решение +solver.print_solution(steps) +``` + +### Пример вывода + +``` +═══════════════════════════════════════════════════════════ +ПОШАГОВОЕ РЕШЕНИЕ +═══════════════════════════════════════════════════════════ + +████████████████████████████████████████████████████████████ +ШАГ 1: Условие задачи +████████████████████████████████████████████████████████████ + +Необходимо сложить два числа в двоичная (2-ричной) системе счисления + +1011₍2₎ + 110₍2₎ = ? + +████████████████████████████████████████████████████████████ +ШАГ 2: Выравнивание чисел +████████████████████████████████████████████████████████████ + +Дополняем числа нулями слева до одинаковой длины (4 разрядов) + + 1011 ++ 0110 +------ + +████████████████████████████████████████████████████████████ +ШАГ 3: Сложение в столбик +████████████████████████████████████████████████████████████ + +Складываем числа справа налево, учитывая переносы... + +➤ Результат: 10001₍2₎ + +████████████████████████████████████████████████████████████ +ШАГ 4: Проверка результата +████████████████████████████████████████████████████████████ + +Проверим правильность через перевод в десятичную систему + +1011₍2₎ = 11₍₁₀₎ +110₍2₎ = 6₍₁₀₎ +11 + 6 = 17 +17₍₁₀₎ = 10001₍2₎ + +➤ Результат: ✓ Ответ верен: 10001₍2₎ + +═══════════════════════════════════════════════════════════ +РЕШЕНИЕ ЗАВЕРШЕНО +═══════════════════════════════════════════════════════════ +``` + +## Структура проекта + +``` +app/src/ +├── step_by_step_solver.py # Основной алгоритм пошагового решения +├── calculator_mixin.py # Миксин для интеграции в UI +├── ALGORITHM_DOCUMENTATION.md # Подробная документация алгоритма +├── USER_GUIDE.md # Руководство пользователя +└── pages/tabs/ + ├── binary_calculeator4.py # Двоичный калькулятор с пошаговым решением + ├── calculater2.py # Десятичный калькулятор с пошаговым решением + ├── octogon_calculeator6.py # Восьмеричный калькулятор с пошаговым решением + └── sedecim_calculeator8.py # Шестнадцатеричный калькулятор с пошаговым решением +``` + +## Ключевые особенности алгоритма + +### 1. Универсальность +Работает с любой системой счисления от 2 до 36: + +```python +binary_solver = StepByStepSolver(base=2) # Двоичная +octal_solver = StepByStepSolver(base=8) # Восьмеричная +decimal_solver = StepByStepSolver(base=10) # Десятичная +hex_solver = StepByStepSolver(base=16) # Шестнадцатеричная +quinary_solver = StepByStepSolver(base=5) # Пятеричная +``` + +### 2. Подробное объяснение +Каждый шаг включает: +- 📋 Название и описание +- 📐 Визуализация вычислений +- ✅ Промежуточные результаты +- 🔍 Проверка правильности + +### 3. Поддерживаемые операции +- ➕ Сложение (с переносами) +- ➖ Вычитание (с заимствованиями) +- 🔄 Конвертация между системами (в разработке) + +## Примеры использования + +### Двоичное сложение +```python +solver = StepByStepSolver(base=2) +steps = solver.add_with_steps("1011", "110") +# Результат: 10001₍₂₎ +``` + +### Восьмеричное вычитание +```python +solver = StepByStepSolver(base=8) +steps = solver.subtract_with_steps("756", "237") +# Результат: 517₍₈₎ +``` + +### Шестнадцатеричное сложение +```python +solver = StepByStepSolver(base=16) +steps = solver.add_with_steps("A5", "3F") +# Результат: E4₍₁₆₎ +``` + +## Интеграция в UI (Flet) + +Калькуляторы уже интегрированы с пошаговым решением. При нажатии кнопки "=" автоматически: +1. Вычисляется результат +2. Генерируется пошаговое решение +3. Отображается в специальной области под калькулятором + +### Визуальное оформление +- 🟡 Золотой цвет - заголовки +- 🔵 Циан - названия шагов +- 🟢 Зеленый - результаты +- ⚪ Серый - описания + +## API алгоритма + +### Класс `StepByStepSolver` + +#### Методы: +- `solve(num1, num2, operation)` - Решить задачу с пошаговым объяснением +- `add_with_steps(num1, num2)` - Сложение с шагами +- `subtract_with_steps(num1, num2)` - Вычитание с шагами +- `convert_with_steps(number, target_base)` - Конвертация с шагами +- `print_solution(steps)` - Вывести решение в консоль + +#### Класс `Step` +Представляет один шаг решения: +- `title` - Название шага +- `description` - Описание что делается +- `visual` - Визуальное представление +- `result` - Результат шага + +## Тестирование + +Запустите встроенные примеры: + +```bash +python3 step_by_step_solver.py +``` + +Вы увидите 4 примера: +1. Сложение в двоичной системе +2. Вычитание в восьмеричной системе +3. Сложение в шестнадцатеричной системе +4. Сложение в пятеричной системе + +## Документация + +- 📘 [ALGORITHM_DOCUMENTATION.md](ALGORITHM_DOCUMENTATION.md) - Полное описание алгоритма +- 📗 [USER_GUIDE.md](USER_GUIDE.md) - Руководство пользователя + +## Технологии + +- Python 3.x +- Flet (для UI) +- Enum (для типов операций) +- Type hints (для типобезопасности) + +## Будущие улучшения + +- [ ] Поддержка умножения и деления +- [ ] Работа с дробными числами +- [ ] Конвертация между любыми системами счисления +- [ ] Экспорт решения в PDF +- [ ] История вычислений +- [ ] Темная/светлая тема + +## Лицензия + +Этот проект разработан для образовательных целей. + +## Авторы + +Разработано как часть проекта калькулятора систем счисления. + +--- + +**Примечание**: Алгоритм полностью автономен и может использоваться отдельно от UI калькулятора. diff --git a/app/src/USER_GUIDE.md b/app/src/USER_GUIDE.md new file mode 100644 index 0000000..2cf2732 --- /dev/null +++ b/app/src/USER_GUIDE.md @@ -0,0 +1,249 @@ +# Руководство пользователя - Калькулятор с системами счисления + +## Обзор + +Калькулятор с пошаговым решением задач для различных систем счисления (2, 8, 10, 16-ричные). Каждая операция сопровождается подробным объяснением хода решения, аналогично приложению Photomath. + +## Доступные калькуляторы + +### 1. Двоичный калькулятор (base=2) +- **Система счисления**: Двоичная (цифры: 0, 1) +- **Функции**: Сложение и вычитание двоичных чисел +- **Особенности**: + - Поддержка дополнительного кода для отрицательных чисел + - Разрядность: 16 бит + - Автоматическое определение знаковых чисел + +**Пример использования:** +``` +Ввод: 1011 + 110 +Результат: 10001 +Пошаговое решение покажет: +- Выравнивание разрядов +- Сложение в столбик с переносами +- Проверку через десятичную систему +``` + +### 2. Восьмеричный калькулятор (base=8) +- **Система счисления**: Восьмеричная (цифры: 0-7) +- **Функции**: Сложение и вычитание восьмеричных чисел +- **Особенности**: Поддержка отрицательных чисел + +**Пример использования:** +``` +Ввод: 756 - 237 +Результат: 517₍₈₎ = 335₍₁₀₎ +``` + +### 3. Десятичный калькулятор (base=10) +- **Система счисления**: Десятичная (цифры: 0-9) +- **Функции**: Сложение и вычитание целых чисел +- **Особенности**: Стандартная арифметика с подробным объяснением + +### 4. Шестнадцатеричный калькулятор (base=16) +- **Система счисления**: Шестнадцатеричная (цифры: 0-9, A-F) +- **Функции**: Сложение и вычитание шестнадцатеричных чисел +- **Особенности**: + - Поддержка дробных чисел (пошаговое решение только для целых) + - Автоматическое преобразование в десятичную систему + +**Пример использования:** +``` +Ввод: A5 + 3F +Результат: E4₍₁₆₎ = 228₍₁₀₎ +``` + +## Как использовать пошаговое решение + +### Что показывается в решении: + +#### Шаг 1: Условие задачи +- Исходные числа +- Тип операции (сложение/вычитание) +- Система счисления + +#### Шаг 2: Выравнивание чисел +- Дополнение нулями слева до одинаковой длины +- Визуальное представление выровненных чисел + +#### Шаг 3: Вычисление в столбик +- **Для сложения:** + - Пошаговое сложение справа налево + - Объяснение переносов + - Когда сумма ≥ base: остаток идет в результат, частное - в перенос + +- **Для вычитания:** + - Пошаговое вычитание справа налево + - Объяснение заимствований + - Когда нужно занять из следующего разряда + +#### Шаг 4: Проверка результата +- Перевод в десятичную систему +- Вычисление в десятичной системе +- Обратный перевод и сравнение + +### Визуальное представление + +Каждый шаг содержит: +- 📝 **Заголовок**: Краткое название шага +- **Описание**: Что делается на этом шаге +- **Визуализация**: Формулы и вычисления в монoширинном шрифте +- ➤ **Результат**: Итог шага (выделен зеленым) + +### Цветовая схема + +- 🟡 **Золотой** (#FFD700): Заголовки разделов +- 🔵 **Циан** (#00FFFF): Названия шагов +- ⚪ **Светло-серый** (#E0E0E0): Описания +- 🟢 **Светло-зеленый** (#90EE90): Визуализация вычислений +- 🟢 **Зеленый** (#00FF00): Результаты + +## Примеры работы алгоритма + +### Пример 1: Сложение в двоичной системе + +**Задача:** 1011₍₂₎ + 110₍₂₎ + +**Решение:** + +``` +Шаг 1: Условие задачи +1011₍₂₎ + 110₍₂₎ = ? + +Шаг 2: Выравнивание + 1011 ++ 0110 +------ + +Шаг 3: Сложение в столбик +Позиция 0: 1 + 0 = 1 +Позиция 1: 1 + 1 = 2 = 0 (перенос 1) +Позиция 2: 0 + 1 + перенос(1) = 2 = 0 (перенос 1) +Позиция 3: 1 + 0 + перенос(1) = 2 = 0 (перенос 1) +Финальный перенос: 1 + +Результат: 10001₍₂₎ + +Шаг 4: Проверка +1011₍₂₎ = 11₍₁₀₎ +0110₍₂₎ = 6₍₁₀₎ +11 + 6 = 17 +17₍₁₀₎ = 10001₍₂₎ ✓ +``` + +### Пример 2: Вычитание в восьмеричной системе + +**Задача:** 756₍₈₎ - 237₍₈₎ + +**Решение:** + +``` +Шаг 1: Условие задачи +756₍₈₎ - 237₍₈₎ = ? + +Шаг 2: Выравнивание + 756 +- 237 +----- + +Шаг 3: Вычитание в столбик +Позиция 0: 6 - 7 → занимаем 8, получаем 14 - 7 = 7 +Позиция 1: 5 - заем(1) = 4, 4 - 3 = 1 +Позиция 2: 7 - 2 = 5 + +Результат: 517₍₈₎ + +Шаг 4: Проверка +756₍₈₎ = 494₍₁₀₎ +237₍₈₎ = 159₍₁₀₎ +494 - 159 = 335 +335₍₁₀₎ = 517₍₈₎ ✓ +``` + +### Пример 3: Сложение в шестнадцатеричной системе + +**Задача:** A5₍₁₆₎ + 3F₍₁₆₎ + +**Решение:** + +``` +Шаг 1: Условие задачи +A5₍₁₆₎ + 3F₍₁₆₎ = ? + +Шаг 2: Выравнивание + A5 ++ 3F +---- + +Шаг 3: Сложение в столбик +Позиция 0: 5 + F(15) = 20 = 4 (перенос 1) +Позиция 1: A(10) + 3 + перенос(1) = 14 = E + +Результат: E4₍₁₆₎ + +Шаг 4: Проверка +A5₍₁₆₎ = 165₍₁₀₎ +3F₍₁₆₎ = 63₍₁₀₎ +165 + 63 = 228 +228₍₁₀₎ = E4₍₁₆₎ ✓ +``` + +## Советы по использованию + +### 1. Ввод чисел +- Используйте только допустимые цифры для каждой системы +- Для шестнадцатеричной: можно вводить как заглавные, так и строчные буквы (A-F) +- Отрицательные числа вводите со знаком "-" + +### 2. Интерпретация результатов +- Результат всегда показывается в исходной системе счисления +- Десятичный эквивалент показывается для справки +- Пошаговое решение прокручивается - смотрите все шаги + +### 3. Обучение +- Читайте каждый шаг последовательно +- Обращайте внимание на объяснения переносов и заимствований +- Проверяйте результаты через десятичную систему + +### 4. Ограничения +- Пошаговое решение для шестнадцатеричной системы работает только с целыми числами +- Двоичная система использует дополнительный код для отрицательных чисел + +## Техническая информация + +### Алгоритм работы + +1. **Парсинг ввода**: Проверка корректности введенных чисел +2. **Нормализация**: Выравнивание разрядов +3. **Вычисление**: Операция в столбик с отслеживанием переносов/заимствований +4. **Проверка**: Верификация через десятичную систему +5. **Отображение**: Форматированный вывод всех шагов + +### Файлы модулей + +- `step_by_step_solver.py` - Основной алгоритм решения +- `calculator_mixin.py` - Миксин для интеграции в калькуляторы +- `ALGORITHM_DOCUMENTATION.md` - Подробная документация алгоритма + +## Часто задаваемые вопросы + +**Q: Почему пошаговое решение не отображается для дробных чисел в hex?** +A: Текущая версия алгоритма оптимизирована для целочисленной арифметики. Поддержка дробей планируется в будущих версиях. + +**Q: Как работает дополнительный код в двоичной системе?** +A: Отрицательные числа представляются в дополнительном коде: если старший бит = 1 и длина ≥ 8 бит, число интерпретируется как отрицательное. + +**Q: Можно ли увеличить разрядность для двоичного калькулятора?** +A: Текущая разрядность - 16 бит. Для изменения нужно отредактировать параметр `bit_width` в коде. + +**Q: Поддерживаются ли другие системы счисления (например, пятеричная)?** +A: Алгоритм поддерживает любую систему счисления от 2 до 36. Для добавления нового калькулятора создайте экземпляр `StepByStepSolver(base=n)`. + +## Контакты и поддержка + +Для сообщений об ошибках и предложений по улучшению обращайтесь к разработчикам проекта. + +--- + +*Версия документации: 1.0* +*Дата обновления: 7 ноября 2025 г.* diff --git a/app/src/calculator_mixin.py b/app/src/calculator_mixin.py new file mode 100644 index 0000000..af59105 --- /dev/null +++ b/app/src/calculator_mixin.py @@ -0,0 +1,117 @@ +""" +Миксин для добавления пошагового решения в калькуляторы +""" +import flet as ft +import sys +import os + +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from step_by_step_solver import StepByStepSolver, Operation + + +class StepByStepMixin: + """ + Миксин для добавления функциональности пошагового решения в калькуляторы + """ + + def init_solver(self, base: int): + """Инициализация солвера и контейнеров для шагов""" + self.solver = StepByStepSolver(base=base) + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + + def show_steps(self, steps_container: ft.Column, num1: str, num2: str, operation: Operation): + """ + Отображает пошаговое решение + + Args: + steps_container: контейнер для отображения шагов (self.steps_left или self.steps_right) + num1: первое число (строка) + num2: второе число (строка) + operation: операция (сложение или вычитание) + """ + try: + # Получаем шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очищаем предыдущие шаги + steps_container.controls.clear() + + # Добавляем заголовок + steps_container.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + # Добавляем каждый шаг + for i, step in enumerate(steps, 1): + step_content = [] + + # Название шага + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + + # Описание + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + # Визуализация (если есть) + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + # Результат (если есть) + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + # Добавляем контейнер с шагом + steps_container.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + steps_container.update() + except Exception as ex: + steps_container.controls.clear() + steps_container.controls.append( + ft.Text(f"Ошибка при генерации решения: {str(ex)}", color="red", size=10) + ) + steps_container.update() + + def create_steps_container(self, steps_column: ft.Column, height: int = 400, width: int = 350): + """ + Создает контейнер для отображения шагов решения + + Args: + steps_column: колонка с шагами (self.steps_left или self.steps_right) + height: высота контейнера + width: ширина контейнера + + Returns: + ft.Container с настроенным стилем + """ + return ft.Container( + content=steps_column, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + height=height, + width=width, + ) diff --git a/app/src/designer.py b/app/src/designer.py index 3c2d318..052658d 100644 --- a/app/src/designer.py +++ b/app/src/designer.py @@ -7,4 +7,13 @@ right_column = ft.Column( ) -alphabet="αβγδεζηθικλμνξοπρστυφχψω" \ No newline at end of file +alphabet="αβγδεζηθικλμνξοπρστυφχψω" + + +class Obstr(ft.Column): + def __init__(self): + super().__init__( + controls=[ + + ] + ) \ No newline at end of file diff --git a/app/src/pages/main_page.py b/app/src/pages/main_page.py index 4b8c172..596f685 100644 --- a/app/src/pages/main_page.py +++ b/app/src/pages/main_page.py @@ -1,6 +1,6 @@ import flet as ft import designer as ds -from pages.tabs import start1,calculater2, binary_data3, binary_calculeator4, octogon_data5, octogon_calculeator6, sedecim_data7,sedecim_calculeator8, pentecostal_system9 +from pages.tabs import start1,calculater2, binary_data3, binary_calculeator4, octogon_data5, octogon_calculeator6, sedecim_data7,sedecim_calculeator8, pentecostal_system9, pentecostal_calculeator10 class Main_page(ft.View): def __init__(self, page): @@ -25,6 +25,7 @@ class Main_page(ft.View): sedecim_data7.Button_sedecim_data(), sedecim_calculeator8.Button_sedecim_calculator(), pentecostal_system9.Button_binary_data(), + pentecostal_calculeator10.Button_quinquagesimal_calculator(), ft.Container( bgcolor="#280A3F", expand=True, diff --git a/app/src/pages/tabs/binary_calculeator4.py b/app/src/pages/tabs/binary_calculeator4.py index bc4b952..53d7a9e 100644 --- a/app/src/pages/tabs/binary_calculeator4.py +++ b/app/src/pages/tabs/binary_calculeator4.py @@ -1,5 +1,11 @@ from designer import right_column import flet as ft +import sys +import os + +# Добавляем путь к модулю step_by_step_solver +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from step_by_step_solver import StepByStepSolver, Operation class Button_binary_calculator(ft.CupertinoButton): def __init__(self, text: str = "Двоичный калькулятор"): @@ -16,6 +22,13 @@ class Button_binary_calculator(ft.CupertinoButton): self.input_right_1 = None self.input_right_2 = None + # Контейнеры для пошагового решения + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True) + + # Солвер для двоичной системы + self.solver = StepByStepSolver(base=2) + # Количество бит для представления (по умолчанию 8) self.bit_width = 16 @@ -54,6 +67,140 @@ class Button_binary_calculator(ft.CupertinoButton): # Только 0 и 1 return all(c in "01" for c in value) + def show_steps_left(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для левой колонки (вычитание)""" + try: + # Получаем шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очищаем предыдущие шаги + self.steps_left.controls.clear() + + # Добавляем заголовок + self.steps_left.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + # Добавляем каждый шаг + for i, step in enumerate(steps, 1): + step_content = [] + + # Название шага + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + + # Описание + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + # Визуализация (если есть) + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + # Результат (если есть) + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + # Добавляем контейнер с шагом + self.steps_left.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_left.update() + except Exception as ex: + self.steps_left.controls.clear() + self.steps_left.controls.append( + ft.Text(f"Ошибка при генерации решения: {str(ex)}", color="red", size=10) + ) + self.steps_left.update() + + def show_steps_right(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для правой колонки (сложение)""" + try: + # Получаем шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очищаем предыдущие шаги + self.steps_right.controls.clear() + + # Добавляем заголовок + self.steps_right.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + # Добавляем каждый шаг + for i, step in enumerate(steps, 1): + step_content = [] + + # Название шага + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + + # Описание + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + # Визуализация (если есть) + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + # Результат (если есть) + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + # Добавляем контейнер с шагом + self.steps_right.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_right.update() + except Exception as ex: + self.steps_right.controls.clear() + self.steps_right.controls.append( + ft.Text(f"Ошибка при генерации решения: {str(ex)}", color="red", size=10) + ) + self.steps_right.update() + def calculate_subtraction(self, e): try: bin1 = self.input_left_1.value or "0" @@ -89,6 +236,10 @@ class Button_binary_calculator(ft.CupertinoButton): self.decimal_left.value = f"({result}₁₀)" self.result_left.update() self.decimal_left.update() + + # Получаем пошаговое решение + self.show_steps_left(bin1, bin2, Operation.SUBTRACTION) + except ValueError as ex: self.result_left.value = "Ошибка" self.decimal_left.value = str(ex) @@ -130,6 +281,10 @@ class Button_binary_calculator(ft.CupertinoButton): self.decimal_right.value = f"({result}₁₀)" self.result_right.update() self.decimal_right.update() + + # Получаем пошаговое решение + self.show_steps_right(bin1, bin2, Operation.ADDITION) + except ValueError as ex: self.result_right.value = "Ошибка" self.decimal_right.value = str(ex) @@ -191,7 +346,7 @@ class Button_binary_calculator(ft.CupertinoButton): controls=[ ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_left_1, @@ -206,12 +361,19 @@ class Button_binary_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_subtraction) + ft.CupertinoButton("=", on_click=self.calculate_subtraction), + ft.Container( + expand=True, + content=self.steps_left, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ), ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_right_1, @@ -226,7 +388,14 @@ class Button_binary_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_addition) + ft.CupertinoButton("=", on_click=self.calculate_addition), + ft.Container( + expand=True, + content=self.steps_right, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ) ] diff --git a/app/src/pages/tabs/calculater2.py b/app/src/pages/tabs/calculater2.py index 9608f0c..6aa90c0 100644 --- a/app/src/pages/tabs/calculater2.py +++ b/app/src/pages/tabs/calculater2.py @@ -1,12 +1,16 @@ from designer import right_column import flet as ft +import sys +import os + +# Добавляем путь к модулю step_by_step_solver +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from step_by_step_solver import StepByStepSolver, Operation class Button_calculeator(ft.CupertinoButton): def __init__(self, text: str = "Десятичный калькулятор"): self.result_left = ft.Text(value="0", size=30, width=300) self.result_right = ft.Text(value="0", size=30, width=300) - #self.binary_left = ft.Text(value="(0₂)", size=14, color="#B0B0B0", width=300) - #self.binary_right = ft.Text(value="(0₂)", size=14, color="#B0B0B0", width=300) # Поля ввода для вычитания self.input_left_1 = None @@ -16,6 +20,13 @@ class Button_calculeator(ft.CupertinoButton): self.input_right_1 = None self.input_right_2 = None + # Контейнеры для пошагового решения + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5, expand=True) + + # Солвер для десятичной системы + self.solver = StepByStepSolver(base=10) + super().__init__( bgcolor="#00000000", disabled_bgcolor="#3A1E51", @@ -38,15 +49,14 @@ class Button_calculeator(ft.CupertinoButton): result = num1 - num2 self.result_left.value = str(result) - #self.binary_left.value = f"({self.to_binary(result)}₂)" - self.result_left.update() - #self.binary_left.update() + + # Получаем пошаговое решение + self.show_steps_left(str(num1), str(num2), Operation.SUBTRACTION) + except ValueError: self.result_left.value = "Ошибка" - #self.binary_left.value = "Неверный формат" self.result_left.update() - #self.binary_left.update() def calculate_addition(self, e): try: @@ -55,15 +65,124 @@ class Button_calculeator(ft.CupertinoButton): result = num1 + num2 self.result_right.value = str(result) - #self.binary_right.value = f"({self.to_binary(result)}₂)" - self.result_right.update() - #self.binary_right.update() + + # Получаем пошаговое решение + self.show_steps_right(str(num1), str(num2), Operation.ADDITION) + except ValueError: self.result_right.value = "Ошибка" - #self.binary_right.value = "Неверный формат" self.result_right.update() - #self.binary_right.update() + + def show_steps_left(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для левой колонки (вычитание)""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_left.controls.clear() + + self.steps_left.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + self.steps_left.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_left.update() + except Exception as ex: + self.steps_left.controls.clear() + self.steps_left.controls.append( + ft.Text(f"Ошибка: {str(ex)}", color="red", size=10) + ) + self.steps_left.update() + + def show_steps_right(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для правой колонки (сложение)""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_right.controls.clear() + + self.steps_right.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + self.steps_right.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_right.update() + except Exception as ex: + self.steps_right.controls.clear() + self.steps_right.controls.append( + ft.Text(f"Ошибка: {str(ex)}", color="red", size=10) + ) + self.steps_right.update() def test_data(self, e): # Создаем поля ввода с валидацией @@ -123,7 +242,7 @@ class Button_calculeator(ft.CupertinoButton): controls=[ ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_left_1, @@ -132,18 +251,24 @@ class Button_calculeator(ft.CupertinoButton): ft.Container( content=ft.Column([ self.result_left, - #self.binary_left ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_subtraction) + ft.CupertinoButton("=", on_click=self.calculate_subtraction), + ft.Container( + expand=True, + content=self.steps_left, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ), ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_right_1, @@ -152,13 +277,19 @@ class Button_calculeator(ft.CupertinoButton): ft.Container( content=ft.Column([ self.result_right, - #self.binary_right ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), bgcolor="#3A1E51", padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_addition) + ft.CupertinoButton("=", on_click=self.calculate_addition), + ft.Container( + expand=True, + content=self.steps_right, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ) ] diff --git a/app/src/pages/tabs/octogon_calculeator6.py b/app/src/pages/tabs/octogon_calculeator6.py index fbebbd2..f257641 100644 --- a/app/src/pages/tabs/octogon_calculeator6.py +++ b/app/src/pages/tabs/octogon_calculeator6.py @@ -1,5 +1,11 @@ from designer import right_column import flet as ft +import sys +import os + +# Добавляем путь к модулю step_by_step_solver +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from step_by_step_solver import StepByStepSolver, Operation class Button_octogon_calculator(ft.CupertinoButton): def __init__(self, text: str = "Восьмеричный калькулятор"): @@ -15,6 +21,13 @@ class Button_octogon_calculator(ft.CupertinoButton): # Поля ввода для сложения self.input_right_1 = None self.input_right_2 = None + + # Контейнеры для пошагового решения + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + + # Солвер для восьмеричной системы + self.solver = StepByStepSolver(base=8) super().__init__( bgcolor="#00000000", @@ -74,6 +87,12 @@ class Button_octogon_calculator(ft.CupertinoButton): self.decimal_left.value = f"({result}₁₀)" self.result_left.update() self.decimal_left.update() + + # Получаем пошаговое решение (убираем знак минус для солвера) + clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1 + clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2 + self.show_steps_left(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.SUBTRACTION) + except ValueError as ex: self.result_left.value = "Ошибка" self.decimal_left.value = str(ex) @@ -115,11 +134,85 @@ class Button_octogon_calculator(ft.CupertinoButton): self.decimal_right.value = f"({result}₁₀)" self.result_right.update() self.decimal_right.update() + + # Получаем пошаговое решение + clean_val1 = val1.lstrip('-') if val1.startswith('-') and num1 < 0 else val1 + clean_val2 = val2.lstrip('-') if val2.startswith('-') and num2 < 0 else val2 + self.show_steps_right(clean_val1 if num1 >= 0 else val1, clean_val2 if num2 >= 0 else val2, Operation.ADDITION) + except ValueError as ex: self.result_right.value = "Ошибка" self.decimal_right.value = str(ex) self.result_right.update() self.decimal_right.update() + + def show_steps_left(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для левой колонки""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_left.controls.clear() + + self.steps_left.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")) + step_content.append(ft.Text(step.description, size=10, color="#E0E0E0")) + if step.visual: + step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True)) + if step.result: + step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00")) + + self.steps_left.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + self.steps_left.update() + except Exception as ex: + self.steps_left.controls.clear() + self.steps_left.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)) + self.steps_left.update() + + def show_steps_right(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для правой колонки""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_right.controls.clear() + + self.steps_right.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")) + step_content.append(ft.Text(step.description, size=10, color="#E0E0E0")) + if step.visual: + step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True)) + if step.result: + step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00")) + + self.steps_right.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + self.steps_right.update() + except Exception as ex: + self.steps_right.controls.clear() + self.steps_right.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)) + self.steps_right.update() def test_data(self, e): # Создаем поля ввода с валидацией для двоичных чисел @@ -177,7 +270,7 @@ class Button_octogon_calculator(ft.CupertinoButton): controls=[ ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_left_1, @@ -192,12 +285,19 @@ class Button_octogon_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_subtraction) + ft.CupertinoButton("=", on_click=self.calculate_subtraction), + ft.Container( + expand=True, + content=self.steps_left, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ), ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_right_1, @@ -212,7 +312,14 @@ class Button_octogon_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_addition) + ft.CupertinoButton("=", on_click=self.calculate_addition), + ft.Container( + expand=True, + content=self.steps_right, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ) ] diff --git a/app/src/pages/tabs/pentecostal_calculeator10.py b/app/src/pages/tabs/pentecostal_calculeator10.py new file mode 100644 index 0000000..b0c450e --- /dev/null +++ b/app/src/pages/tabs/pentecostal_calculeator10.py @@ -0,0 +1,421 @@ +from designer import right_column +import flet as ft +import sys +import os + +# Добавляем путь к модулю step_by_step_solver +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from step_by_step_solver import StepByStepSolver, Operation + +class Button_quinquagesimal_calculator(ft.CupertinoButton): + def __init__(self, text: str = "50-ричный калькулятор"): + self.result_left = ft.Text(value="0", size=30, width=300) + self.result_right = ft.Text(value="0", size=30, width=300) + self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) + self.decimal_right = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) + + # Поля ввода для вычитания + self.input_left_1 = None + self.input_left_2 = None + + # Поля ввода для сложения + self.input_right_1 = None + self.input_right_2 = None + + # Контейнеры для пошагового решения + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + + # Допустимые символы: 0-9, A-Z, a-n (всего: 10 + 26 + 14 = 50) + self.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn" + + # Солвер для 50-ричной системы с нашим алфавитом + self.solver = StepByStepSolver(base=50, digits=self.digits) + + super().__init__( + bgcolor="#00000000", + disabled_bgcolor="#3A1E51", + text=text, + color="white", + on_click=self.test_data + ) + + def validate_base50(self, value: str) -> bool: + """Проверяет, является ли строка корректным 50-ричным числом. + + Допустимые символы: 0-9, A-Z, a-n (0-49), опциональный знак минус + """ + if value is None or value == "": + return True + v = value.strip() + if v == "": + return True + if v[0] == '-': + v = v[1:] + # В 50-ричной системе используются цифры 0-9, A-Z, a-n (всего 50 символов) + valid_chars = self.digits # 0-9, A-Z, a-n + return all(c in valid_chars for c in v) + + def to_base50(self, decimal_num: int) -> str: + """Преобразование десятичного числа в 50-ричное""" + if decimal_num == 0: + return "0" + + is_negative = decimal_num < 0 + decimal_num = abs(decimal_num) + + result = "" + while decimal_num > 0: + remainder = decimal_num % 50 + result = self.digits[remainder] + result + decimal_num //= 50 + + return ("-" + result) if is_negative else result + + def from_base50(self, base50_str: str) -> int: + """Преобразование 50-ричного числа в десятичное""" + if not base50_str: + return 0 + + base50_str = base50_str.strip() + if base50_str == "" or base50_str == "0": + return 0 + + is_negative = base50_str.startswith('-') + if is_negative: + base50_str = base50_str[1:] + + result = 0 + for char in base50_str: + if char in self.digits: + digit_value = self.digits.index(char) + result = result * 50 + digit_value + else: + raise ValueError(f"Недопустимый символ '{char}' для 50-ричной системы") + + return -result if is_negative else result + + def show_steps_left(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для левой колонки (вычитание)""" + try: + # Получаем шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очищаем предыдущие шаги + self.steps_left.controls.clear() + + # Добавляем заголовок + self.steps_left.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + # Добавляем каждый шаг + for i, step in enumerate(steps, 1): + step_content = [] + + # Название шага + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + + # Описание + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + # Визуализация (если есть) + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + # Результат (если есть) + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + # Добавляем контейнер с шагом + self.steps_left.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_left.update() + except Exception as ex: + self.steps_left.controls.clear() + self.steps_left.controls.append( + ft.Text(f"Ошибка: {str(ex)}", color="red", size=10) + ) + self.steps_left.update() + + def show_steps_right(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для правой колонки (сложение)""" + try: + # Получаем шаги решения + steps = self.solver.solve(num1, num2, operation) + + # Очищаем предыдущие шаги + self.steps_right.controls.clear() + + # Добавляем заголовок + self.steps_right.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + # Добавляем каждый шаг + for i, step in enumerate(steps, 1): + step_content = [] + + # Название шага + step_content.append( + ft.Text(f"Шаг {i}: {step.title}", + size=12, weight=ft.FontWeight.BOLD, color="#00FFFF") + ) + + # Описание + step_content.append( + ft.Text(step.description, size=10, color="#E0E0E0") + ) + + # Визуализация (если есть) + if step.visual: + step_content.append( + ft.Text(step.visual, + size=10, + font_family="Courier New", + color="#90EE90", + selectable=True) + ) + + # Результат (если есть) + if step.result: + step_content.append( + ft.Text(f"➤ {step.result}", + size=11, + weight=ft.FontWeight.BOLD, + color="#00FF00") + ) + + # Добавляем контейнер с шагом + self.steps_right.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + + self.steps_right.update() + except Exception as ex: + self.steps_right.controls.clear() + self.steps_right.controls.append( + ft.Text(f"Ошибка: {str(ex)}", color="red", size=10) + ) + self.steps_right.update() + + def calculate_subtraction(self, e): + try: + val1 = (self.input_left_1.value or "").strip() + val2 = (self.input_left_2.value or "").strip() + + # Проверка корректности 50-ричных чисел + if not self.validate_base50(val1) or not self.validate_base50(val2): + self.result_left.value = "Ошибка: неверный формат" + self.decimal_left.value = "Используйте 0-9, A-Z, a-n" + e.page.update() + return + + # Преобразуем в десятичные числа + num1 = self.from_base50(val1 if val1 else "0") + num2 = self.from_base50(val2 if val2 else "0") + + # Вычисляем результат + result = num1 - num2 + + # Преобразуем обратно в 50-ричный формат + base50_result = self.to_base50(result) + + self.result_left.value = base50_result + self.decimal_left.value = f"({result}₁₀)" + e.page.update() + + # Получаем пошаговое решение (убираем знак минус для солвера) + clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 + clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 + self.show_steps_left(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.SUBTRACTION) + + except ValueError as ex: + self.result_left.value = "Ошибка" + self.decimal_left.value = str(ex) + e.page.update() + + def calculate_addition(self, e): + try: + val1 = (self.input_right_1.value or "").strip() + val2 = (self.input_right_2.value or "").strip() + + # Проверка корректности 50-ричных чисел + if not self.validate_base50(val1) or not self.validate_base50(val2): + self.result_right.value = "Ошибка: неверный формат" + self.decimal_right.value = "Используйте 0-9, A-Z, a-n" + e.page.update() + return + + # Преобразуем в десятичные числа + num1 = self.from_base50(val1 if val1 else "0") + num2 = self.from_base50(val2 if val2 else "0") + + # Вычисляем результат + result = num1 + num2 + + # Преобразуем обратно в 50-ричный формат + base50_result = self.to_base50(result) + + self.result_right.value = base50_result + self.decimal_right.value = f"({result}₁₀)" + e.page.update() + + # Получаем пошаговое решение + clean_val1 = val1.lstrip('-') if val1.startswith('-') else val1 + clean_val2 = val2.lstrip('-') if val2.startswith('-') else val2 + self.show_steps_right(clean_val1 if clean_val1 else "0", clean_val2 if clean_val2 else "0", Operation.ADDITION) + + except ValueError as ex: + self.result_right.value = "Ошибка" + self.decimal_right.value = str(ex) + e.page.update() + + def test_data(self, e): + # Создаем поля ввода для 50-ричных чисел + # Явно перечисляем ВСЕ 50 допустимых символов в regex + # 0-9 (10), A-Z (26), a-n (14) = 50 символов + allowed_chars = r"^-?[0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn]*$" + + self.input_left_1 = ft.TextField( + label="50-ричное число 1", + width=250, + text_align=ft.TextAlign.RIGHT, + value="0", + input_filter=ft.InputFilter(regex_string=allowed_chars), + hint_text="Например: 1A (60₁₀) или Z (35₁₀) или a (36₁₀)" + ) + self.input_left_2 = ft.TextField( + label="50-ричное число 2", + width=250, + text_align=ft.TextAlign.RIGHT, + value="0", + input_filter=ft.InputFilter(regex_string=allowed_chars), + hint_text="Например: B (11₁₀) или n (49₁₀)" + ) + + self.input_right_1 = ft.TextField( + label="50-ричное число 1", + width=250, + text_align=ft.TextAlign.RIGHT, + value="0", + input_filter=ft.InputFilter(regex_string=allowed_chars), + hint_text="Например: 1A (60₁₀) или c (38₁₀)" + ) + self.input_right_2 = ft.TextField( + label="50-ричное число 2", + width=250, + text_align=ft.TextAlign.RIGHT, + value="0", + input_filter=ft.InputFilter(regex_string=allowed_chars), + hint_text="Например: C (12₁₀) или k (46₁₀)" + ) + + right_column.controls = [ft.Container( + bgcolor="#55257D", + padding=ft.padding.all(10), + border_radius=10, + expand=True, + content=ft.Column( + scroll=ft.ScrollMode.AUTO, + expand=True, + controls=[ + ft.Text("50-ричный калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD), + ft.Text("Система счисления с основанием 50", size=11, color="#B0B0B0", italic=True), + ft.Text("Используйте: 0-9 (0-9), A-Z (10-35), a-n (36-49)", size=11, color="#B0B0B0"), + ft.Text("Примеры: 10₍₅₀₎=50₁₀, A₍₅₀₎=10₁₀, Z₍₅₀₎=35₁₀, n₍₅₀₎=49₁₀", size=10, color="#90EE90"), + ft.Row( + expand=True, + alignment=ft.MainAxisAlignment.CENTER, + vertical_alignment=ft.CrossAxisAlignment.START, + controls=[ + ft.Column( + expand=True, + alignment=ft.MainAxisAlignment.START, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + self.input_left_1, + ft.Text("——— Вычитание", size=12, color="white"), + self.input_left_2, + ft.Container( + content=ft.Column([ + self.result_left, + self.decimal_left + ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), + bgcolor="#3A1E51", + padding=10, + border_radius=5, + ), + ft.CupertinoButton("=", on_click=self.calculate_subtraction), + ft.Container( + expand=True, + content=self.steps_left, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) + ] + ), + ft.Column( + expand=True, + alignment=ft.MainAxisAlignment.START, + horizontal_alignment=ft.CrossAxisAlignment.CENTER, + controls=[ + self.input_right_1, + ft.Text("—|— Сложение", size=12, color="white"), + self.input_right_2, + ft.Container( + content=ft.Column([ + self.result_right, + self.decimal_right + ], horizontal_alignment=ft.CrossAxisAlignment.CENTER), + bgcolor="#3A1E51", + padding=10, + border_radius=5, + ), + ft.CupertinoButton("=", on_click=self.calculate_addition), + ft.Container( + expand=True, + content=self.steps_right, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) + ] + ) + ] + ) + ], + ) + )] + e.page.update() \ No newline at end of file diff --git a/app/src/pages/tabs/pentecostal_system9.py b/app/src/pages/tabs/pentecostal_system9.py index 70a8b48..50296fc 100644 --- a/app/src/pages/tabs/pentecostal_system9.py +++ b/app/src/pages/tabs/pentecostal_system9.py @@ -2,7 +2,7 @@ from designer import right_column import flet as ft class Button_binary_data(ft.CupertinoButton): - def __init__(self, text: str = "Двоичная система счисления"): + def __init__(self, text: str = "50-ричная система счисления"): super().__init__( bgcolor="#00000000", disabled_bgcolor="#3A1E51", @@ -11,136 +11,134 @@ class Button_binary_data(ft.CupertinoButton): on_click=self.test_data ) - def test_data(e, self): + def test_data(self, e): md1=""" - **N-мерные системы счисления** +# 50-ричная система счисления -**Расширение позиционных систем в многомерное пространство** +## Основные понятия -Основная концепция: -Вместо линейной цепочки разрядов используется n-мерная решётка, где каждая ячейка имеет свои координаты и вес. +**50-ричная система счисления** — позиционная система счисления с основанием 50. -**Математическое представление:** +### Алфавит системы: +- **Цифры 0-9**: значения 0-9 (10 символов) +- **Заглавные A-Z**: значения 10-35 (26 символов) +- **Строчные a-n**: значения 36-49 (14 символов) +- **Итого**: 50 уникальных символов -Для 2D системы счисления: -N = ΣΣ a_ij × b₁ⁱ × b₂ʲ +### Примеры перевода: +- `10₅₀ = 50₁₀` (одна пятидесятка) +- `A₅₀ = 10₁₀` (символ A = десять) +- `Z₅₀ = 35₁₀` (последняя заглавная буква) +- `n₅₀ = 49₁₀` (последний символ алфавита) -Для 3D системы счисления: -N = ΣΣΣ a_ijk × b₁ⁱ × b₂ʲ × b₃ᵏ +### Математическое представление: -Общая формула для n-мерной системы: -N = Σ...Σ a_i₁...iₙ × b₁ⁱ¹ × b₂ⁱ² × ... × bₙⁱⁿ +Общая формула перевода из 50-ричной в десятичную: + +**N₁₀ = aₙ×50ⁿ + aₙ₋₁×50ⁿ⁻¹ + ... + a₁×50¹ + a₀×50⁰** где: -- a_i₁...iₙ - цифра в позиции (i₁,...,iₙ) -- b₁,...,bₙ - основания по каждому измерению -- i₁,...,iₙ - координаты в n-мерном пространстве +- N₁₀ — число в десятичной системе +- aᵢ — цифра на позиции i (значение 0-49) +- n — количество разрядов минус 1 -**Пример 2D системы:** +### Пример вычисления: -Основания: b₁ = 10 (по горизонтали), b₂ = 8 (по вертикали) +Число `Ab5₅₀`: +- A = 10, b = 37, 5 = 5 +- Ab5₅₀ = 10×50² + 37×50¹ + 5×50⁰ +- Ab5₅₀ = 10×2500 + 37×50 + 5×1 +- Ab5₅₀ = 25000 + 1850 + 5 +- **Ab5₅₀ = 26855₁₀** -Числовая матрица: -[3 5] -[2 7] +### Применение: -Расчёт: -N = 3×10¹×8¹ + 5×10¹×8⁰ + 2×10⁰×8¹ + 7×10⁰×8⁰ -N = 3×80 + 5×10 + 2×8 + 7×1 -N = 240 + 50 + 16 + 7 = 313 - -**Преимущества n-мерных систем:** - -• Компактное представление многомерных данных -• Естественное отображение для изображений, матриц, тензоров -• Эффективные алгоритмы для многомерных преобразований Фурье -• Прямое соответствие структурам данных в машинном обучении -• Упрощённая работа с пространственными данными - -**Применения:** - -• Обработка изображений (2D системы) -• Научные вычисления с тензорами (3D+ системы) -• Сжатие многомерных данных -• Нейронные сети и глубокое обучение -• Квантовые вычисления (многомерные представления) - -**Особенности:** - -Каждое измерение может иметь своё основание, что позволяет оптимально кодировать различные типы данных в многомерных структурах. +• Компактное представление больших чисел +• Кодирование данных с расширенным алфавитом +• Учебные задачи по системам счисления +• Альтернативные схемы нумерации """ md2=""" - **Сборка 60-мерной системы счисления** +## Арифметические операции в 50-ричной системе -**1. Определение структуры:** -- 60 независимых измерений (осей) -- Каждое измерение имеет своё основание b₁, b₂, ..., b₆₀ -- Пример оснований: [2, 3, 5, 7, 11, ..., 293] (простые числа) +### Сложение -**2. Математическая модель:** -N = ΣΣ...Σ a_i₁i₂...i₆₀ × b₁ⁱ¹ × b₂ⁱ² × ... × b₆₀ⁱ⁶⁰ +**Правило**: Складываем разряды справа налево. Если сумма ≥ 50, переносим в старший разряд. -где: -- a_i₁i₂...i₆₀ - цифра в 60-мерной позиции -- i₁,...,i₆₀ - координаты от 0 до k (зависит от основания) -- b₁,...,b₆₀ - основания каждого измерения +**Пример**: `A₅₀ + Z₅₀` +- A₅₀ = 10₁₀ +- Z₅₀ = 35₁₀ +- Сумма: 10 + 35 = 45₁₀ = j₅₀ -**3. Практическая реализация:** +**Пример с переносом**: `n₅₀ + 5₅₀` +- n₅₀ = 49₁₀ +- 5₅₀ = 5₁₀ +- Сумма: 49 + 5 = 54₁₀ = 1×50 + 4 = **14₅₀** -**Шаг 1: Выбор оснований** -b₁ = 2 (измерение 1) -b₂ = 3 (измерение 2) -b₃ = 5 (измерение 3) -... -b₆₀ = 293 (измерение 60) +### Вычитание -**Шаг 2: Представление числа** -Каждое число - это 60-мерный массив: -[координата_1][координата_2]...[координата_60] +**Правило**: Вычитаем разряды справа налево. Если не хватает, занимаем из старшего разряда (50 единиц). -**Шаг 3: Пример вычисления** -Для упрощения возьмём маленькие координаты: +**Пример**: `Z₅₀ - A₅₀` +- Z₅₀ = 35₁₀ +- A₅₀ = 10₁₀ +- Разность: 35 - 10 = 25₁₀ = P₅₀ -a₁ = 1, a₂ = 0, a₃ = 2, ..., a₆₀ = 1 +### Карта символов (кратко) +0–9 → 0–9, A–Z → 10–35, a–n → 36–49 -N = 1×2¹ + 0×3¹ + 2×5¹ + ... + 1×293¹ +### Алгоритм перевода обратно в десятичную: -**4. Алгоритм перевода в десятичную:** - -``` -десятичное_число = 0 -для каждого измерения i от 1 до 60: - вес = произведение оснований всех предыдущих измерений - десятичное_число += цифра[i] × вес +```python +def from_base50(число_50): + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn" + результат = 0 + + for i, символ in enumerate(reversed(число_50)): + значение = digits.index(символ) + результат += значение × (50 ** i) + + return результат ``` -**5. Особенности 60-мерной системы:** +### Преимущества 50-ричной системы: -- **Гиперпространство данных**: Каждая точка в 60-мерном пространстве - уникальное число -- **Гибкость**: Можно использовать разные основания для разных типов данных -- **Сжатие**: Эффективное кодирование сложных структур -- **Вычисления**: Сложение и умножение происходят независимо по каждому измерению +✓ Больше символов → компактнее запись +✓ Легко расширяется до других оснований +✓ Учит работать с произвольными системами счисления +✓ Развивает абстрактное математическое мышление + """ + + md3=""" +## Практические примеры -**6. Области применения:** +### Пример 1: Сложение `1A₅₀ + B₅₀` -- Квантовые вычисления (60 кубитов) -- Глубокое обучение (нейросети с 60 слоями) -- Криптография (сверхбольшие пространства ключей) -- Научное моделирование (многопараметрические системы) +**Шаг 1**: Переводим в десятичную +- 1A₅₀ = 1×50 + 10 = 60₁₀ +- B₅₀ = 11₁₀ -**7. Ограничения:** +**Шаг 2**: Складываем +- 60 + 11 = 71₁₀ -- Визуализация невозможна (человек не может представить 60 измерений) -- Вычисления требуют суперкомпьютеров -- Сложность отладки и верификации +**Шаг 3**: Переводим обратно +- 71 ÷ 50 = 1 (остаток 21) +- 21 → L (в алфавите) +- **Ответ: 1L₅₀** -**Простой пример для 3 измерений вместо 60:** -Основания: [2, 3, 5] -Число: [1, 0, 2] -N = 1×(3×5) + 0×(5) + 2×1 = 15 + 0 + 2 = 17 +### Пример 2: Вычитание `Z₅₀ - 5₅₀` -В 60-мерном случае принцип тот же, но измерений в 20 раз больше. +**Решение**: +- Z₅₀ = 35₁₀ +- 5₅₀ = 5₁₀ +- 35 - 5 = 30₁₀ +- 30 → U (в алфавите) +- **Ответ: U₅₀** + +--- + +**Заключение**: 50-ричная система — мощный инструмент для понимания принципов позиционных систем счисления с любым основанием. """ right_column.controls=[ ft.Container( @@ -169,13 +167,13 @@ N = 1×(3×5) + 0×(5) + 2×1 = 15 + 0 + 2 = 17 ), ft.Text("Перевод обратно в десятичную систему:", size=16, color="white"), - ft.Container( - bgcolor="#000000", - content=ft.Image(src="./Система счисления 2.png", expand=True), - border_radius=10, - width=580, - height=300, - ), + ##ft.Container( + ## bgcolor="#000000", + ## content=ft.Image(src="./Система счисления 2.png", expand=True), + ## border_radius=10, + ## width=580, + ## height=300, + ##), ft.Text("Работа со знаком:", size=16, color="white"), ], diff --git a/app/src/pages/tabs/sedecim_calculeator8.py b/app/src/pages/tabs/sedecim_calculeator8.py index 36fac45..c5c97c5 100644 --- a/app/src/pages/tabs/sedecim_calculeator8.py +++ b/app/src/pages/tabs/sedecim_calculeator8.py @@ -1,8 +1,14 @@ from designer import right_column import flet as ft +import sys +import os + +# Добавляем путь к модулю step_by_step_solver +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +from step_by_step_solver import StepByStepSolver, Operation class Button_sedecim_calculator(ft.CupertinoButton): - def __init__(self, text: str = "Двоичный калькулятор"): + def __init__(self, text: str = "Шеснадцатиричный калькулятор"): self.result_left = ft.Text(value="0", size=30, width=300) self.result_right = ft.Text(value="0", size=30, width=300) self.decimal_left = ft.Text(value="(0₁₀)", size=14, color="#B0B0B0", width=300) @@ -16,6 +22,13 @@ class Button_sedecim_calculator(ft.CupertinoButton): self.input_right_1 = None self.input_right_2 = None + # Контейнеры для пошагового решения + self.steps_left = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + self.steps_right = ft.Column(scroll=ft.ScrollMode.AUTO, spacing=5) + + # Солвер для шестнадцатеричной системы + self.solver = StepByStepSolver(base=16) + # Количество бит для представления (по умолчанию 8) self.bit_width = 16 @@ -27,149 +40,240 @@ class Button_sedecim_calculator(ft.CupertinoButton): on_click=self.test_data ) - def to_twos_complement(self, num, bit_width): - """Преобразует число в дополнительный код""" - if num >= 0: - return format(num, f'0{bit_width}b') + def hex_to_decimal(self,hex_str): + """ + Преобразование шестнадцатеричного числа в десятичное + Поддерживает отрицательные числа и дроби + """ + if not hex_str: + return 0 + + # Обработка знака + sign = -1 if hex_str[0] == '-' else 1 + if hex_str[0] in ['-', '+']: + hex_str = hex_str[1:] + + # Разделение на целую и дробную части + if '.' in hex_str: + integer_part, fractional_part = hex_str.split('.') else: - # Для отрицательных чисел используем дополнительный код - return format((1 << bit_width) + num, f'0{bit_width}b') - - def from_twos_complement(self, binary_str): - """Преобразует из дополнительного кода в десятичное число""" - bit_width = len(binary_str) - value = int(binary_str, 2) - - # Проверяем знаковый бит (старший бит) - if binary_str[0] == '1': - # Отрицательное число - value = value - (1 << bit_width) - - return value - - def validate_binary(self, value): - """Проверяет, является ли строка корректным двоичным числом""" - if not value: - return True - # Только 0 и 1 - return all(c in "01" for c in value) - - def calculate_subtraction(self, e): - try: - bin1 = self.input_left_1.value or "0" - bin2 = self.input_left_2.value or "0" - - # Проверка корректности двоичных чисел - if not self.validate_binary(bin1) or not self.validate_binary(bin2): - self.result_left.value = "Ошибка: неверный формат" - self.decimal_left.value = "" - self.result_left.update() - self.decimal_left.update() - return - - # Преобразуем двоичные числа в десятичные - - if len(bin1) > 0 and bin1[0] == '1' and len(bin1) >= 8: - num1 = self.from_twos_complement(bin1) - else: - num1 = int(bin1, 2) if bin1 else 0 - - if len(bin2) > 0 and bin2[0] == '1' and len(bin2) >= 8: - num2 = self.from_twos_complement(bin2) - else: - num2 = int(bin2, 2) if bin2 else 0 - - # Вычисляем результат - result = num1 - num2 - - # Преобразуем обратно в двоичный формат (дополнительный код) - binary_result = self.to_twos_complement(result, self.bit_width) - - self.result_left.value = binary_result - self.decimal_left.value = f"({result}₁₀)" - self.result_left.update() - self.decimal_left.update() - except ValueError as ex: - self.result_left.value = "Ошибка" - self.decimal_left.value = str(ex) - self.result_left.update() - self.decimal_left.update() - + integer_part, fractional_part = hex_str, "" + + # Преобразование целой части + decimal_value = int(integer_part, 16) if integer_part else 0 + + # Преобразование дробной части + if fractional_part: + for i, digit in enumerate(fractional_part, 1): + decimal_value += int(digit, 16) / (16 ** i) + + return decimal_value * sign + + def decimal_to_hex(self,decimal_num, precision=6): + """ + Перевод десятичного числа в шестнадцатеричное + """ + if decimal_num == 0: + return "0" + + # Обработка знака + sign = "-" if decimal_num < 0 else "" + decimal_num = abs(decimal_num) + + # Разделение на целую и дробную части + integer_part = int(decimal_num) + fractional_part = decimal_num - integer_part + + # Перевод целой части + hex_integer = hex(integer_part)[2:].upper() + + # Перевод дробной части + hex_fractional = "" + if fractional_part > 0: + hex_fractional = "." + temp = fractional_part + for _ in range(precision): + if temp == 0: + break + temp *= 16 + digit = int(temp) + hex_fractional += hex(digit)[2:].upper() + temp -= digit + + return sign + hex_integer + hex_fractional + + # В классе калькулятора обновите функции расчета: def calculate_addition(self, e): try: - bin1 = self.input_right_1.value or "0" - bin2 = self.input_right_2.value or "0" + # Получаем и преобразуем числа + num1 = self.hex_to_decimal(self.input_right_1.value.upper()) + num2 = self.hex_to_decimal(self.input_right_2.value.upper()) + + # Вычисляем сумму + result_decimal = num1 + num2 + + # Преобразуем обратно в hex + result_hex = self.decimal_to_hex(result_decimal) + + # Обновляем результаты + self.result_right.value = f"HEX: {result_hex}" + self.decimal_right.value = f"DEC: {result_decimal}" - # Проверка корректности двоичных чисел - if not self.validate_binary(bin1) or not self.validate_binary(bin2): - self.result_right.value = "Ошибка: неверный формат" - self.decimal_right.value = "" - self.result_right.update() - self.decimal_right.update() - return - - # Преобразуем двоичные числа в десятичные - # Если введено в дополнительном коде - интерпретируем как дополнительный код - if len(bin1) > 0 and bin1[0] == '1' and len(bin1) >= 8: - num1 = self.from_twos_complement(bin1) + # Получаем пошаговое решение (только для целых чисел) + if '.' not in self.input_right_1.value and '.' not in self.input_right_2.value: + hex_val1 = self.input_right_1.value.upper().lstrip('-+') if self.input_right_1.value else "0" + hex_val2 = self.input_right_2.value.upper().lstrip('-+') if self.input_right_2.value else "0" + self.show_steps_right(hex_val1, hex_val2, Operation.ADDITION) else: - num1 = int(bin1, 2) if bin1 else 0 + self.steps_right.controls.clear() + self.steps_right.controls.append( + ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12) + ) + self.steps_right.update() + + except Exception as ex: + self.result_right.value = f"Ошибка: {str(ex)}" + self.decimal_right.value = "DEC: ошибка" + + self.page.update() + + def calculate_subtraction(self, e): + try: + # Получаем и преобразуем числа + num1 = self.hex_to_decimal(self.input_left_1.value.upper()) + num2 = self.hex_to_decimal(self.input_left_2.value.upper()) + + # Вычисляем разность + result_decimal = num1 - num2 + + # Преобразуем обратно в hex + result_hex = self.decimal_to_hex(result_decimal) + + # Обновляем результаты + self.result_left.value = f"HEX: {result_hex}" + self.decimal_left.value = f"DEC: {result_decimal}" + + # Получаем пошаговое решение (только для целых чисел) + if '.' not in self.input_left_1.value and '.' not in self.input_left_2.value: + hex_val1 = self.input_left_1.value.upper().lstrip('-+') if self.input_left_1.value else "0" + hex_val2 = self.input_left_2.value.upper().lstrip('-+') if self.input_left_2.value else "0" + self.show_steps_left(hex_val1, hex_val2, Operation.SUBTRACTION) + else: + self.steps_left.controls.clear() + self.steps_left.controls.append( + ft.Text("Пошаговое решение доступно только для целых чисел", color="#FFD700", size=12) + ) + self.steps_left.update() + + except Exception as ex: + self.result_left.value = f"Ошибка: {str(ex)}" + self.decimal_left.value = "DEC: ошибка" + + self.page.update() + + def show_steps_left(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для левой колонки""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_left.controls.clear() + + self.steps_left.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) + + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")) + step_content.append(ft.Text(step.description, size=10, color="#E0E0E0")) + if step.visual: + step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True)) + if step.result: + step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00")) - if len(bin2) > 0 and bin2[0] == '1' and len(bin2) >= 8: - num2 = self.from_twos_complement(bin2) - else: - num2 = int(bin2, 2) if bin2 else 0 + self.steps_left.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + self.steps_left.update() + except Exception as ex: + self.steps_left.controls.clear() + self.steps_left.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)) + self.steps_left.update() + + def show_steps_right(self, num1: str, num2: str, operation: Operation): + """Отображает пошаговое решение для правой колонки""" + try: + steps = self.solver.solve(num1, num2, operation) + self.steps_right.controls.clear() - # Вычисляем результат - result = num1 + num2 + self.steps_right.controls.append( + ft.Text("📝 Ход решения:", size=14, weight=ft.FontWeight.BOLD, color="#FFD700") + ) - # Преобразуем обратно в двоичный формат (дополнительный код) - binary_result = self.to_twos_complement(result, self.bit_width) - - self.result_right.value = binary_result - self.decimal_right.value = f"({result}₁₀)" - self.result_right.update() - self.decimal_right.update() - except ValueError as ex: - self.result_right.value = "Ошибка" - self.decimal_right.value = str(ex) - self.result_right.update() - self.decimal_right.update() + for i, step in enumerate(steps, 1): + step_content = [] + step_content.append(ft.Text(f"Шаг {i}: {step.title}", size=12, weight=ft.FontWeight.BOLD, color="#00FFFF")) + step_content.append(ft.Text(step.description, size=10, color="#E0E0E0")) + if step.visual: + step_content.append(ft.Text(step.visual, size=10, font_family="Courier New", color="#90EE90", selectable=True)) + if step.result: + step_content.append(ft.Text(f"➤ {step.result}", size=11, weight=ft.FontWeight.BOLD, color="#00FF00")) + + self.steps_right.controls.append( + ft.Container( + content=ft.Column(step_content, spacing=3), + bgcolor="#2A1A3A", + padding=8, + border_radius=5, + border=ft.border.all(1, "#4A3A5A") + ) + ) + self.steps_right.update() + except Exception as ex: + self.steps_right.controls.clear() + self.steps_right.controls.append(ft.Text(f"Ошибка: {str(ex)}", color="red", size=10)) + self.steps_right.update() def test_data(self, e): - # Создаем поля ввода с валидацией для двоичных чисел + # Создаем поля ввода с валидацией для шестнадцатеричных чисел self.input_left_1 = ft.TextField( - label="Двоичное число 1", + label="Шестнадцатеричное число 1", width=250, text_align=ft.TextAlign.RIGHT, value="0", - input_filter=ft.InputFilter(regex_string=r"^[01]*$"), # только 0 и 1 - hint_text="Например: 00001010 (10) или 11110110 (-10)" + input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"), # только 0-9, A-F, точка и минус + hint_text="Например: A (10) или -A (-10) или 1A.C (26.75)" ) self.input_left_2 = ft.TextField( - label="Двоичное число 2", + label="Шестнадцатеричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", - input_filter=ft.InputFilter(regex_string=r"^[01]*$"), - hint_text="Например: 00000101 (5)" + input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"), + hint_text="Например: 5 (5) или F.8 (15.5)" + ) + + self.input_right_1 = ft.TextField( + label="Шестнадцатеричное число 1", + width=250, + text_align=ft.TextAlign.RIGHT, + value="0", + input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"), + hint_text="Например: A (10) или FF (255)" ) - self.input_right_1 = ft.TextField( - label="Двоичное число 1", - width=250, - text_align=ft.TextAlign.RIGHT, - value="0", - input_filter=ft.InputFilter(regex_string=r"^[01]*$"), - hint_text="Например: 00001010 (10)" - ) self.input_right_2 = ft.TextField( - label="Двоичное число 2", + label="Шестнадцатеричное число 2", width=250, text_align=ft.TextAlign.RIGHT, value="0", - input_filter=ft.InputFilter(regex_string=r"^[01]*$"), - hint_text="Например: 00000101 (5)" + input_filter=ft.InputFilter(regex_string=r"^[0-9A-Fa-f.-]*$"), + hint_text="Например: 5 (5) или 1F (31)" ) right_column.controls = [ft.Container( @@ -181,9 +285,9 @@ class Button_sedecim_calculator(ft.CupertinoButton): scroll=ft.ScrollMode.AUTO, expand=True, controls=[ - ft.Text("Двоичный калькулятор (дополнительный код)", size=20, color="white", weight=ft.FontWeight.BOLD), - ft.Text("Отрицательные числа в дополнительном коде (старший бит = 1)", size=11, color="#B0B0B0", italic=True), - ft.Text(f"Разрядность: {self.bit_width} бит. Диапазон: {-(1 << (self.bit_width-1))} .. {(1 << (self.bit_width-1))-1}", size=11, color="#B0B0B0"), + ft.Text("Шестнадцатеричный калькулятор", size=20, color="white", weight=ft.FontWeight.BOLD), + ft.Text("Поддерживает отрицательные числа и дроби", size=11, color="#B0B0B0", italic=True), + ft.Text("Используйте A-F для цифр 10-15, точку для дробей, минус для отрицательных", size=11, color="#B0B0B0"), ft.Row( expand=True, alignment=ft.MainAxisAlignment.CENTER, @@ -191,7 +295,7 @@ class Button_sedecim_calculator(ft.CupertinoButton): controls=[ ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_left_1, @@ -206,12 +310,19 @@ class Button_sedecim_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_subtraction) + ft.CupertinoButton("=", on_click=self.calculate_subtraction), + ft.Container( + expand=True, + content=self.steps_left, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ), ft.Column( expand=True, - alignment=ft.MainAxisAlignment.CENTER, + alignment=ft.MainAxisAlignment.START, horizontal_alignment=ft.CrossAxisAlignment.CENTER, controls=[ self.input_right_1, @@ -226,7 +337,14 @@ class Button_sedecim_calculator(ft.CupertinoButton): padding=10, border_radius=5, ), - ft.CupertinoButton("=", on_click=self.calculate_addition) + ft.CupertinoButton("=", on_click=self.calculate_addition), + ft.Container( + expand=True, + content=self.steps_right, + bgcolor="#1A0A2A", + padding=10, + border_radius=5, + ) ] ) ] diff --git a/app/src/step_by_step_solver.py b/app/src/step_by_step_solver.py new file mode 100644 index 0000000..d11a139 --- /dev/null +++ b/app/src/step_by_step_solver.py @@ -0,0 +1,537 @@ +""" +Модуль для пошагового решения задач с системами счисления (2, 8, 16, n-ричные) +Алгоритм работает аналогично Photomath - показывает каждый шаг решения +""" + +from typing import List, Tuple, Dict +from enum import Enum + + +class Operation(Enum): + """Типы операций""" + ADDITION = "сложение" + SUBTRACTION = "вычитание" + + +class NumberSystem(Enum): + """Поддерживаемые системы счисления""" + BINARY = 2 + OCTAL = 8 + DECIMAL = 10 + HEXADECIMAL = 16 + + +class Step: + """Класс для представления одного шага решения""" + + def __init__(self, title: str, description: str, visual: str = "", result: str = ""): + self.title = title # Название шага + self.description = description # Описание что делается + self.visual = visual # Визуальное представление + self.result = result # Результат этого шага + + def __str__(self): + output = f"\n{'='*60}\n" + output += f"ШАГ: {self.title}\n" + output += f"{'='*60}\n" + output += f"{self.description}\n" + if self.visual: + output += f"\n{self.visual}\n" + if self.result: + output += f"\n➤ Результат: {self.result}\n" + return output + + +class StepByStepSolver: + """ + Класс для пошагового решения задач с системами счисления + """ + + def __init__(self, base: int = 10, digits: str = None): + """ + Инициализация решателя + + Args: + base: основание системы счисления (2, 8, 10, 16 или любое n) + digits: строка с символами для представления цифр (по умолчанию 0-9A-Z) + """ + self.base = base + self.steps: List[Step] = [] + # Используем пользовательский набор символов или стандартный + if digits is not None: + self.digits = digits + else: + self.digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + def clear_steps(self): + """Очистить все шаги решения""" + self.steps = [] + + def get_system_name(self) -> str: + """Получить название системы счисления""" + names = { + 2: "двоичная", + 8: "восьмеричная", + 10: "десятичная", + 16: "шестнадцатеричная" + } + return names.get(self.base, f"{self.base}-ричная") + + def validate_number(self, number: str) -> bool: + """ + Проверка корректности числа в данной системе счисления + + Args: + number: строка с числом + + Returns: + True если число корректно, False иначе + """ + number = number.upper().replace('-', '') + valid_digits = self.digits[:self.base] + return all(c in valid_digits for c in number) + + def to_decimal(self, number: str) -> int: + """ + Преобразование числа из n-ричной системы в десятичную + + Args: + number: число в n-ричной системе + + Returns: + число в десятичной системе + """ + is_negative = number.startswith('-') + number = number.replace('-', '').upper() + + result = 0 + for i, digit in enumerate(reversed(number)): + digit_value = self.digits.index(digit) + result += digit_value * (self.base ** i) + + return -result if is_negative else result + + def from_decimal(self, number: int) -> str: + """ + Преобразование из десятичной системы в n-ричную + + Args: + number: число в десятичной системе + + Returns: + число в n-ричной системе + """ + if number == 0: + return "0" + + is_negative = number < 0 + number = abs(number) + + result = "" + while number > 0: + result = self.digits[number % self.base] + result + number //= self.base + + return ("-" + result) if is_negative else result + + def convert_with_steps(self, number: str, target_base: int) -> List[Step]: + """ + Преобразование числа в другую систему счисления с пошаговым объяснением + + Args: + number: исходное число + target_base: целевая система счисления + + Returns: + список шагов решения + """ + steps = [] + is_negative = number.startswith('-') + number = number.replace('-', '').upper() + + # Шаг 1: Исходные данные + step1 = Step( + "Исходные данные", + f"Необходимо преобразовать число {number} из {self.get_system_name()} " + + f"({self.base}-ричной) системы в {target_base}-ричную систему", + f"Исходное число: {'-' if is_negative else ''}{number}₍{self.base}₎" + ) + steps.append(step1) + + # Шаг 2: Перевод в десятичную систему (если нужно) + if self.base != 10: + decimal_value = 0 + calculation = [] + + for i, digit in enumerate(reversed(number)): + digit_value = self.digits.index(digit) + power = self.base ** i + term_value = digit_value * power + decimal_value += term_value + calculation.append(f"{digit}×{self.base}^{i} = {digit_value}×{power} = {term_value}") + + visual = "\n".join(calculation) + visual += f"\n\nСумма: {' + '.join([str(digit_value * (self.base ** i)) for i, digit in enumerate(reversed(number)) for digit_value in [self.digits.index(digit)]])} = {decimal_value}" + + step2 = Step( + "Перевод в десятичную систему", + f"Каждую цифру умножаем на {self.base} в степени её позиции (справа налево, начиная с 0)", + visual, + f"{decimal_value}₍₁₀₎" + ) + steps.append(step2) + else: + decimal_value = int(number) + + # Шаг 3: Перевод из десятичной в целевую систему (если нужно) + if target_base != 10: + divisions = [] + temp = decimal_value + result_digits = [] + + while temp > 0: + remainder = temp % target_base + quotient = temp // target_base + result_digits.insert(0, self.digits[remainder]) + divisions.append(f"{temp} ÷ {target_base} = {quotient}, остаток: {remainder} ({self.digits[remainder]})") + temp = quotient + + result_number = ''.join(result_digits) if result_digits else "0" + + visual = "\n".join(divisions) + visual += f"\n\nЧитаем остатки снизу вверх: {result_number}" + + step3 = Step( + f"Перевод в {target_base}-ричную систему", + f"Делим число на {target_base} и записываем остатки. " + + "Читаем остатки снизу вверх", + visual, + f"{'-' if is_negative else ''}{result_number}₍{target_base}₎" + ) + steps.append(step3) + + final_result = ('-' if is_negative else '') + result_number + else: + final_result = str(decimal_value) + + # Финальный шаг + final_step = Step( + "Итоговый результат", + f"Число {'-' if is_negative else ''}{number}₍{self.base}₎ = {final_result}₍{target_base}₎", + "", + f"{final_result}₍{target_base}₎" + ) + steps.append(final_step) + + return steps + + def add_with_steps(self, num1: str, num2: str) -> List[Step]: + """ + Сложение двух чисел в n-ричной системе с пошаговым объяснением + + Args: + num1: первое число + num2: второе число + + Returns: + список шагов решения + """ + steps = [] + + # Шаг 1: Исходные данные + step1 = Step( + "Условие задачи", + f"Необходимо сложить два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления", + f"{num1}₍{self.base}₎ + {num2}₍{self.base}₎ = ?", + "" + ) + steps.append(step1) + + # Проверка на отрицательные числа + is_neg1 = num1.startswith('-') + is_neg2 = num2.startswith('-') + num1_clean = num1.replace('-', '').upper() + num2_clean = num2.replace('-', '').upper() + + # Шаг 2: Выравнивание разрядов + max_len = max(len(num1_clean), len(num2_clean)) + num1_aligned = num1_clean.zfill(max_len) + num2_aligned = num2_clean.zfill(max_len) + + visual = f" {num1_aligned}\n+ {num2_aligned}\n" + "-" * (max_len + 2) + + step2 = Step( + "Выравнивание чисел", + f"Дополняем числа нулями слева до одинаковой длины ({max_len} разрядов)", + visual, + "" + ) + steps.append(step2) + + # Шаг 3: Сложение в столбик + result = [] + carry = 0 + detailed_calc = [] + + for i in range(max_len - 1, -1, -1): + d1 = self.digits.index(num1_aligned[i]) + d2 = self.digits.index(num2_aligned[i]) + + total = d1 + d2 + carry + digit_result = total % self.base + new_carry = total // self.base + + result.insert(0, self.digits[digit_result]) + + position = max_len - i - 1 + explanation = f"Позиция {position}: {num1_aligned[i]}({d1}) + {num2_aligned[i]}({d2})" + if carry > 0: + explanation += f" + перенос({carry})" + explanation += f" = {total}" + + if new_carry > 0: + explanation += f" = {digit_result} (перенос {new_carry})" + else: + explanation += f" = {digit_result}" + + detailed_calc.insert(0, explanation) + carry = new_carry + + if carry > 0: + result.insert(0, self.digits[carry]) + detailed_calc.insert(0, f"Финальный перенос: {carry}") + + result_str = ''.join(result) + + visual = "\n".join(detailed_calc) + visual += f"\n\nВизуализация:\n {num1_aligned}\n+ {num2_aligned}\n" + "-" * (max_len + 2) + f"\n {result_str}" + + step3 = Step( + "Сложение в столбик", + f"Складываем числа справа налево, учитывая переносы. " + + f"При сумме ≥ {self.base} делим на {self.base}: остаток - результат, частное - перенос", + visual, + f"{result_str}₍{self.base}₎" + ) + steps.append(step3) + + # Шаг 4: Проверка через десятичную систему + dec1 = self.to_decimal(num1) + dec2 = self.to_decimal(num2) + dec_result = dec1 + dec2 + + step4 = Step( + "Проверка результата", + "Проверим правильность через перевод в десятичную систему", + f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" + + f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n" + + f"{dec1} + {dec2} = {dec_result}\n" + + f"{dec_result}₍₁₀₎ = {result_str}₍{self.base}₎", + f"✓ Ответ верен: {result_str}₍{self.base}₎" + ) + steps.append(step4) + + return steps + + def subtract_with_steps(self, num1: str, num2: str) -> List[Step]: + """ + Вычитание двух чисел в n-ричной системе с пошаговым объяснением + + Args: + num1: уменьшаемое + num2: вычитаемое + + Returns: + список шагов решения + """ + steps = [] + + # Шаг 1: Исходные данные + step1 = Step( + "Условие задачи", + f"Необходимо вычесть два числа в {self.get_system_name()} ({self.base}-ричной) системе счисления", + f"{num1}₍{self.base}₎ - {num2}₍{self.base}₎ = ?", + "" + ) + steps.append(step1) + + # Определяем знак результата + dec1 = self.to_decimal(num1) + dec2 = self.to_decimal(num2) + is_negative = dec1 < dec2 + + # Работаем с абсолютными значениями + if is_negative: + num1, num2 = num2, num1 + num1 = num1.replace('-', '') + num2 = num2.replace('-', '') + + num1_clean = num1.replace('-', '').upper() + num2_clean = num2.replace('-', '').upper() + + # Шаг 2: Выравнивание разрядов + max_len = max(len(num1_clean), len(num2_clean)) + num1_aligned = num1_clean.zfill(max_len) + num2_aligned = num2_clean.zfill(max_len) + + visual = f" {num1_aligned}\n- {num2_aligned}\n" + "-" * (max_len + 2) + + step2 = Step( + "Выравнивание чисел", + f"Дополняем числа нулями слева до одинаковой длины ({max_len} разрядов)", + visual, + "" + ) + steps.append(step2) + + # Шаг 3: Вычитание в столбик + result = [] + borrow = 0 + detailed_calc = [] + + num1_list = list(num1_aligned) + + for i in range(max_len - 1, -1, -1): + d1 = self.digits.index(num1_list[i]) + d2 = self.digits.index(num2_aligned[i]) + + # Учитываем заем + d1 -= borrow + + position = max_len - i - 1 + explanation = f"Позиция {position}: {num1_list[i]}({self.digits.index(num1_list[i])})" + + if borrow > 0: + explanation += f" - заем({borrow})" + explanation += f" = {d1}" + + explanation += f" - {num2_aligned[i]}({d2})" + + if d1 < d2: + # Нужен заем + d1 += self.base + borrow = 1 + explanation += f" → занимаем {self.base}, получаем {d1} - {d2} = {d1 - d2}" + else: + borrow = 0 + explanation += f" = {d1 - d2}" + + digit_result = d1 - d2 + result.insert(0, self.digits[digit_result]) + detailed_calc.insert(0, explanation) + + # Убираем ведущие нули + result_str = ''.join(result).lstrip('0') or '0' + + visual = "\n".join(detailed_calc) + visual += f"\n\nВизуализация:\n {num1_aligned}\n- {num2_aligned}\n" + "-" * (max_len + 2) + visual += f"\n {result_str.zfill(max_len)}" + + if is_negative: + result_str = '-' + result_str + + step3 = Step( + "Вычитание в столбик", + f"Вычитаем числа справа налево. " + + f"Если цифра уменьшаемого меньше вычитаемого, занимаем {self.base} из следующего разряда", + visual, + f"{result_str}₍{self.base}₎" + ) + steps.append(step3) + + # Шаг 4: Проверка + dec_result = dec1 - dec2 + + step4 = Step( + "Проверка результата", + "Проверим правильность через перевод в десятичную систему", + f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" + + f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n" + + f"{dec1} - {dec2} = {dec_result}\n" + + f"{dec_result}₍₁₀₎ = {result_str}₍{self.base}₎", + f"✓ Ответ верен: {result_str}₍{self.base}₎" + ) + steps.append(step4) + + return steps + + def solve(self, num1: str, num2: str, operation: Operation) -> List[Step]: + """ + Решить задачу с пошаговым объяснением + + Args: + num1: первое число + num2: второе число + operation: тип операции (сложение/вычитание) + + Returns: + список шагов решения + """ + if operation == Operation.ADDITION: + return self.add_with_steps(num1, num2) + elif operation == Operation.SUBTRACTION: + return self.subtract_with_steps(num1, num2) + else: + raise ValueError(f"Неподдерживаемая операция: {operation}") + + def print_solution(self, steps: List[Step]): + """ + Вывести решение в консоль + + Args: + steps: список шагов решения + """ + print("\n" + "="*60) + print("ПОШАГОВОЕ РЕШЕНИЕ") + print("="*60) + + for i, step in enumerate(steps, 1): + print(f"\n{'█'*60}") + print(f"ШАГ {i}: {step.title}") + print(f"{'█'*60}") + print(f"\n{step.description}") + + if step.visual: + print(f"\n{step.visual}") + + if step.result: + print(f"\n➤ Результат: {step.result}") + + print("\n" + "="*60) + print("РЕШЕНИЕ ЗАВЕРШЕНО") + print("="*60 + "\n") + + +# Пример использования +if __name__ == "__main__": + # Пример 1: Сложение в двоичной системе + print("ПРИМЕР 1: СЛОЖЕНИЕ В ДВОИЧНОЙ СИСТЕМЕ") + print("="*60) + + solver = StepByStepSolver(base=2) + steps = solver.solve("1011", "110", Operation.ADDITION) + solver.print_solution(steps) + + # Пример 2: Вычитание в восьмеричной системе + print("\n\nПРИМЕР 2: ВЫЧИТАНИЕ В ВОСЬМЕРИЧНОЙ СИСТЕМЕ") + print("="*60) + + solver = StepByStepSolver(base=8) + steps = solver.solve("756", "237", Operation.SUBTRACTION) + solver.print_solution(steps) + + # Пример 3: Сложение в шестнадцатеричной системе + print("\n\nПРИМЕР 3: СЛОЖЕНИЕ В ШЕСТНАДЦАТЕРИЧНОЙ СИСТЕМЕ") + print("="*60) + + solver = StepByStepSolver(base=16) + steps = solver.solve("A5", "3F", Operation.ADDITION) + solver.print_solution(steps) + + # Пример 4: Сложение в пятеричной системе + print("\n\nПРИМЕР 4: СЛОЖЕНИЕ В ПЯТЕРИЧНОЙ СИСТЕМЕ") + print("="*60) + + solver = StepByStepSolver(base=5) + steps = solver.solve("234", "142", Operation.ADDITION) + solver.print_solution(steps) diff --git a/app/src/test.py b/app/src/test.py new file mode 100644 index 0000000..f579578 --- /dev/null +++ b/app/src/test.py @@ -0,0 +1,14 @@ +import math + +v=20#скорость +p=21.5#необходимы путь +g=10#ускорение свободного падения + +t_par=v/g +h_pod=(v**2)/(2*g) +h_main=p-h_pod +t_pod1=g/2 +t_pod2=math.sqrt(h_main/t_pod1) +t=t_pod2-t_par + +pass \ No newline at end of file diff --git a/app/src/test_base50.py b/app/src/test_base50.py new file mode 100644 index 0000000..55bcf25 --- /dev/null +++ b/app/src/test_base50.py @@ -0,0 +1,67 @@ +""" +Тестирование 50-ричного калькулятора +""" + +import sys +import os +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + +from step_by_step_solver import StepByStepSolver, Operation + +# Тест 50-ричной системы +print("="*60) +print("ТЕСТИРОВАНИЕ 50-РИЧНОЙ СИСТЕМЫ СЧИСЛЕНИЯ") +print("="*60) + +solver = StepByStepSolver(base=50) + +# Пример 1: Простое сложение +print("\n\nПРИМЕР 1: Сложение A + B в 50-ричной системе") +print("="*60) +print("A₍₅₀₎ = 10₍₁₀₎") +print("B₍₅₀₎ = 11₍₁₀₎") +print("Ожидаемый результат: V₍₅₀₎ = 21₍₁₀₎") +print() +steps = solver.solve("A", "B", Operation.ADDITION) +solver.print_solution(steps) + +# Пример 2: Сложение с переносом +print("\n\nПРИМЕР 2: Сложение N + 1 в 50-ричной системе") +print("="*60) +print("N₍₅₀₎ = 49₍₁₀₎") +print("1₍₅₀₎ = 1₍₁₀₎") +print("Ожидаемый результат: 10₍₅₀₎ = 50₍₁₀₎") +print() +steps = solver.solve("N", "1", Operation.ADDITION) +solver.print_solution(steps) + +# Пример 3: Большие числа +print("\n\nПРИМЕР 3: Сложение 1A + 2B в 50-ричной системе") +print("="*60) +print("1A₍₅₀₎ = 1×50 + 10 = 60₍₁₀₎") +print("2B₍₅₀₎ = 2×50 + 11 = 111₍₁₀₎") +print("Ожидаемый результат: 3V₍₅₀₎ = 171₍₁₀₎") +print() +steps = solver.solve("1A", "2B", Operation.ADDITION) +solver.print_solution(steps) + +# Пример 4: Вычитание +print("\n\nПРИМЕР 4: Вычитание 2A - B в 50-ричной системе") +print("="*60) +print("2A₍₅₀₎ = 2×50 + 10 = 110₍₁₀₎") +print("B₍₅₀₎ = 11₍₁₀₎") +print("Ожидаемый результат: 1N₍₅₀₎ = 99₍₁₀₎") +print() +steps = solver.solve("2A", "B", Operation.SUBTRACTION) +solver.print_solution(steps) + +# Проверка правильности конвертации +print("\n\nПРОВЕРКА ТАБЛИЦЫ СООТВЕТСТВИЙ (50-ричная → десятичная)") +print("="*60) +digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" +print("Первые 50 символов:") +for i in range(50): + if i % 10 == 0: + print() + print(f"{digits[i]}={i:2d}", end=" ") +print("\n") diff --git a/main.py b/main.py index e69de29..6a5f7ce 100644 --- a/main.py +++ b/main.py @@ -0,0 +1,28 @@ +import math as m + +def decimal_to_base50(n): + if n == 0: + return "0" + + # Символы для 50-ичной системы: 0-9, A-Z, a-m (всего 10+26+14=50) + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm" + + result = "" + while n > 0: + remainder = n % 50 + result = digits[remainder] + result + n = n // 50 + + return result + +def base50_to_decimal(s): + # Словарь для преобразования символов в числа + digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm" + char_to_value = {char: i for i, char in enumerate(digits)} + + result = 0 + for char in s: + result = result * 50 + char_to_value[char] + + return result +