Autor Zpráva
janbarasek
Profil
Ahoj,
představme si, že mám nějaký řetězec, ve kterém je umístěn nějaký matematický příklad.

Pokud se jedná o výraz, tak lze často praktikovat něco jako:
<?php
$q = "5+3*2";
eval("$r = (".$q.");");
echo $r; // vypíše 11
?>

Ale jak řešit rovnice? Mějme následující předpis:
<?php
$q = "x+3=2x-2";
$parser = explode("=", $q); // napadlo mě to rozparsovat
/*
   Tady s tou částí potřebuji poradit
*/
echo $r; // vypíše 5
?>

V tomto případě by to šlo řešit jednoduchým přehazováním členů, hledám ale nějaké univerzálnější řešení, co bude fungovat ve více případech (například, když budu chtít použít v rovnici nějakou funkci - třeba logaritmus), tak se musí provést ještě nějaké úpravy.

Existuje na toto nějaké hotové řešení (stačí jen teoreticky). Napadlo mě to rozložit na strom a pak to nějak řešit (to fungovalo u výrazů), nicméně se bojím toho, že stromy jsou pro rovnice příliš slabé.
_es
Profil
janbarasek:
například, když budu chtít použít v rovnici nějakou funkci - třeba logaritmus
Napríklad budeš chcieť použiť viac premenných, pracovať s komplexnými číslami, deriváciami, integrálmi, premennou môže byť aj funkcia...

Existuje na toto nějaké hotové řešení (stačí jen teoreticky).
Poznám iba „prakticky“: www.wolframalpha.com/input/?i=x%2B3%3D2x-2
quatzael
Profil
janbarasek:
v PHP na to najdeš asi těžko nějaké funkce. Musíš každou rovnici řešit algebraicky. V případě implicitně zadaných funkcí to budeš muset řešit iteracema:o) Záleží co vše uživateli dovolíš. Jestli mu necháš volnej input ať si tam napíše co chce, tak potom budeš muset nejdřív si naprogramovat nějakou vlastní funkci, kterou analyzuješ, o jaký typ rovnice se jedná. A podle toho jí jedním postupem vyřešíš (nebo nevyřešíš)..
janbarasek
Profil
quatzael:
Záleží co vše uživateli dovolíš.
Pokud to bude technicky možné, tak bych se nerad omezoval. Je mi jasné, že něco podobného není vůbec jednoduché, takže budu možnosti spíše postupně rozšiřovat.

nejdřív si naprogramovat nějakou vlastní funkci, kterou analyzuješ, o jaký typ rovnice se jedná.
Máš nějaký nápad, jak by se to dalo jednoduše udělat? Co si myslíš o mé představě?
1. vytvořím seznam podporovaných typů rovnic
2. pro každý typ naprogramuji algoritmus, co jej bude umět vyřešit
3. při zadání vstupu zjistím, o jaký typ jde (podle syntaxe, vůbec nebudu řešit obsah)
4. zavolá se modul s tím konkrétním algoritmem, provede se výpočet a uživateli se zobrazí výsledky

Jak mám řešit zejména 3. bod tak, aby byl algoritmus co možná nejrychlejší? Mohl bych udělat víc parserů a projíždět jeden po druhém, než najdu shodné vlastnosti se vzorem, ale to se mi nezdá jako nejlepší řešení.

A podle toho jí jedním postupem vyřešíš (nebo nevyřešíš)
Máš nějaký tip, jak detekovat správnost výpočtu? Mohl bych sice udělat zkoušku, ale někdy může dojít v důsledku dělení k nepřesnostem a pak může být ve zkoušce odchylka. Mám toleranci nastavit na nějakou konstantu, nebo třeba dát povolenou odchylku +/- 1%?

