ljw20090730 2024-02-10 00:48 采纳率: 17.4%
浏览 3
已结题

sympy中解决自定义类型放入solve的问题

为了提高精度,我自定义了一个MyFloat类型

from sympy.core.numbers import *
from sympy.core.numbers import _sympifyit, _as_integer_ratio, _errdict, _sympify

class MyFloat(Float):
    #is_rational = is_Rational = True
    #is_irrational = False
    def __new__(cls, arg):
        if isinstance(Rational(arg), Integer):
            return Rational(arg)
        arg = str(arg).replace(' ', '').strip('0')
        if arg[0] == '.': arg = f'0{arg}'
        prec = len(arg.replace('.', ''))
        return super().__new__(cls, arg, prec)
    def __init__(self, arg):
        self.content = str(arg)
    def __str__(self):
        return self.content
    @_sympifyit('other', NotImplemented)
    def __add__(self, other):
        if isinstance(other, Number) and global_parameters.evaluate:
            if isinstance(other, Rational) and other.q != 1:
                return Rational(str(self)) + other
            a1 = str(self).split('.')
            a2 = str(Float(other)).split('.')
            length = max(len(a1[1]), len(a2[1]))
            a1[1] = a1[1].ljust(length, '0')
            a2[1] = a2[1].ljust(length, '0')
            result = str(int(''.join(a1)) + int(''.join(a2)))
            return MyFloat(f'{result[:-length]}.{result[-length:]}')
        return Number.__add__(self, other)

    @_sympifyit('other', NotImplemented)
    def __sub__(self, other):
        if isinstance(other, Number) and global_parameters.evaluate:
            if isinstance(other, Rational) and other.q != 1:
                return Rational(str(self)) - other
            a1 = str(self).split('.')
            a2 = str(Float(other)).split('.')
            length = max(len(a1[1]), len(a2[1]))
            a1[1] = a1[1].ljust(length, '0')
            a2[1] = a2[1].ljust(length, '0')
            result = str(int(''.join(a1)) - int(''.join(a2)))
            return MyFloat(f'{result[:-length]}.{result[-length:]}')
        return Number.__sub__(self, other)

    @_sympifyit('other', NotImplemented)
    def __mul__(self, other):
        if isinstance(other, Number) and global_parameters.evaluate:
            if isinstance(other, Rational) and other.q != 1:
                return Rational(str(self)) * other
            a1 = str(self).rstrip('0').split('.')
            a2 = str(Float(other)).rstrip('0').split('.')
            length = len(a1[1]) + len(a2[1])
            result = str(int(''.join(a1)) * int(''.join(a2)))
            return MyFloat(f'{result[:-length]}.{result[-length:]}')
        return Number.__mul__(self, other)

    @_sympifyit('other', NotImplemented)
    def __truediv__(self, other):
        if isinstance(other, Number) and other != 0 and global_parameters.evaluate:
            return Rational(str(self))/Rational(str(other))
        return Number.__truediv__(self, other)

    @_sympifyit('other', NotImplemented)
    def __mod__(self, other):
        if isinstance(other, Rational) and other.q != 1 and global_parameters.evaluate:
            # calculate mod with Rationals, *then* round the result
            return Rational(str(self)) % other
        if isinstance(other, Number) and global_parameters.evaluate:
            r = Rational(str(self)) // Rational(str(other))
            return self - MyFloat(str(other))*r
        return Number.__mod__(self, other)

    def _latex(self, printer):
        return str(self).rstrip('0')

但是下面的代码我期望输出[45/23],我想知道出了什么问题以及如何解决(这里的MyFloat不能用Rational代替)

>>> solve(x*MyFloat('2.3')-MyFloat('4.5')) # solve和x已经在sympy和sympy.abc中导入
[1.95652173913043]
  • 写回答

4条回答 默认 最新

  • 专家-赤兔[在线] 优质创作者: 编程框架技术领域 2024-02-10 00:49
    关注

    引自免费微信小程序:皆我百晓生

    在您提供的自定义 MyFloat 类中,尽管您已经实现了基本的数学运算符重载方法(如加减乘除和取模),但在 SymPy 中使用 solve 函数时遇到了问题。SymPy 的 solve 函数并不直接理解或支持 MyFloat 类型作为方程中的元素。

    要解决这个问题,您需要确保 SymPy 能够识别并正确处理 MyFloat 类型。一种可能的解决方案是为 MyFloat 添加足够的 SymPy 兼容性方法,特别是实现 _eval_solveset 方法,该方法允许 SymPy 在求解方程时将 MyFloat 转换为可处理的类型。以下是一个示例,展示了如何添加 _eval_solveset 方法:

    from sympy import solveset, S, sympify
    
    class MyFloat(Float):
        # ...(保留您的现有代码)
    
        def _eval_solveset(self, f, symbol, domain=S.Complexes):
            return solveset(sympify(f.subs(symbol, Rational(str(self)))), symbol, domain)
    
    # 现在尝试解决方程
    from sympy.abc import x
    from sympy import solve
    
    solution = solve(x*MyFloat('2.3') - MyFloat('4.5'), x)
    print(solution)  # 应输出期望的结果
    

    然而,在实际应用中,由于 MyFloat 是基于字符串表示的浮点数,并且其设计目的是提高精度,将其用于符号计算可能会遇到精度丢失的问题。在 SymPy 中进行精确求解时,通常更推荐使用 Rational 或保持变量为未指定类型的 Symbol

    为了使上述代码正常工作,您还需要确保 MyFloat 类与 SymPy 内部的其他机制兼容。但是,请注意,即使实现 _eval_solveset 方法,由于 MyFloat 类的设计和内部转换逻辑,仍有可能无法得到理想的 [45/23] 结果,除非您在类内部实现对浮点数到有理数的智能转换,并在涉及求解时始终转换为 Rational 类型。这通常意味着在关键的数学运算中,应确保优先返回 Rational 对象而不是 MyFloat 对象。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

问题事件

  • 系统已结题 8月5日
  • 已采纳回答 7月28日
  • 创建了问题 2月10日