Snippet to find multiple solutions to any equations
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.
*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
}