_es:
Poznám iba ‚prakticky‘: www.wolframalpha.com/input/?i=x%2B3%3D2x-2
To bych bez tebe vážně neznal. :D
_es
Profil
janbarasek:
Máš nějaký nápad, jak by se to dalo jednoduše udělat?
Obávam sa, že to pôjde len „zložito“. Vlastne chceš niečo ako vo Wolfram Alpha no s oveľa menšími znalosťami, s oveľa menšou prácou, s oveľa menším množstvom ľudí... - teda to asi len tak „jednoducho“ nepôjde. Ak chceš dať riešiť nejakým spôsobom na webe matematické rovnice, tak jednoduchší spôsob bude vytvoriť formuláre na zadanie koeficientov nejakého konkrétneho typu rovníc a potom výstup ich riešení.
juriad
Profil
janbarasek:
Můžeš si vstup rozložit na rovnost dvou tokenových stromů. A provádět nad nimi ekvivalentní operace - převádění na druhou stranu, násobení, dělení. Jakmile dojdeš do stavu, že rovnici neumíš víc upravit, tak na ní poštveš řešič podle kritérií:
- nulární (bez proměnné: konstanta = 0, tautologie či kontradikce)
- lineární (jediná proměnná, jediný výskyt, v první mocnině)
- kvadratická (jediná proměnná, jen nultá, první a druhá mocnina)
- logaritmická (pokud je tvaru log jediné proměnné = kostanta)
- ...

Musíš dát pozor na uspořádání výrazů. Každá operace, kterou provedeš musí ohodnocení snížit (snížit počet termů, snížit stupeň proměnné, snížit počet operátorů, ...). V opačném případě by ses mohl zacyklit.

K přesnosti výpočtů: můžeš vlastní výpočet oddálit tím, že každou konstantu budeš reprezentovat výpočetním stomem a až v okamžiku nalezení řešení na ten strom poštveš další potvoru, která jej ekvivalentně přeskládá tak, že se bude minimalizovat chyba.
janbarasek
Profil
juriad:
Díky, tvůj postup je zajímavý. Zkusil jsem něco splácat a pak jsem zjistil, že se na rovnici lze dívat také jako na rovnost dvou funkcí. Pro lineární rovnice dobře funguje tento kód:
function equation_solve($left, $right) {
    $eq = $left . '-(' . $right . ')';
    if (substr_count($eq, '(') != substr_count($eq, ')')) { return('Všechny závorky nejsou uzavřené.'); }
    if (preg_match('/\([^)]*\(/', $eq)) { return('Je povolena pouze jedna úroveň zanoření závorek'); }
    if (preg_match('/[^x0-9+*\/().=-]/', $eq)) { return('V současné době můžete použít pouze znaky: x, 0-9, +, '. '*, /, (, ).'); }
    if (preg_match('/[^x0-9\)][-+\/*]/', $eq) || preg_match('/[-+\/*][^x0-9\(]/', $eq)) { return('Byla zadána příliš složitá výpočetní operace, na kterou nemáme algoritmus. :('); }

    $eq = str_replace('x', '$x', $eq);
    $y0 = $y1 = 0;
    $x = 0; eval('$y0 = ' . $eq . ';');
    $x = 1; eval('$y1 = ' . $eq . ';');
    $slope = $y1 - $y0;
    $intersect = $y0;
    $result = (- ($intersect / $slope));
    if (substr_count($result, '.') > 0) { return 'A|'.$intersect.'/'.$slope.'|'.$result; } else { return 'B|'.$result; }
}

Funkce má 2 parametry: Levou a pravou část rovnice. Například pro vstup: "x/2", "4*3-x+1" vrací správný výsledek formou zlomku (který je potřeba dále upravit, ale to už není složité). Pokud má být výsledek celé číslo, tak se nevrací zlomek, ale právě to číslo. Výstup funkce je nutné ještě rozparsovat podle znaku "|", který odděluje různé výstupní hodnoty.
Jan Tvrdík
Profil
janbarasek:
Pokud to chceš řešit alespoň trochu seriozně, tak se stejně nevyhneš asi tomu, co navrhuje juriad. Tedy překladu rovnice na abstraktní syntaktický strom pomocí lexikální a syntaktické analýzy.

Pokud si chceš usnadnit práci, tak parser nemusíš psát, ale můžeš použít nějaký generátor parseru na základě gramatiky. Např. PEG pro jednoduchou lineární rovnici by mohla vypadat nějak takto:

equation
  = expression "=" expression

expression 
  = component ([+-] component)*

component
  = factor ([*/] factor)*

factor
  = number "x"?
  / "x"
  / "(" expression ")"

number
  = [+-]? [0-9]+ ("." [0-9]+)?

