Nu har Kevin svaret siden jeg begyndte på mit længere svar, men siden jeg nu har brugt tid på det:
Her er mit bud på en løsning som består af to dele.
Den (relativt) nemme del:
1. Parse dit udtryk til et AST (Abstract Syntax Tree). Dette er et træ som beskriver din ligning. Det er ikke nok bare at læse dit udtryk som en liste bestående af TAL/VARIABEL efterfulgt af en OPERATOR efterfulgt af en TAL/VARIABEL igen. Ellers kan du risikere at parse 5 + 3 * 4 som (5 + 3) * 4, hvilket jo er forkert. En liste kan ikke fortælle dig at * binder stærkere end +, men det kan en træstruktur.
Og den svære del. Hvor svær denne del er afhænger af hvor komplekse dine udtryk er. Hvis du holder dig til en enkelt ukendte variabel, som kun er et sted, og holder dig til simpel matematik, herunder, eventuelt brug af avancerede matematiske funktioner som log, potens, rødder bør det være praktisk muligt. Hvis du går udover dette, med f.eks. integration, differentialligninger, eller flere ukendte, eller bare en enkelt ukendt flere steder i udtrykket, kan det blive meget mere komplekst. Husk det er heller ikke alle ligninger, man kender en analytisk løsning til endnu (eller måske nogensinde). Hvis du kan finde et andet bibliotek der kan omskrive ligningen for at isolere variablen for dig (måske et skrevet i C/C++ du kan wrappe som et PHP module), vil jeg stærkt anbefale dig dette.
2. Brug "rewrite-rules" (omskrivningsregler) til at omskrive dit udtryk med henblik på at isolere den ukendte variabel på den ene side af lighedstegnet. Kan eventuelt simplificere andre udtryk undervejs. F.eks i stedet for at løse for Y: X = (2+3) * Y ==> Y = X / (2+3), laver du først ==> X = 5 * Y og så ==> Y = X / 5.
For at parse udtrykket i PHP anbefaler jeg dig enten en såkaldt
recursive descent parser, eller at du bruger en parser generator som f.eks.:
https://github.com/hafriedlander/php-peg , hvor du beskriver hvordan dine udtryk er formet og så kan den læse noget tekst og give dig et AST som output.
Et AST er en træstruktur bestående af knuder. Hver knude indikerer et matematisk udtryk som kan bestå af underudtryk. Lad os tage et eksempel, hvor vi gerne vil løse for A: Z = (A + 4)^2 * Y
Dette bliver parset til et AST der ligner:
Billede 1For at simplificere sådan et AST evaluerer vi udtrykket fra bunden af og op. Lad og sige at A var kendt og vi ønskede at finde Z. Så ville vi evaluere højre side af lighedstegnet. Dette er en produktknude hvilket betyder vi skal gange dens to underudtryk. Men de skal måske evalueres først. Lad os sige vi valgte højre side af multiplikationen først, nemlig Y. Hvad er Y for en knude? Det er en "variabel" knude, (og siden vi antager at alle variabler er kendte undtagen lige 'Z'), så slår vi bare op i en tabel og erstatter den med 7, fordi vi siger lige at Y = 7. Nu har vi løst højre side af multiplikationen, men mangler stadig venstre side. Det er en Pow(2), dvs. opløftet i 2. Den kan vi simplificere ved at erstatte den med en multiplikationen af dens underudtryk 2 gange. Nu står vi med:
Billede 2Vi evaluerer den højre side. Den er en "tal-knude", så den evaluerer bare til sin egen værdi. Så tager vi venstre side, det er en "variabel" knude, som vi slår op. Lad os sige at A = 8. Nu har vi reduceret begge underudtryk til vores plus-knude, så vi lægger bare tallene sammen og den får værdien 12. Så løser vi den anden plus-knude under den nederste multiplikation. Den giver selvfølgelig også 12. Nu kender vi begge underudtryk til multiplikations-knuden, nemlig 12, og 12 og vi ganger dem og det giver 144. Nu kender vi begge underudtryk til den øverste multiplikations-knude som er 144 og 7. Dem ganger vi og får 1008. Nu ved vi at Z = 1008. Dette er det generelle princip for hvordan en interpreter virker.
Lad os vendte tilbage til problemet hvor vi vil løse A og kender de andre. Her kan vi ikke bruge samme trick, fordi vi har ikke isoleret A. For at gøre det skal vi bruge lignende, men mere komplekse omskrivnings- og reduceringsregler.
Lad os starte i toppen igen. Vi har måske en omskrivningsregel der siger at for en multiplikationsknude, hvor den ukendte (A), forekommer i det ene underudtryk men ikke i det andet, så dividerer vi begge sider af lighedstegnet med det andet. Det betyder vi fjerner multiplikations-udtrykket for højreside, og tilføjet et divisions-udtryk på venstre side:
Billede 3For at reducere Pow(2), tager vi kvadrat roden af begge sider:
Billede 4Det modsatte af plus er minus, så vi trækker 4 fra begge sider:
Billede 5Nu har du isoleret A. For at finde svaret evaluerer du den anden side af lighedstegnet med metoden jeg beskrev lige før. Jo flere omskrivningsregler du kan lave desto flere ligninger kan dit system løse.
Indlæg senest redigeret d. 26.02.2015 17:22 af Bruger #14645