Re: Reverse Polish Notation Question
by Marshall (Canon) on May 02, 2015 at 00:27 UTC
|
I am not sure why RPN is necessary?
For something like this, where we know that X is:
(2(4*x-8)+3*x)-(11*x+1-9)
(2*(4*x-8)+3*x)-(11*x+1-9)
In converting an algebraic representation to RPN, the order of the operands will be unchanged. You wind up either pushing an operand to the stack or applying an operator the previous 2 things on the stack. (well there is the case of an unary operator, 4+ (-8), etc.)
(2*(4*x-8)):
push 2
push 4
push x
op *; (4*x) is now last on stack
push 8
op -; now 4*x-8 is last on stack
op * ; now result is last thing on stack
In Perl, you can compile and execute an expression "on the fly" using "eval". No conversion by you to RPN is necessary. Any legal Perl statement can be executed via eval. There are ways to intercept and report errors in the eval'ed expression (divide by zero), etc.
An expression like this:
2(4x-8)+3x=11x+1-9
where you want "x", is not so easy.
I am flummoxed as to why you think that RPN could help here?
| [reply] [d/l] [select] |
Re: Reverse Polish Notation Question
by hdb (Monsignor) on May 01, 2015 at 21:13 UTC
|
You need some separator (like the ENTER key on a HP 12), otherwise you cannot distinguis between 2 followed by 4 and 24.
| [reply] |
Re: Reverse Polish Notation Question
by Laurent_R (Canon) on May 02, 2015 at 11:08 UTC
|
Although this is not really what you are asking for, another approach for such equation solving, frequently employed in math libraries, is to guess a value and make successive improvements to get closer and closer approximations of the target number.
I thought that it would take me 15 minutes to show an example, but it turned out that it took me an hour and a half, because of some initial mistakes on my part due to the fact that I was not entirely clear in my mind on what exactly I needed to do.
It could probably be simpler, this is very imperfect and not tested against all kinds of cases, but anyway, this gives an idea of the approach:
use strict;
use warnings;
use feature "say";
use constant DEBUG => 1;
my $epsilon = 1e-12; # precision
my ($result, $step) = (0, 10); # arbitrary start values
my $equation = "(2*(4*x-8)+3*x)-(10*x+1-9)"; # " = 0" omitted
$equation =~ s/x/\$x/g;
my $eval = create_eval($equation); # currying the equation for simplic
+ity
my $debug = 1;
while (1) {
my $val = $eval->($result);
last if abs $val < $epsilon;
my $new_result = $result + $step;
my $temp = $eval->($new_result);
say "x = $result ; val = $val ; temp = $temp; step = $step" if DEB
+UG;
$result = $new_result and last if abs $temp < $epsilon;
if ($temp == $val) {
say "No solution for this equation";
undef $result;
last;
}
if (($val > 0 and $temp < 0) or ($val < 0 and $temp > 0)) {
while (1) {
$step = 0;
my $avg = ($result + $new_result) /2;
my $avg_val = $eval->($avg);
# say "x = $result; val = $val ; temp = $temp; step = $ste
+p; avg = $avg" if DEBUG;
printf "%s%.15f%s%.15f%s%.15f%s%.15f\n", "x = ", $result,
+" val = ", $val, " temp = ", $temp, " avg = ", $avg;
$new_result = $avg and last if abs $avg_val < $epsilon;
if ($avg_val > 0) {
$result = $avg and $val= $avg_val if $val > 0;
$new_result = $avg and $temp = $avg_val if $temp > 0;
} else {
$result = $avg and $val = $avg_val if $val < 0;
$new_result = $avg and $temp = $avg_val if $temp < 0;
}
}
}
$step = -$step and next if $temp < $val and $val < 0;
$step /= 2 and next if abs $temp > abs $val; # we are further
+ from 0, reduce step by half
$result =$new_result; # $temp is closer to 0, let's continue
+with $new_resultp
}
printf "%s%.10f\n", "Result is: ", $result;# if defined $result;
sub create_eval {
my $formula = shift;
return sub { my $x = shift; return eval($formula);}
}
The result I get is 8.00000..., so the same as the RPN unraveling by Anonymous Monk, except of course that I do not get an integer but a floating point number.
Just to see the successive approximations, this is the debugging printout.
| [reply] [d/l] [select] |
|
|
Two comments:
From $temp == $val, you cannot conclude that there is no solution, unless you explicitly assume that we have a linear equation, ie a straight line.
If you would calculate the slope from the first two values, you can guess quite well where the solution is, especially for a linear equation.
| [reply] [d/l] |
|
|
Thank you hdb for your comments.
As for the first comment, we have a first degree equation, so, yes, I assumed a straight line on that specific condition, although I was more broadly trying to implement a more general solution.
For your second comment, yes, you are absolutely right, calculating the slope would far easier, especially for a linear equation, and I would not even need to painfully consider whether the various calculated values are positive or negative. And even for a polynomial equation, say second or third degree, calculating the slope would narrow down faster on the solution (something similar to the Newton-Raphson method).
My error was to initially think it could be done in 15 minutes and 20 lines of code, so I started to code something without carefully thinking of the problem, and discovered only when doing it that it was more complicated than I thought. If I have time this weekend, I'll try to come up with a better design.
| [reply] |
|
|
|
|
|
|
Re: Reverse Polish Notation Question
by LanX (Saint) on May 02, 2015 at 07:59 UTC
|
| [reply] |
Re: Reverse Polish Notation Question
by GotToBTru (Prior) on May 01, 2015 at 20:32 UTC
|
| [reply] |
|
|
I don't actually have any code that isn't working or needs fixing. I've gotten to the point where I have converted a single-variable linear equation string into a RPN string. I posted the string in the OP. Now, I have no idea where to go from here. I'm thinking it was a mistake to try to use RPN to solve for 1 variable.
| [reply] |
|
|
When I simplify your equation, I get -8 = 0. I suppose the algorithm works the same on solvable versus insolvable equations, but the latter makes it harder to test your results!
What you have does not resemble RPN as I understand it.
2 (4x - 8) + 3x = 11x + 1 - 9
LHS: 4 x * 8 - 2 * 3 x * +
RHS: 11 x * 1 + 9 -
| [reply] [d/l] |
|
|
|
|
|
|
|
Re: Reverse Polish Notation Question
by Anonymous Monk on May 01, 2015 at 20:54 UTC
|
Go through your string left-to-right. If it's an operand, stack it, otherwise if it's an operator, perform it on the top two items on the stack and place the result back on the stack.
| [reply] |
|
|
And when an x is encountered?
| [reply] |
|
|
#!/usr/bin/perl
# http://perlmonks.org/?node_id=1125392
use strict;
my @stack;
$_ = @ARGV ? "@ARGV" : '2 4x*8-*3x*+11x*1+9--'; # x cancels out
$_ = @ARGV ? "@ARGV" : '2 4x*8-*3x*+10x*1+9--'; # adjusted
print " raw $_\n";
use Data::Dump qw(pp);
while( /(\d+)|(x)|([*+-])/g )
{
pp \@stack;
if( length $1 )
{
push @stack, [ $1 ];
}
elsif( $2 )
{
push @stack, [ 0, 1 ];
}
else
{
my $op = $3;
my ($x, $y) = splice @stack, -2;
if( $op eq '+' )
{
my $i = 0;
$x->[$i++] += $_ for @$y;
push @stack, $x;
}
elsif( $op eq '-' )
{
my $i = 0;
$x->[$i++] -= $_ for @$y;
push @stack, $x;
}
elsif( $op eq '*' )
{
push @stack, multiply( $x, $y );
}
}
}
pp \@stack;
my ($x0, $x1) = @{ pop @stack };
if( $x1 )
{
print "x = ", -$x0 / $x1, "\n";
}
else
{
die "x has cancelled out";
}
sub multiply # two polynomials
{
my ($x, $y) = @_;
my ($n, @result) = (0);
for my $xval (@$x)
{
my $pos = $n;
$result[$pos++] += $xval * $_ for @$y;
$n++;
}
return \@result;
}
| [reply] [d/l] |
|
|
|
|
|
|
|
|
|
| [reply] |
|
|
|
|
|
|
|
| [reply] |
|
|
Re: Reverse Polish Notation Question
by Anonymous Monk on May 01, 2015 at 20:49 UTC
|
| [reply] |
|
|
I'm trying to learn how to do this by hand, without using CPAN.
| [reply] |