(Jde si s tím hrát i online.)
quatzael
Profil
janbarasek:
Máš nějaký tip, jak detekovat správnost výpočtu? Mohl bych sice udělat zkoušku, ale někdy může dojít v důsledku dělení k nepřesnostem a pak může být ve zkoušce odchylka. Mám toleranci nastavit na nějakou konstantu, nebo třeba dát povolenou odchylku +/- 1%?
Co chceš proboha detekovat? Když chceš naprogramovat nějakou kalkulačku, tak prostě budeš muset rozumět matematice. Když tam naprogramuješ správný postup tak Ti to bude házet správný výsledky a žádnou zkoušku dělat nemusíš.

Žádný "nepřesnosti" tam nevymýšlej pokud je výsledek pátá odmocnina ze sedmi krát 9/7, tak to tak vyjádři i v outputu. To, že to budeš uživateli zaokrouhlovat na desetinný čísla, mu je naprosto k ničemu. Akorát to je důvod, proč Tvůj kalkulátor nepoužívat a poohlídnout se po něčem jiném. Co to je nápad to zaokrouhlovat.. Můžeš mu tam samozřejmě pro informaci vedle toho přesnýho výsledku tohle uvést jako info navíc, ale pokud by to tam mělo být samotný, tak je celej ten Tvůj výtvor v podstatě k ničemu..
Fisir
Profil
Reaguji na quatzaela:
Žádný nešťastník, který kdy vyráběl nějakou kalkulačku (v jakémkoli programovacím jazyce) si tam ty nepřesnosti nevymýšlí. Počítače jednoduše neumějí počítat s našimi čísly, respektive je neumějí správně uložit ve dvojkové soustavě a tak vznikají nepatrné odchylky, které ovšem mohou díky dalším matematickým operacím nabýt velkých rozměrů. => Blábol.
quatzael
Profil
Fisir:
Vždyť píšu, že tam nemá nic vymýšlet s desetinnýma číslama, kromě těch co zadá přímo uživatel. Neříkej mi, že počítače neumí správně sčítat, odčítat a násobit integer čísla.. Na dělení má použít fmod. Pak nebude mít žádnej problém..
Wolfram je normální počítačový program a počítá podle mě správně. To, že MS Excel dělá tyhle chyby je evergreen od Microsoftu, kterej bez chyb snad ještě nic nevydal..
_es
Profil
quatzael:
Na dělení má použít fmod. Pak nebude mít žádnej problém..
K čomu mu bude funkcia fmod? Ak je napríklad výsledok presne v tvare 2/7*(3^(5/11)), tak v takom nejakom tvare asi budú musieť byť aj medzivýsledky pred vyrátaním výsledku/výsledkov.
janbarasek
Profil
quatzael:
Díky za hrozně motivující příspěvek - vážně! Ukázal mi, že se na to můžu rovnou vy***t a udělám nejlíp. :)

A teď vážně, pracovat s čísly přesně je docela dost velký problém a mám pro to hromadu argumentů, které nemůžeš popřít. Částečně se to snažím řešit tak, že čísla ukládám v desítkové soustavě jako string a pak s tím počítám (protože to PHP umí) a tím dochází k menší chybovosti - ale i tak pomocí drobného zaokrouhlování dochází (na úrovni milionů desetinných míst), takže dělení ukládám spíše jako zlomek a přednostně se snažím vypsat právě to.

Pokusil jsem se nějak sestavit algoritmus (který se mi nechce úplně zveřejňovat), co umí pracovat s příkladem formou zlomků a tak jsem docílil relativně přesného počítání. Podívejte se na ukázku. Bohužel můj algoritmus neumí vyřešit všechny typy rovnic, takže ty složitější řeší graficky.

Budu rád, když se vyjádříte ke kvalitě poskytovaných výsledků a případně konstruktivně poradíte, jak to zlepšit (rady typu "celé to překopej" beru jako urážku).
_es
Profil
janbarasek:
Částečně se to snažím řešit tak, že čísla ukládám v desítkové soustavě jako string a pak s tím počítám (protože to PHP umí) a tím dochází k menší chybovosti“. Ani v desiatkovej sústave predsa neuložíš všetky ani racionálne čísla. Ak máš nejaký vlastný formát čísla v tvare zlomku, tak ten „špeciálny desiatkový“ asi bude nadbytočný.

