Equation Solver - $solve

By MoNoXiDe on Dec 19, 2007

Snippet to find multiple solutions to any equations

Usage:

*$solve()

Things to Note:
That will return what the solution is.
Only one unknown can be in the equation.
Currently the unknown has to be x
If there is no "=" in the equation it will assume the equation equals 0.
Functions supported: sin,asin,cos,acos,tan,atan,log,sqrt
You can input equations as you would write them, e.g. 2sin(2x) = 1
It will return a string with solutions separated by commas.
returns $false if it cannot solve even 1 solution.
*put a bracket around the expression when using a function. Such as sin(x) or sqrt(x). This will not be required in the next update.

Examples:
$solve(2x+4=-20) will return -12
$solve(10x^2 - 17x=-3) will return 1.5,0.2
$solve(x^3/6 + x^2/2 + x/3) will return 0,-1,-2
$solve(2sin(2x) = 1) will return 15
*$solve((0.01x^8-0.4x^6+10x^3)-20x=1) will return 5.962365,2.874398,1.549226,-0.050063,-1.329

/*
Equation Solver v2.5
By George

Massive thanks to TropNul who helped me considerably (basically redid all of the regsubex expressions :)
Also to:
*myggan,jaytea for $regsubex help :)

Usage:

*$solve(<equation>)

Things to Note:
*That will return what the solution is.
*Only one unknown can be in the equation.
*Currently the unknown has to be x
*If there is no "=" in the equation it will assume the equation equals 0.
*Functions supported: sin,asin,cos,acos,tan,atan,log,sqrt
*You can input equations as you would write them, e.g. 2sin(2x) = 1
*It will return a string with solutions separated by commas.
*returns $false if it cannot solve even 1 solution.

Examples:
*$solve(2x+4=-20) will return -12
*$solve(10x^2 - 17x=-3) will return 1.5,0.2
*$solve(x^3/6 + x^2/2 + x/3) will return 0,-1,-2
*$solve(logx/log10=2) will return 100
*$solve(2sin(2x) = 1) will return 15
*$solve((0.01x^8-0.4x^6+10x^3)-20x=1) will return 5.962365,2.874398,1.549226,-0.050063,-1.329

Update History
*v2.5
**Major redo of parser. Much more flexibility in input of equation.
*v2.0.1
**Fixed a rather major bug that disabled the multiple solution ability
*v2.0 
**Added new functions (viewable above)
**Greatly improved the equation transformer on input that was necessary for extra functions

Enjoy :)

Thanks
.
.
.
How this code works:
I used the newton raphson method to solve for equations. This is x = x - (f(x) / f'(x)) where f(x) is a function of x and f'(x) is the first derivative function of x.
If you iterate that 30 times on the function of x it will be solved. The format required is f(x) = 0.
I have used numerical differentiation to calculate the first derivative which is done by (f(x+h) - f(x-h)) / 2h. I have used 1 for h.
The multiple solution algorithm works like this:
*Find one solution using the newton raphson method
*Construct a new equation and solve again using the newton raphson method to home in on the new solution
*The new equation is formed like this f(x) = f(x) / (x - ?st ans>)
*if you have more than one solution you eventually get f(x) = (f(x) / (x - ?st ans>)) / (x - 2nd ans>))
*/

alias solve {
  %eq = $trans($1)
  return $multiple(%eq,$xsolve(%eq))
}

alias trans {
  ;this makes the input more mIRC compatible, so adds a * between brackets )( --> )*( and 4x --> 4*x etc
  %eq = $remove($1,$chr(32))
  if (= isin $1) %eq = $gettok(%eq,1,61) $+ -( $+ $gettok($1,2,61) $+ )
  ;this makes the equation equal 0. eg 1=2sin(x) --> 1 - (2sin(x))
  %eq = $regsubex(%eq,/(sin|asin|acos|atan|cos|tan|log|sqrt)(x+)/g,\1 $+ $regsubex(s,\2,/(x)/g,$+($chr(40),x,$chr(41))))
  ;Thanks to TropNul for this wonderful line :) makes functionx --> function(x), sinx --> sin(x).
  %eq = $regsubex(%eq,/(\x29|\d)\x28/g,\t* $+ $chr(40))
  %eq = $regsubex(a,%eq,/(\d)([a-z])/g,\1*\2) 
  ;TropNul also :)
  return %eq
}

