787 lines
37 KiB
Python
787 lines
37 KiB
Python
"""
|
||
Модуль для пошагового решения задач с системами счисления (2, 8, 16, n-ричные)
|
||
Алгоритм работает аналогично Photomath - показывает каждый шаг решения
|
||
"""
|
||
|
||
from typing import List, Tuple, Dict
|
||
from enum import Enum
|
||
|
||
|
||
class Operation(Enum):
|
||
"""Типы операций"""
|
||
ADDITION = "сложение"
|
||
SUBTRACTION = "вычитание"
|
||
MULTIPLICATION = "умножение"
|
||
DIVISION = "деление"
|
||
|
||
|
||
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}-ричную систему.\n" +
|
||
f"Это как перевод с одного языка на другой - число остаётся тем же, меняется только способ записи!",
|
||
f"Исходное число: {'-' if is_negative else ''}{number}₍{self.base}₎"
|
||
)
|
||
steps.append(step1)
|
||
|
||
# Шаг 2: Перевод в десятичную систему (если нужно)
|
||
if self.base != 10:
|
||
decimal_value = 0
|
||
calculation = []
|
||
position_table = []
|
||
|
||
# Создаём таблицу разрядов
|
||
digits_list = list(reversed(number))
|
||
position_table.append("┌─" + "─┬─".join(["─"*5 for _ in digits_list]) + "─┐")
|
||
position_table.append("│ " + " │ ".join([f"Поз {i}" for i in range(len(digits_list))]) + " │")
|
||
position_table.append("├─" + "─┼─".join(["─"*5 for _ in digits_list]) + "─┤")
|
||
position_table.append("│ " + " │ ".join([f"{self.base}^{i}={self.base**i}".center(5) for i in range(len(digits_list))]) + " │")
|
||
position_table.append("├─" + "─┼─".join(["─"*5 for _ in digits_list]) + "─┤")
|
||
position_table.append("│ " + " │ ".join([f"{d}".center(5) for d in digits_list]) + " │")
|
||
position_table.append("└─" + "─┴─".join(["─"*5 for _ in digits_list]) + "─┘")
|
||
|
||
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"Позиция {i}: {digit} × {self.base}^{i} = {digit_value} × {power} = {term_value}")
|
||
|
||
visual = "\n".join(position_table)
|
||
visual += "\n\nПОШАГОВЫЙ РАСЧЁТ:\n"
|
||
visual += "Каждая позиция (справа налево) имеет свой \"вес\" - степень основания.\n"
|
||
visual += f"В {self.base}-ричной системе веса позиций: 1, {self.base}, {self.base**2}, {self.base**3}...\n\n"
|
||
visual += "\n".join(calculation)
|
||
|
||
sum_parts = [str(self.digits.index(digit) * (self.base ** i)) for i, digit in enumerate(reversed(number))]
|
||
visual += f"\n\nИТОГОВАЯ СУММА: {' + '.join(sum_parts)} = {decimal_value}"
|
||
|
||
step2 = Step(
|
||
"Перевод в десятичную систему",
|
||
f"Представьте, что каждая позиция - это \"кармашек\" с определённой ценностью.\n" +
|
||
f"Самый правый \"стоит\" 1, следующий - {self.base}, потом {self.base**2} и так далее.\n" +
|
||
f"Мы умножаем цифру на \"цену\" её позиции и складываем всё вместе.",
|
||
visual,
|
||
f"{decimal_value}₍₁₀₎"
|
||
)
|
||
steps.append(step2)
|
||
else:
|
||
decimal_value = int(number)
|
||
|
||
# Шаг 3: Перевод из десятичной в целевую систему (если нужно)
|
||
if target_base != 10:
|
||
divisions = []
|
||
temp = decimal_value
|
||
result_digits = []
|
||
step_num = 1
|
||
|
||
visual = f"МЕТОД: Делим число на {target_base} до тех пор, пока не получим 0.\n"
|
||
visual += "Каждый остаток от деления - это одна цифра результата.\n"
|
||
visual += "Важно! Первый остаток будет ПОСЛЕДНЕЙ цифрой ответа.\n\n"
|
||
|
||
while temp > 0:
|
||
remainder = temp % target_base
|
||
quotient = temp // target_base
|
||
result_digits.insert(0, self.digits[remainder])
|
||
divisions.append(f"Шаг {step_num}: {temp} ÷ {target_base} = {quotient}, остаток: {remainder} → цифра '{self.digits[remainder]}'")
|
||
step_num += 1
|
||
temp = quotient
|
||
|
||
result_number = ''.join(result_digits) if result_digits else "0"
|
||
|
||
visual += "\n".join(divisions)
|
||
visual += "\n\n" + "="*50
|
||
visual += f"\nСобираем остатки СНИЗУ ВВЕРХ (от последнего к первому):\n"
|
||
visual += "═"*50 + "\n"
|
||
for i, digit in enumerate(result_digits):
|
||
visual += f" ↑ {digit} "
|
||
visual += f"\n\nОтвет: {result_number}"
|
||
|
||
step3 = Step(
|
||
f"Перевод в {target_base}-ричную систему",
|
||
f"Алгоритм деления с остатком - классический способ перевода.\n" +
|
||
f"Представьте, что вы раскладываете конфеты по коробкам: в каждой {target_base} штук.\n" +
|
||
f"Остатки показывают, сколько конфет не поместилось в полные коробки на каждом этапе.",
|
||
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}-ричной) системе счисления.\n" +
|
||
f"Принцип такой же, как в десятичной системе, но \"переполнение\" происходит при {self.base}, а не при 10!\n" +
|
||
f"Пример: если в десятичной 9+1=10 (переносим 1), то в двоичной 1+1=10₍₂₎ (тоже переносим 1).",
|
||
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.append(explanation)
|
||
carry = new_carry
|
||
|
||
if carry > 0:
|
||
result.insert(0, self.digits[carry])
|
||
detailed_calc.append(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"Складываем числа справа налево, учитывая переносы.\n" +
|
||
f"ПРАВИЛО: Если сумма ≥ {self.base}, то делим на {self.base}:\n" +
|
||
f" • Остаток от деления → записываем в результат\n" +
|
||
f" • Частное → переносим в следующий разряд\n" +
|
||
f"Например: если {self.base-1} + 1 = {self.base}, пишем 0 и переносим 1 (как 9+1=10 в десятичной).",
|
||
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}-ричной) системе счисления.\n" +
|
||
f"Вычитание работает как в десятичной, но занимаем не 10, а {self.base}.\n" +
|
||
f"Например: в десятичной при 3-5 занимаем 10, получается 13-5=8.\n" +
|
||
f"В {self.base}-ричной системе занимаем {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.append(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"Вычитаем числа справа налево.\n" +
|
||
f"ПРАВИЛО ЗАИМСТВОВАНИЯ:\n" +
|
||
f" • Если верхняя цифра < нижней, занимаем {self.base} из следующего разряда\n" +
|
||
f" • К верхней цифре прибавляем {self.base}, вычитаем нижнюю\n" +
|
||
f" • Из следующего разряда вычитаем 1 (это \"долг\")\n" +
|
||
f"Это как занять 10₽ у друга, чтобы хватило на покупку!",
|
||
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 multiply_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}-ричной) системе счисления.\n" +
|
||
f"\nСПОСОБ РЕШЕНИЯ:\n" +
|
||
f"Умножение напрямую в {self.base}-ричной системе сложное, поэтому используем \"переводчика\":\n" +
|
||
f" 1. Переводим оба числа в привычную десятичную систему\n" +
|
||
f" 2. Умножаем в десятичной системе (это мы умеем!)\n" +
|
||
f" 3. Переводим результат обратно в {self.base}-ричную систему\n" +
|
||
f"Это как перевести иностранный текст на русский, решить задачу и перевести ответ обратно.",
|
||
f"{num1}₍{self.base}₎ × {num2}₍{self.base}₎ = ?",
|
||
""
|
||
)
|
||
steps.append(step1)
|
||
|
||
# Определяем знак результата
|
||
dec1 = self.to_decimal(num1)
|
||
dec2 = self.to_decimal(num2)
|
||
|
||
num1_clean = num1.replace('-', '').upper()
|
||
num2_clean = num2.replace('-', '').upper()
|
||
|
||
# Шаг 2: Умножение через десятичную систему (простой подход)
|
||
result_dec = dec1 * dec2
|
||
result_str = self.from_decimal(result_dec)
|
||
|
||
step2 = Step(
|
||
"Шаг 1: Преобразование в десятичную систему",
|
||
f"Переводим множители из {self.base}-ричной в десятичную систему.\n" +
|
||
f"Используем метод разложения по разрядам (см. примеры выше).",
|
||
f"{num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||
f"{num2}₍{self.base}₎ = {dec2}₍₁₀₎\n\n" +
|
||
f"Теперь работаем с привычными десятичными числами!",
|
||
f"Множители: {dec1} и {dec2}"
|
||
)
|
||
steps.append(step2)
|
||
|
||
# Шаг 3: Умножение в десятичной системе
|
||
step3 = Step(
|
||
"Шаг 2: Умножение в десятичной системе",
|
||
f"Перемножаем числа обычным способом (как учили в школе).\n" +
|
||
f"Это самая простая часть - обычное умножение!",
|
||
f"{dec1} × {dec2} = {result_dec}\n\n" +
|
||
f"Получили результат в десятичной системе: {result_dec}",
|
||
f"{result_dec}₍₁₀₎"
|
||
)
|
||
steps.append(step3)
|
||
|
||
# Шаг 4: Обратное преобразование
|
||
step4 = Step(
|
||
f"Шаг 3: Перевод результата в {self.base}-ричную систему",
|
||
f"Переводим полученный результат обратно в {self.base}-ричную систему.\n" +
|
||
f"Используем метод деления с остатком (см. примеры выше).",
|
||
f"{result_dec}₍₁₀₎ = {result_str}₍{self.base}₎\n\n" +
|
||
f"Вернули ответ в исходную систему счисления!",
|
||
f"{result_str}₍{self.base}₎"
|
||
)
|
||
steps.append(step4)
|
||
|
||
# Шаг 5: Проверка
|
||
step5 = Step(
|
||
"Итоговый результат и проверка",
|
||
f"Проверяем логику решения:\n" +
|
||
f" • Исходная задача: {num1}₍{self.base}₎ × {num2}₍{self.base}₎\n" +
|
||
f" • Перевели в десятичную: {dec1} × {dec2}\n" +
|
||
f" • Получили результат: {result_dec}\n" +
|
||
f" • Вернули в {self.base}-ричную: {result_str}₍{self.base}₎",
|
||
f"┌─────────────────────────────────┐\n" +
|
||
f"│ {num1}₍{self.base}₎ × {num2}₍{self.base}₎ = {result_str}₍{self.base}₎ │\n" +
|
||
f"└─────────────────────────────────┘\n" +
|
||
f"Проверка: {dec1} × {dec2} = {result_dec}₍₁₀₎ = {result_str}₍{self.base}₎",
|
||
f"✓ Ответ: {result_str}₍{self.base}₎"
|
||
)
|
||
steps.append(step5)
|
||
|
||
return steps
|
||
|
||
def divide_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}-ричной) системе счисления.\n" +
|
||
f"\nСПОСОБ РЕШЕНИЯ:\n" +
|
||
f"Деление в {self.base}-ричной системе очень трудоёмкое, поэтому используем метод \"переводчика\":\n" +
|
||
f" 1. Переводим делимое и делитель в десятичную систему\n" +
|
||
f" 2. Делим в десятичной системе (получаем частное и остаток)\n" +
|
||
f" 3. Переводим частное и остаток обратно в {self.base}-ричную систему\n" +
|
||
f"Проще решить на \"родном языке\" математики - в десятичной системе!",
|
||
f"{num1}₍{self.base}₎ ÷ {num2}₍{self.base}₎ = ?",
|
||
""
|
||
)
|
||
steps.append(step1)
|
||
|
||
# Проверка деления на ноль
|
||
dec2 = self.to_decimal(num2)
|
||
if dec2 == 0:
|
||
step_error = Step(
|
||
"Ошибка",
|
||
"Деление на ноль невозможно!",
|
||
"",
|
||
"❌ Деление на ноль"
|
||
)
|
||
steps.append(step_error)
|
||
return steps
|
||
|
||
dec1 = self.to_decimal(num1)
|
||
|
||
num1_clean = num1.replace('-', '').upper()
|
||
num2_clean = num2.replace('-', '').upper()
|
||
|
||
# Шаг 2: Преобразование в десятичную систему
|
||
step2 = Step(
|
||
"Шаг 1: Преобразование в десятичную систему",
|
||
f"Переводим делимое и делитель из {self.base}-ричной в десятичную систему.\n" +
|
||
f"Используем знакомый метод разложения по разрядам.",
|
||
f"Делимое: {num1}₍{self.base}₎ = {dec1}₍₁₀₎\n" +
|
||
f"Делитель: {num2}₍{self.base}₎ = {dec2}₍₁₀₎\n\n" +
|
||
f"Теперь задача выглядит просто: {dec1} ÷ {dec2}",
|
||
f"Числа в десятичной: {dec1} ÷ {dec2}"
|
||
)
|
||
steps.append(step2)
|
||
|
||
# Шаг 3: Деление в десятичной системе (целочисленное)
|
||
quotient = dec1 // dec2
|
||
remainder = dec1 % dec2
|
||
|
||
step3 = Step(
|
||
"Шаг 2: Деление в десятичной системе",
|
||
f"Выполняем целочисленное деление (деление нацело).\n" +
|
||
f"\nЧТО ПОЛУЧАЕМ:\n" +
|
||
f" • Частное (сколько раз {dec2} помещается в {dec1})\n" +
|
||
f" • Остаток (что осталось после деления)\n" +
|
||
f"\nПример: 17 ÷ 5 = 3 (остаток 2), потому что 5×3=15, а 17-15=2",
|
||
f"{dec1} ÷ {dec2} = {quotient} (остаток {remainder})\n\n" +
|
||
f"Проверка: {dec2} × {quotient} + {remainder} = {dec2 * quotient} + {remainder} = {dec1} ✓",
|
||
f"Частное: {quotient}₍₁₀₎, Остаток: {remainder}₍₁₀₎"
|
||
)
|
||
steps.append(step3)
|
||
|
||
# Шаг 4: Обратное преобразование
|
||
quotient_str = self.from_decimal(quotient)
|
||
remainder_str = self.from_decimal(remainder) if remainder != 0 else "0"
|
||
|
||
step4 = Step(
|
||
f"Шаг 3: Перевод результата в {self.base}-ричную систему",
|
||
f"Переводим частное и остаток обратно в {self.base}-ричную систему.\n" +
|
||
f"Используем метод деления с остатком для перевода.",
|
||
f"Частное: {quotient}₍₁₀₎ → {quotient_str}₍{self.base}₎\n" +
|
||
f"Остаток: {remainder}₍₁₀₎ → {remainder_str}₍{self.base}₎\n\n" +
|
||
f"Вернули ответ в исходную систему счисления!",
|
||
f"{quotient_str}₍{self.base}₎ (остаток {remainder_str}₍{self.base}₎)"
|
||
)
|
||
steps.append(step4)
|
||
|
||
# Шаг 5: Проверка
|
||
check_result = dec2 * quotient + remainder
|
||
step5 = Step(
|
||
"Итоговый результат и проверка",
|
||
f"Проверяем правильность по формуле:\n" +
|
||
f" Делимое = Делитель × Частное + Остаток\n" +
|
||
f"Это основное правило деления с остатком!",
|
||
f"┌──────────────────────────────────────┐\n" +
|
||
f"│ {num1}₍{self.base}₎ ÷ {num2}₍{self.base}₎ = {quotient_str}₍{self.base}₎" +
|
||
(f" (ост. {remainder_str}₍{self.base}₎)" if remainder != 0 else "") + " │\n" +
|
||
f"└──────────────────────────────────────┘\n\n" +
|
||
f"Проверка в десятичной: {dec2} × {quotient} + {remainder} = {check_result} ✓\n" +
|
||
f"Проверка в {self.base}-ричной: {num2}₍{self.base}₎ × {quotient_str}₍{self.base}₎" +
|
||
(f" + {remainder_str}₍{self.base}₎" if remainder != 0 else "") + f" = {num1}₍{self.base}₎ ✓",
|
||
f"✓ Ответ: {quotient_str}₍{self.base}₎" + (f" (остаток {remainder_str}₍{self.base}₎)" if remainder != 0 else "")
|
||
)
|
||
steps.append(step5)
|
||
|
||
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)
|
||
elif operation == Operation.MULTIPLICATION:
|
||
return self.multiply_with_steps(num1, num2)
|
||
elif operation == Operation.DIVISION:
|
||
return self.divide_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)
|