Bohužel můj algoritmus neumí vyřešit všechny typy rovnic, takže ty složitější řeší graficky.
To máš ešte čo robiť: www.wolframalpha.com/input/?i=x^2%3Dabs%283x%29, pritom ide len o trochu skomplikované riešenie obyčajných kvadratických rovníc. Tvoj „stroj“ nedokáže zrátať ani obyčajnú kubickú rovnicu, na ktorú existujú jednoduché vzorce: mathematicator.cz/search.php?q=x^3+%2B+5x^2+%2B+6x+%2B+1000+%3D+0 „Grafické riešenie“ je akosi úplne mimo vhodného zobrazenia časti roviny xy. Pre porovnanie: www.wolframalpha.com/input/?i=x^3+%2B+5x^2+%2B+6x+%2B+1000+%3D+0.
Ak si myslíš, že to, čo máš, stačí len trošku „podopĺňať“ a máš už skoro to, čo WolframAlpha, tak sa mýliš.

Tak som to ešte aj precenil, očakával som aspoň bezproblémové vyrátanie kvadratickej rovnice, no: mathematicator.cz/search.php?q=5x^2+%2B+6x+-+1000+%3D+0 . pre porovnanie www.wolframalpha.com/input/?i=5x^2+%2B+6x+-+1000+%3D+0.

Prečo by mal niekto používať tvoj pokus o napodobenie WolframAlpha a nie originál - akú to má pre návštevníka „pridanú hodnotu“?
Moderátor Chamurappi: Nežádal o názor na stránku, ptá se, jak vyřešit konkrétní druh problému. Příspěvky typu „neřeš to“ jsou skutečně nežádoucí a budou mazány.
Jan Tvrdík
Profil
janbarasek:
případně konstruktivně poradíte, jak to zlepšit
Pořád jsem toho názoru, že dokud s tou rovnicí nebudeš pracovat jako se stromem, tak se nepohneš z místa. Momentálně nejsi schopný řešit ani lineární rovnice jako x = 7 + 0*(x^2) nebo x=sqrt(9), protože vůbec nepracuješ s obecnými výrazy.

Pro začátek koukni na Timyho tutoriál se základy OOP, kde ukazuje, jak lze s výrazy pracovat objektově.
Fisir
Profil
Reaguji na quatzaela:
Tak zkusíme něco víc cool, jo?

Pokud si ten příklad spočítáš na Windowsovské kalkulačce, vyjde ti 0,9. Ta to evidentně nějak kompenzuje.

nemá nic vymýšlet s desetinnýma číslama, kromě těch co zadá přímo uživatel
A co bys jako chtěl dělat s tím příkladem na screenu, když bys nemohl používat mezivýsledky? Kouknu a vidím?

počítače neumí správně sčítat, odčítat a násobit integer čísla
Ani to cool anglické slovíčko to nezachrání. Počítače umějí počítat, ale ve dvojkové soustavě. My jim předhazujeme čísla v desítkové soustavě, a počítače je musejí nějak převést do dvojkové, aby s nimi mohly pracovat. Ne každé číslo ale lze přesně převést, proto vznikají ty odchylky. Je to popsané v tom článku, místo Excelu si představ Pythoní skript.
_es
Profil
Fisir:
„počítače neumí správně sčítat, odčítat a násobit integer čísla“(quatzael)
Ani to cool anglické slovíčko to nezachrání. Počítače umějí počítat, ale ve dvojkové soustavě. My jim předhazujeme čísla v desítkové soustavě,
Nejako si citáciu usekol tak, aby to vyzeralo, že tvrdí niečo iné, chýba tam „Neříkej mi, že počítače neumí správně sčítat, ...“ Pre celé čísla je predsa jedno, v akej číselnej sústave sú uložené, nie je v tom rozdiel. Jediné riziko je, ak sa používa nejaký formát s pohyblivou čiarkou a „pretečie“ sa do príliš veľkých hodnôt, kedy je číslo síce uložené no už nepresne. Okrem toho, formát ukladania čísel aj operácie s nimi si môže každý vymyslieť aj vlastné, nemusí byť viazaný na tie, čo mu systém alebo programovací jazyk umožňuje.