alias multiple {
  var %x = $2
  ;$2 is a solution as it was solved before being inputted in the solve alias
  var %eq = $1
  var %trig = $iif($regex(%eq,asin|acos|atan|sin|cos|tan),$true,$false)
  ;disables multiple solutions if trig function as they have infinite solutions and would freeze the code.
  var %solutions = $iif($abs($xcalculate(%eq,%x)) < 0.001,$+($chr(44),$2,$chr(44)),$false)
  ;checks if the inputted solution works, if not the equation is not solvable (by the solver).
  if (%trig) return $iif(%solutions,$right($left(%solutions,-1),-1),$false)
  if (!%solutions) return $false
  while ($abs($xcalculate(%eq,%x)) < 0.001) {
    ;while the new solutions work, the while loop continues.
    %eq = $+($chr(40),%eq,$chr(41),/,$chr(40),x,-,%x,$chr(41))
    ;formulation of new equation: f(x) = f(x) / (x - ?st ans>)
    %x = $xsolve(%eq)
    ;solving the new equation
    if ($redundant(%solutions,%x)) goto finish
    ;due to mIRCs limitations (only 6dp) sometimes you get the same answer twice, so $redundant checks for any solutions that are the same.
    if (($abs($xcalculate(%eq,%x)) < 0.001)) %solutions = %solutions $+ %x $+ ,
    ;if solution works, add to the solutions var list
  }
  :finish
  return $right($left(%solutions,-1),-1)
}

alias xsolve {
  ;this is the actual newton raphson solving algorithm
  var %x = 20
  ;you can start on any value, I chose 20, for absolutely no reason.
  var %a = 0
  while (%a <= 30) {
    %x = $calc(%x - $+($chr(40),$xcalculate($1,%x),/,$derivative($1,%x),$chr(41)))
    ;x = x - (f(x) / f'(x))
    inc %a  
  }
  return $xcheck($1,%x)
  ;xcheck cleans up the solution. Sometimes due to the mIRC limitations you get answers such as 1.99987 when the exact answer is infact just 2. So it trys to iron out these small differences and be more accurate.
}

alias derivative {
  ;(f(x+h) - f(x-h)) / 2h
  var %num2 = $xcalculate($1,$2 + 1)
  var %num3 = $xcalculate($1,$2 - 1)
  return $calc((%num2 - %num3) / 2)
}

alias xcheck {
  if ($abs($xcalculate($1,$2)) < 0.001) {
    if (!$xcalculate($1,$round($2,0))) return $round($2,0) 
    if (!$xcalculate($1,$floor($2))) return $floor($2) 
    if (!$xcalculate($1,$ceil($2))) return $ceil($2) 
    var %y = 5
    while ((%y >= 0) && (!$xcalculate($1,$round($2,%y)))) dec %y
    return $round($2,$calc(%y +1))
  }
  else return $false
}

alias xcalculate return $calc($parser($replace($1,x,$calc($2))))
;this feeds a value into the equation and gives the output. eg if the equation was 20x - 6, $xcalculate(20x - 6, 0.5) would return 4.

alias parser return $regsubex($1-,/((sin|asin|acos|atan|cos|tan|log|sqrt)\050(-?\d+(?:\.\d+)?(?:[\*\/\+-]\d+(?:\.\d+)?)*)\051)/g,$($\2($calc(\3)) $+ .deg,2))
;the parser works out functions such as sqrt and sin(3x) etc as these aren't supported in the $calc identifier. Input sin(3*x) - 3 with x = 2 and you would get 0.104528 - 3 which $calc can do.
;Courtesy of TropNul (again!)

alias redundant {
  var %temp = $chr(44)
  var %a = 1
  while (%a <= $gettok($1,0,44)) { 
    %temp = $+(%temp,$round($gettok($1,%a,44),1),$chr(44)) 
    inc %a
  }
  if ($+($chr(44),$round($2,1),$chr(44)) isin %temp) return $true
  else return $false
}

Comments

Sign in to comment.
Jonesy44   -  Dec 16, 2008

haha, ok no worries. nice work x]

 Respond  
MoNoXiDe   -  Dec 16, 2008

THanks for your comments, I've already done this in csharp. Simultaenous equations are very very complicated in computing and I believe beyond the capacity of mIRC scripting language. I have a multiple solution simultaneous equation solver if you're interested. This script also does quadratics and gives all solutions to them. Factorisation is possible but unfortunately don't have time to implement them into mIRC though have done all these things in csharp. You're welcome to edit the script how you wish though :)

 Respond  
