Evaluating multiple expressions to determine the range of a numeric variable in php
Sorry if the title is confusing.
I have several string expressions in an array like these:
var1 <= 6 && var1 > 3
var1 > 2
var1 > 4.5
var2 < 22.5
var2 >= 14.25
var2 < 16
How can I go about evaluating all of the expressions to determine:
var1 m开发者_运维百科in
var1 max
var2 min
var2 max
I understand that with the expressions that are not "or equal to" I won't be able to get an exact value. That is alright.
You're trying to do simple linear programming. SimplexInPHP implements linear programming in PHP, which you could use. See it in action here.
The other option is to implement a solution yourself. Use split()
to split each inequality into tokens. For each variable, compute the min/max by starting off with the range [-inf, inf] and update it for each inequality. If the operator begins with <
then update max to max(cur_max, value)
; otherwise update min to min(cur_min, value)
.
You'll also have to keep track of whether the end-points are inclusive or exclusive. This can be done with booleans is_min_inclusive
and is_max_inclusive
. A new end-point is inclusive if the operator ends in =
, otherwise it's to exclusive. Be sure to handle the case where you have x < 1
and x <= 1
(in both orders), which should result in x < 1
.
<?php
$string = "var1 <= 6 && var1 > 0 && var2 >= 4 && var2 < 200";
//creates an array with the key 'name' and 'min OR 'max'
function parseExpression($expression){
$parts = preg_split("|( )+|", $expression,3,PREG_SPLIT_NO_EMPTY);
$result = array('name'=>$parts[0]);
switch ($parts[1]){
case '<':
$parts[2]-=1;
//NO BREAK <x same as <=(x-1)
case '<=':
$result['max'] = $parts[2];
break;
case '>':
$parts[2]+=1;
//NO BREAK >x same as >=(x+1)
case '>=':
$result['min'] = $parts[2];
break;
default:
throw new Exception("format not supported");
}
return $result;
}
$expressions = explode("&&", $string);
$vars = array();
foreach ($expressions as $expression){
$parsed = parseExpression($expression);
$name = array_shift($parsed);
foreach ($parsed as $key => $value){
if (array_key_exists($key,$vars[$name])){
switch ($key){
case 'min':
$vars[$name][$key] = min($vars[$name][$key],$value);
break;
case 'max':
$vars[$name][$key] = max($vars[$name][$key],$value);
break;
default:
}
throw new Exception("format not supported");
}
else{
$vars[$name][$key] = $value;
}
}
}
var_dump($vars);
?>
<?php
$arr = array("var1 <= 6",
"var1 > 2",
"var1 > 4.5",
"var2 < 22.5",
"var2 >= 14.25",
"var2 < 16");
function find_min_max($arr, $variable) {
$min = '-inf';
$max = 'inf';
while (list($i, $v) = each($arr)) {
list($var, $rel, $value) = preg_split('/\s+/', $v);
if ($var != $variable) continue;
if ($rel == "<" || $rel == "<=") {
if ($value < $max)
$max = $value;
}
else if ($rel == ">" || $rel == ">=") {
if ($value > $min)
$min = $value;
}
}
return array($min, $max);
}
list($min, $max) = find_min_max($arr, "var1");
echo "var1 $min - $max \n";
list($min, $max) = find_min_max($arr, "var2");
echo "var3 $min - $max \n";
?>
So in your example, you'd expect the minimum var1 to be >4.5 and the max to be <=6? Have you considered changing your data structure?
If, for example, your data structure looked something like this:
$limits=array('lte'=>array(6), 'lt'=>array(),'gte'=>array(),'gt'=>(2,4.5);
then parsing becomes trivial. The max of the gt and gte arrays would be the minimum value, with > or >= depending on which array it came from. The minimum of lt and lte arrays would be the max value.
I guess...
http://php.net/manual/en/function.max.php
http://php.net/manual/en/function.min.php
In the example they show nummeric string expressions (so not only casted floats or ints)
精彩评论