Hallo Habr! Heute finden Sie ein Miniatur-Tutorial zum Parsen eines Strings mit einem mathematischen Ausdruck und zum Berechnen mit unscharfen Dreieckszahlen. Bei entsprechenden Änderungen am Code funktioniert das Lernprogramm mit anderen "benutzerdefinierten" Variablen. Hilfe: Fuzzy-Dreieckszahlen - ein Sonderfall von Fuzzy-Zahlen (Fuzzy-Variablen auf der numerischen Achse). Ich empfehle
hier und
hier näher
kennenzulernen .
Anforderungen:
- Die Programmiersprache Python 3.x (der im Artikel bereitgestellte Code wurde auf Python 3.5 getestet)
- Sympy- Bibliothek , kann über das Terminal (Konsole) installiert werden:
pip install sympy
Das Verfahren zur Lösung des Problems:
- Wir verbinden Bibliotheken
from fractions import Fraction import re from typing import Iterable from random import random import sympy
Verbindungsbrüche sind optional. Wir verwenden Bruch, um reelle Zahlen in Form eines Bruchs zu speichern (um den Genauigkeitsverlust zu minimieren). Wir werden die re-Bibliothek verwenden, um Zeichenfolgen zu analysieren und automatisch eine Liste von Zeichenvariablen zu generieren.
Die Verwendung der Typisierungsbibliothek ist optional. Wir verwenden sie, um die Arten von Funktionsparametern explizit anzugeben. Die Zufallsbibliothek wird verwendet, um Testwerte für Fuzzy-Variablen zu generieren. sympy ist eine großartige Bibliothek für Zeichenberechnungen in Python. Damit arbeiten wir mit der Ausdruckszeichenfolge selbst.
- Wir beschreiben die Klasse der Fuzzy-Dreieckszahlen und deren Operationen. In diesem Beispiel sind drei Operationen ausreichend (Addition, Subtraktion und Division). Wir werden Operationen mit der Überladung von "magischen" Methoden der entsprechenden Klasse einführen:
class FuzzyTriangular(object): """ FuzzyTriangular""" def __init__(self, floatdigit = None, ABC = None, CAB = None, CDD = None): super(FuzzyTriangular, self).__init__() if ABC or floatdigit: if isinstance(floatdigit, (int, float)): self._a = Fraction(floatdigit)
Darstellungsformen von Fuzzy-Dreieckszahlen können unterschiedlich sein, wir werden nicht tief gehen. Im vorgestellten Code werden wir auf die Methoden __add__ (Additionsoperator), __sub__ (Subtraktionsoperator), __mul__ (Multiplikationsoperator) achten. Wenn Sie versuchen, einer Fuzzy-Dreieckszahl eine reelle Zahl hinzuzufügen, wird diese in eine Fuzzy-Dreieckszahl umgewandelt. Eine ähnliche Situation mit einem Tupel oder einer Liste von reellen Zahlen - die ersten drei Zahlen werden als Fuzzy-Dreieck wahrgenommen (und auch in die FuzzyTriangular-Klasse konvertiert). Die Methode __pos__ überschreibt den unären Operator "+". Die __neg__- Methode ist ein unäres "-". Die Methode __eq__ überschreibt den Operator ==. Auf Wunsch können Sie zusätzlich Operationen neu definieren, z. B.:
- Teilung
- Potenzierung
- Zahlenmodul
- Vergleiche (mehr / weniger, mehr oder gleich / weniger oder gleich)
- Skalarisierung (Umwandlung in int, float, komplexe Zahlen, Rundung)
- Inversion und andere ...
Sie können die Angemessenheit der eingegebenen Operationen mit einer kleinen Reihe von Tests überprüfen, zum Beispiel:
ZERO = FuzzyTriangular((0,0,0)) ONE = FuzzyTriangular((1,1,1)) A = FuzzyTriangular((0.3,0.5,0.9)) B = FuzzyTriangular((0.2,0.4,0.67)) C = FuzzyTriangular((0,0.33,0.72)) print('ZERO = '+str(ZERO)) print('ONE = '+str(ONE)) print('A = '+str(A)) print('B = '+str(B)) print('C = '+str(C))
Diese Überprüfungsoperationen für Addition, Division und Multiplikation werden im Code angegeben und gemäß der Neudefinition der "magischen" Methoden durchgeführt. Wir möchten in der Lage sein, dieselben Operationen unter Verwendung von Symbolvariablen in zuvor unbekannten Ausdrücken auszuführen. Dies erfordert die Einführung mehrerer Hilfsfunktionen. - Wir führen Hilfsfunktionen ein:
def symbols_from_expr(expr_str: str, pattern=r"[A-Za-z]\d{,2}") -> tuple: """ """ symbols_set = set(re.findall(pattern, expr_str)) symbols_set = sorted(symbols_set) symbols_list = tuple(sympy.symbols(symbols_set)) return symbols_list
Wir werden diese Funktion verwenden, um nach Zeichenvariablen in einer Ausdruckszeichenfolge zu suchen (die Standardvorlage ist ein Zeichen von A bis Z oder von a bis z und eine Ganzzahl danach mit einer Länge von bis zu 2 Zeichen (oder das Fehlen einer Zahl). def expr_subs(expr_str: str, symbols: Iterable, values: Iterable): """ values symbols - expr_str""" expr = sympy.sympify(expr_str) func = sympy.lambdify(tuple(symbols), expr, 'sympy') return func(*values)
Mit dieser Funktion können Sie den Wert eines Zeichenfolgenausdrucks mit Substitution anstelle von Variablen mit symbolischen Variablen eines beliebigen gültigen Typs berechnen (wenn die im Zeichenfolgenausdruck selbst enthaltenen Operationen überschrieben werden). Dies ist dank der Funktion sympy.lambdify möglich, die einen Sympy-Ausdruck in eine Lambda-Funktion konvertiert, die "magische" Methoden akzeptiert. Eine wichtige Voraussetzung für die ordnungsgemäße Funktion der Funktion ist die korrekte Reihenfolge der Elemente in Symbolen und Werten (Entsprechung von Symbolen und ersetzten Werten).- Jedes Mal ist das Erstellen einer Lambda-Funktion teuer. Wenn die mehrfache Verwendung desselben Ausdrucks erforderlich ist, wird empfohlen, die folgenden zwei Funktionen zu verwenden:
def lambda_func(expr_str: str, symbols: Iterable) -> callable: """ -, - expr_str symbols""" expr = sympy.sympify(expr_str) func = sympy.lambdify(tuple(symbols), expr, 'sympy') return func def func_subs(expr_func: callable, values: Iterable): """ - expr_func values""" return expr_func(*values)
Die erste gibt die Lambda-Funktion selbst zurück, und die zweite ermöglicht es Ihnen, die resultierenden Werte durch Ersetzen einer Werteliste zu berechnen. Erneut wird darauf geachtet, dass die verwendeten Werte keine dreieckigen Fuzzy-Zahlen sein müssen.
- Wir lesen die Formelzeile aus der Datei
with open('expr.txt', 'r') as file: expr_str = file.read() print('expr_str', expr_str)
So etwas kann als Zeilenformel für die Datei expr.txt verwendet werden:
p36*q67*p57*p26*p25*p13*q12*q15 + + p36*q67*p47*p26*p24*p13*q12 + + p67*q57*p26*p25*q12*p15 + + q57*p47*p25*p24*q12*p15 + + p57*p25*p12*q15 + + p36*p67*p13 + + p67*p26*p12 + + p47*p24*p12 + + p57*p15 - - p57*p47*p24*p12*p15 - - p67*p47*p26*p24*p12 - - p67*p57*p26*p12*p15 + + p67*p57*p47*p26*p24*p12*p15 - - p36*p67*p26*p13*p12 - - p36*p67*p47*p24*p13*p12 - - p36*p67*p57*p13*p15 + + p36*p67*p57*p47*p24*p13*p12*p15 + + p36*p67*p47*p26*p24*p13*p12 + + p36*p67*p57*p26*p13*p12*p15 - - p36*p67*p57*p47*p26*p24*p13*p12*p15 - - p36*p67*p57*p25*p13*p12*q15 - - p67*p57*p26*p25*p12*q15 - - p57*p47*p25*p24*p12*q15 + + p67*p57*p47*p26*p25*p24*p12*q15 + + p36*p67*p57*p26*p25*p13*p12*q15 + + p36*p67*p57*p47*p25*p24*p13*p12*q15 - - p36*p67*p57*p47*p26*p25*p24*p13*p12*q15 - - p36*p67*q57*p47*q26*p25*p24*p13*q12*p15 - - p67*q57*p47*p26*p25*p24*q12*p15 - - p36*p67*q57*p26*p25*p13*q12*p15 - - p36*q67*q57*p47*p26*p25*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p25*p24*p13*q12*q15
- Wir erhalten die Zeichenvariablen aus dem Zeichenfolgenausdruck:
symbols = symbols_from_expr(expr_str) print('AutoSymbols', symbols)
- Wir generieren zufällige Testdreieckszahlen:
values = tuple([FuzzyTriangular(sorted([random(),random(),random()]))\ for i in range(len(symbols))])
Das Sortieren von Zufallswerten ist erforderlich, um der Reihenfolge der Werte der linken „0“, der mittleren und der rechten „0“ zu entsprechen. - Konvertieren Sie die Formelzeichenfolge in einen Ausdruck:
func = lambda_func(expr_str, symbols) print('func', '=', func)
- Wir berechnen den Wert der Formel mit der Lambda-Funktion (wir verwenden func_subs und expr_subs, um sicherzustellen, dass die Ergebnisse übereinstimmen):
print('func_subs', '=', func_subs(func, values)) print('expr_subs', '=', expr_subs(expr_str, symbols, values))
Ausgabebeispiel:
expr_str p36*q67*p57*p26*p25*p13*q12*q15 + + p36*q67*p47*p26*p24*p13*q12 + + p67*q57*p26*p25*q12*p15 + + q57*p47*p25*p24*q12*p15 + + p57*p25*p12*q15 + + p36*p67*p13 + + p67*p26*p12 + + p47*p24*p12 + + p57*p15 - - p57*p47*p24*p12*p15 - - p67*p47*p26*p24*p12 - - p67*p57*p26*p12*p15 + + p67*p57*p47*p26*p24*p12*p15 - - p36*p67*p26*p13*p12 - - p36*p67*p47*p24*p13*p12 - - p36*p67*p57*p13*p15 + + p36*p67*p57*p47*p24*p13*p12*p15 + + p36*p67*p47*p26*p24*p13*p12 + + p36*p67*p57*p26*p13*p12*p15 - - p36*p67*p57*p47*p26*p24*p13*p12*p15 - - p36*p67*p57*p25*p13*p12*q15 - - p67*p57*p26*p25*p12*q15 - - p57*p47*p25*p24*p12*q15 + + p67*p57*p47*p26*p25*p24*p12*q15 + + p36*p67*p57*p26*p25*p13*p12*q15 + + p36*p67*p57*p47*p25*p24*p13*p12*q15 - - p36*p67*p57*p47*p26*p25*p24*p13*p12*q15 - - p36*p67*q57*p47*q26*p25*p24*p13*q12*p15 - - p67*q57*p47*p26*p25*p24*q12*p15 - - p36*p67*q57*p26*p25*p13*q12*p15 - - p36*q67*q57*p47*p26*p25*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p24*p13*q12*p15 - - p36*q67*p57*p47*p26*p25*p24*p13*q12*q15 AutoSymbols (p12, p13, p15, p24, p25, p26, p36, p47, p57, p67, q12, q15, q26, q57, q67) func = <function <lambda> at 0x06129C00> func_subs = (-0.391482058715, 0.812813114469, 2.409570627378) expr_subs = (-0.391482058715, 0.812813114469, 2.409570627378) [Finished in 1.5s]
Das Tutorial ist vorbei. Ich hoffe, Sie finden hier etwas Nützliches!
PS: Das Hauptmerkmal des beschriebenen Ansatzes ist die Fähigkeit, über die Standardtypen von Variablen und Operationen für Python und Sympy hinauszugehen. Indem Sie Ihre Klasse deklarieren und die "magischen" Methoden überladen, können Sie bisher unbekannte mathematische Ausdrücke mit Sympy berechnen (Lambda-Funktionen erstellen, die sowohl Standard- als auch Benutzertypen und -operationen akzeptieren).
Vielen Dank für Ihre Aufmerksamkeit!