Pokud si ten příklad spočítáš na Windowsovské kalkulačce, vyjde ti 0,9. Ta to evidentně nějak kompenzuje.
Jediná „kompenzácia“ je taká, že je výsledok vhodne zaokrúhlený, to sa tu často radilo aj v iných vláknach na vyriešenie problému.
quatzael
Profil
Fisir:
Tak zkusíme něco víc cool, jo?
Ty jsi nečetl co jsem tady psal? Psal jsem, že má počítat s výrazy, které obsahují celý čísla a ty potom upravovat. Takže (43.1-43.2)+1 si jednoduše převede (431-432) desetin, přičte 10 desetin a napíše výsledek 9 desetin. Tzn. "9/10" případně pro info převede na 0.9. Ještě před tím samozřejmě zkontroluje, jestli se ten zlomek nedá nějak zkrátit..
Když tam bude mít nějaký iracionální a transcendentní čísla tak s nima bude zacházet jako s konstantama (jabkama, hruškama) a nebude tam nic vyčíslovat numericky. To dá přece rozum..

janbarasek:
Díky za hrozně motivující příspěvek - vážně! Ukázal mi, že se na to můžu rovnou vy***t a udělám nejlíp. :)
Tak se snaž udělat něco, co má nějakou přidanou hodnotu. Jelikož podobných kalkulaček je na netu celkem dost, tak Tvojí největší přidanou hodnotou bude asi to, že to bude česky.. Každopádně můžeš naprogramovat kalkulačku na řešení některých složitějších typů diferenciálních rovnic. To myslím online nikde není (aspoň jsem to nikde nenašel).. Je to akorát offline na Wolframu.

Pro inspiraci, tady jsou některý online kalkulátory, se kterými se dobře pracuje:
symbolab.com
integral-calculator.com


U toho druhýho máš dokonce vysvětlený jakým způsobem to provádí výpočty. Takže máš prozrazený celý tajemství a můžeš to bez problému implementovat:o)
Fisir
Profil
Uznávám, stěr.
Virtus
Profil
Zdravím,
počítat rovnice pomocí PHP mi připadá hloupé, časem když budeš přidávat další a další možnosti, narazíš na věci který v PHP rozumně nevyřešíš (př.: jedna z mnoha neintegrovatelných funkcí: f(x) = sin(x^2)/x ), nebo ti to zabere spoustu času (málo muziky za hodně peněz), osobně bych se vydal spíš cestou, že PHP bude jenom rozhraní mezi nějakým pokročilejším programe pro výpočty, na linuxu to může být třeba program Octave s doinstalovaným Octave-forge.
janbarasek
Profil
Virtus:
Vidím to tak, že opráším znalosti ze střední a jádro udělám v C++, které už postupně zapomínám (ale uznávám, že se mi bude ještě hodně hodit).

PHP mám rád z toho důvodu, že u mě vítězí pohodlí. Právě PHP mi dává velké možnosti, co se týče práce s řetězci a daty obecně - protože moc neřeší datové typy a je poměrně tolerantní vůči "prasárnám" (což uznávám, že není nic dobrého - ale hodí se to). U PHP vidím jediný problém v přesnosti počtů u velkých čísel - V C++ se mi obecně pracuje s velkým integerem mnohem lépe, než v PHP.

Fisir:
Z toho si nic nedělej, mě tu stírají každou chvíli. Na druhou stranu by člověk měl umět přiznat chybu a netrvat vždy na své pravdě. Já osobně jsem rád i za negativní kritiku (když je dobře napsaná), protože mi ukáže, jak se zlepšit. Je ale fakt, že když tě někdo stírá často, tak to taky není dobré.

Pokud si ten příklad spočítáš na Windowsovské kalkulačce, vyjde ti 0,9. Ta to evidentně nějak kompenzuje.
Promiň, ne každý tady má Windows. Já osobně používám Linux, kde CMD fakt není. :D
Jan Tvrdík
Profil
janbarasek:
V C++ se mi obecně pracuje s velkým integerem mnohem lépe, než v PHP
Tak si nainstaluj PHP 5.6, Nikita Popov dodělal přetížení operátorů pro GMP.
Joker
Profil
janbarasek:
Promiň, ne každý tady má Windows. Já osobně používám Linux, kde CMD fakt není. :D
Já v Linuxu kalkulačku mám stejně jako calc, protože se tak jmenuje „Excel“ v Open/Libre Office :-) a ten výpočet tam taky vrátí 0,9.
Což není žádná věda, když omezím přesnost na určitý počet desetinných míst, stačí to číslo jen zaokrouhlit. Programovací jazyky vesměs nezaokrouhlují, od toho tam je programátor, aby s tím číslem pak pracoval.

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0