Jonesy44   -  Dec 16, 2008

Great script! i will test it later. looks really impressive & since this is basically all we do in math i can understand it! lol. great stuff, can't wait to try it out.

Some ideas for more scripts, or integrating them;

similtaniouos equations
quadratic eq.
inequalities
factorisation eq.

etcetc, just some ideas if you fancy doin some more math :L

 Respond  
MoNoXiDe   -  Dec 16, 2008

I never got round to finishing that bit of the parser where if in the input equation you have a bracket ) followed by a letter, it is replaced by ). So that )x --> )x

Any help anyone? Then I can finally declare this snippet finished.

 Respond  
TropNul   -  Dec 26, 2007

I have no particular reason of learning Perl, except for its \'Magnificient\' implementation of regular expressions lol.
Apart from that, I think it\'s a powerful language worth knowing.

After knowing some Perl, maybe I\'ll start with TCL and later with Python. I know some C already and that helps a lot.

Merry Christmas also and a Happy New Year.

PS: For the $trans issue, trying to do it yourself is the first step to start re-learning regexps. Good luck !
PS: In fact, no regexps are \'simple\' before you understand what you\'re writing. For example in this pattern /(.)/, there\'s
everything or \'maybe not\'. Now, try to find out why I said \'maybe not\'. ;)

 Respond  
MoNoXiDe   -  Dec 25, 2007

Ahh right the backslashes :)
Thanks mate, all fixed. All done.
I\'ve updated the code one last time again :P

Thanks a bunch for all your help, good luck with perl. I moved from mIRC to C# because I never used mIRC for chatting or whatever, just for scripting. What made you choose to learn perl out of all the languages out there?

Also cheers for the links, I\'ll check them out. There\'s one more thing I would like to add to the $trans code which is to do ) --> )* but I\'m going to try to do that myself following your links ;) It\'s a really simple one I know :P

Merry Christmas

 Respond  
TropNul   -  Dec 23, 2007

Fixed. (I hope lol) :)

alias parser {
  return $regsubex($1-,/((sin|asin|acos|atan|cos|tan|log|sqrt)\\\\050(-?\\\\d+(?:\\\\.\\\\d+)?(?:[\\\\*\\\\/\\\\+-]\\\\d+(?:\\\\.\\\\d+)?)*)\\\\051)/g,$($\\\\2($calc(\\\\3)) $+ .deg,2))
}

NB:
When you put this fix in your source, remember to include the \\ as they are!
In your last update, I see the parser function as follows:

alias parser return $regsubex($1-,/((sin|asin|acos|atan|cos|tan|log|sqrt)50(-?d+(?:.d+)?)51)/g,$($2($calc(3)) $+ .deg,2))

(All the \\ are absent :s.)

That apart, here\'s from where I\'ve started regexps.
http://www.scriptsdb.org/tutar.php?id=1 (french site)
Then, I\'ve used these also.
http://www.cs.utah.edu/dept/old/texinfo/regex/regex.html
http://www.pcre.org/pcre.txt
http://en.wikipedia.org/wiki/Regular_expression
http://www.regular-expressions.info/
http://www.expreg.com/ (french site)
and some more sites found on google. :)

The $regsubex identifier is just a possibility that can be used once you know regexp.
I only learnt regexps and then made applications using mIRC mainly.
Now I\'m learning Perl. But that\'s another chapter lol.

Cordialement :)

 Respond  
MoNoXiDe   -  Dec 23, 2007

Without all comments and spaces, it\'s only 2.1kb :o)

EDIT: I actually found another bug :( in the parser it can\'t do sin(23) for example, the parser doesn\'t recognise it with the or / or whatever\'s in the middle for some reason. I uploaded the updated version of you want to test it.

 Respond  
MoNoXiDe   -  Dec 23, 2007

Dude thankyou so much! Updated n done. It\'s pretty much finished now :)(unless there are any more unwanted requests :P). No but seriously I don\'t know what else to add to it now, it works exactly as it should.

Btw, where did you learn your regsubex stuff really well? I would love to learn it, it\'s incredibily useful.

 Respond  
TropNul   -  Dec 23, 2007

In the trans function there are these 2 lines which could be optimized a bit imho.

%eq = $regsubex(%eq,/(\\\\d)x/g,\\\\t*x)
%eq = $regsubex(%eq,/(\\\\d[a-z])/g,$remove(\\\\t,$regsubex(\\\\t,\\\\d+,\\\\t)) $+ * $+ $regsubex(\\\\t,\\\\d+,\\\\t))

Basically, both of them does the same thing, except for the first which considers only the \'x\' character after a digit.

They could be replaced by this single line.

%eq = $regsubex(a,%eq,/(\\\\d)([a-z])/g,\\\\1*\\\\2)

;o)

 Respond  
