finaly
This commit is contained in:
537
app/src/step_by_step_solver.py
Normal file
537
app/src/step_by_step_solver.py
Normal file
@@ -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)
|
||||
Reference in New Issue
Block a user