TropNul   -  Dec 23, 2007

Oops, I\'ve forgotten the /x in the /gx options. It\'s of no use here (I used it for my tests). Just remove it. :)

 Respond  
TropNul   -  Dec 23, 2007

There you are.

1> supports negative numbers inside the trigonometric functions, like sin(-30) for example.
2> calculates directly the contents of the argument of the trigonometric function (still not using $stript).

alias parser {
  return $regsubex($1-,/((sin|asin|acos|atan|cos|tan|log|sqrt)\\\\050(-?\\\\d+(?:\\\\.\\\\d+)?)\\\\051)/gx,$($\\\\2($calc(\\\\3)) $+ .deg,2))
}

Cordialement :o)

 Respond  
MoNoXiDe   -  Dec 22, 2007

TropNul, I have found another bug in the parser, and remembered the pain reason I used stript in the first place, it was to calculate what was in the bracket before the function.

Eg if you try to solve for sin(2x) = 1, it won\'t work. stript would take sin(2*45) = 1 or whatever x was and calculate that to be 90. Is there anyway to incorporate that into your current parser (as well as supporting negatives)? Sorry for all the trouble!

 Respond  
MoNoXiDe   -  Dec 22, 2007

Hey TropNul I\'m extremely grateful for all your help! You\'re code works well, except you\'re right, theres a bug. But I don\'t think it\'s because of what you said, I spent an hour or so trying to figure out what it was because it was very confusing. I would try to solve sin(x)=90 (which should return $false) and it would always return -2. It would return false under the old parser and even through all the checks to make sure the answer was legit it still always returned -2. It turned out that the parser couldn\'t support negative numbers with the functions, so sin(-2) wouldn\'t work. I\'m not quite sure but all I need to add is a -? somewhere in the regsubex line? I\'ve also given them different names so that shouldn\'t be a problem.

@Korvin, thanks for the feedback :) However it\'s impossible to add another variable such as Y, this is because you can only solve 1 variable per equation. eg if you consider y + x = 1. This has infinite number of solutions. This primarily solves any single equation. To solve simultaneous equations its infinitely more complicated. I have done it in C# but I think it\'s pretty much close to impossible in mIRC.

You could still solve for any variable in y=mx + c. If you know any 3 values and you are left with a fourth variable.

 Respond  
Korvin   -  Dec 22, 2007

you should have it work for Y also, ex. Y=2x+5 < mx+b equation for linear graphing. i think that that would be a good thing to try =] good job

 Respond  
TropNul   -  Dec 22, 2007

I think that another bug will raise up once you fix this one. :s
Because, the $regsubexes are not named, it conflicts with previous $regmls and recalling for a previous match if another $regsubex was called. Therefore, just by naming differently each $regsubex, it shall be fixed. If that fixes future bugs, then great, otherwise, I\'ll help again. :)
(Or any other person who knows regexps !)

Cordialement

 Respond  
TropNul   -  Dec 22, 2007

I\'ve done something that does what you need without the use of $stript.

alias parser {
  return $regsubex($1-,/((?:sin|asin|acos|atan|cos|tan|log|sqrt)\\\\050(?:\\\\d+(?:\\\\.\\\\d+)?)\\\\051)/g,$($ $+ \\\\1 $+ .deg,2))
}

Tell me if it suits exactly your needs.

Cordialement :)

 Respond  
MoNoXiDe   -  Dec 21, 2007

Wow thanks a lot! I added the code (under the trans alias) and you in the credits :)

Updated it guys.

Unfortunately I found another bug, if you have more than one function in the code, the solver doesn\'t work. So if you try to solve (sinx)(cosx)=0.2 no luck. I rooted the problem to be the with parser, after it sorts out the first function, it doesn\'t continue to the next one, it just stops.

The parser does this: $parser(sin(30)-3.4) returns 0.5-3.4 and then you can put that into $calc. However if you do (sin(30)+sin(30)-3.4), it returns 0.5+0-3.4. I really have no idea how to solve it. the stript function doesn\'t need any editing, it\'s just the regsubex of the parser, but I have no idea how to do it, I know next to nothing about regex now.

alias parser return $regsubex($regsubex($1,/(?<=sin|asin|acos|atan|cos|tan|log|sqrt)(\\\\d+)/g,(\\\\t)),/((?:sin|asin|acos|atan|cos|tan|log|sqrt).+?\\\\051)/g,$($ $+ $stript(\\\\t,1) $+ $chr(40) $+ $stript(\\\\t) $+ $chr(41) $+ .deg,2)).

It\'s not that important anyway, but just would be nice to have a complete solver. Thanks a lot for before though TropNul :)

 Respond  
TropNul   -  Dec 21, 2007

Wow ! ok ok now, here\'s the code lol.

alias rtrig.x {
  return $regsubex($1-,/(sin|cos|tan)(x+)/g,\\\\1 $+ $regsubex(s,\\\\2,/(x)/g,$+($chr(40),x,$chr(41))))
}
 Respond  
TropNul   -  Dec 21, 2007

Yep, but backslashes are truncated. Maybe I should double-backslash.

test:

\\\\
 Respond  
Rebellious   -  Dec 21, 2007

.. [.code] and [./code] without the .\'s ;)

 Respond  
Rebellious   -  Dec 21, 2007

The BBCODE tags are just

 & 
 Respond  
TropNul   -  Dec 21, 2007

Last try ...

[code=mirc]
alias rtrig.x {
return $regsubex($1-,/(sin|cos|tan)(x+)/g,\1 $+ $regsubex(s,\2,/(x)/g,$+($chr(40),x,$chr(41))))
}
[/code=mirc]

[mirc]
alias rtrig.x {
return $regsubex($1-,/(sin|cos|tan)(x+)/g,\1 $+ $regsubex(s,\2,/(x)/g,$+($chr(40),x,$chr(41))))
}
[/mirc]

 Respond  
TropNul   -  Dec 21, 2007

It seems that backslashes are truncated when posting. I can\'t seen any of them in my last post.

Here\'s the code with plain \'backslash\' in it which are to be replaced for sure.

alias rtrig.x {
return $regsubex($1-,/(sin|cos|tan)(x+)/g,\'backslash\'1 $+ $regsubex(s,\'backslash\'2,/(x)/g,$+($chr(40),x,$chr(41))))
}

 Respond  
TropNul   -  Dec 21, 2007

Here\'s a solution.

alias rtrig.x {
  return $regsubex($1-,/(sin|cos|tan)(x+)/g,\\1 $+ $regsubex(s,\\2,/(x)/g,$+($chr(40),x,$chr(41))))
}

Usage (as an identifier): $rtrig.x(string)

I\'ve included only sin, cos and tan functions. You can add what you want in the alternating group \" (sin|cos|tan) \".

Cordialement :)

 Respond  
MoNoXiDe   -  Dec 21, 2007

Thanks for feedback guys :)

However I need some help with a regsubex expression, I coded this over 6 months ago and only learnt regex over the weekend especially for this. I have forgotton it all...

I need it so that if there is a letter before an x, the x gets replaced to (x). Eg, sinx becomes sin(x) and cosx becomes cos(x) etc. I\'m not sure if this is possible but if xx could be (x)(x) as well?

Could anyone help me with this at all? I want to just do $solve(sinx = 0.23) without having to do sin(x). Cheers

 Respond  
TropNul   -  Dec 20, 2007

Really appreciate the commented version. :*).
Thank you very much. Now I\'ll read it with more pleasure. :)

Good job !

 Respond  
Rebellious   -  Dec 20, 2007

Excellent. It seems to work now. :)

 Respond  
MoNoXiDe   -  Dec 20, 2007

Really? I get 4x = 20 returns 5. Ok I\'ve replaced the code with the one I have exactly. I then deleted my code and pasted the one on here and it still gave me 5. So the one uploaded is definitely the right one now. Sorry about the mix up :)

ty for testing it though

TropNul I have added comments to my code to how it works. If you need anything else clearing up just let me know.

 Respond  
Rebellious   -  Dec 20, 2007

another example: $solve(4x=20) returned $false

Sorry for the d-post.

 Respond  
Are you sure you want to unfollow this person?
Are you sure you want to delete this?
Click "Unsubscribe" to stop receiving notices pertaining to this post.
Click "Subscribe" to resume notices pertaining to this post.