Here's a fun one... How would you solve a basic simultanous equation with PHP?

I have an array with a range [high,low] of [378,395] and I want to normalise the data in that array between the values of [-1, 1].

From that, using x => ax + byou get the two equations:
378a + b = -1
395a + b = 1

Obviously this is quite easy to solve on paper...But I wouldn't have a clue how to go about it in PHP. A quick google search reveals a scary lack of information :/

Recommended Answers

All 8 Replies

That was interesting but it took me a lot more time to decide the names of the variables than actually writing (I am not sure for those names till now). I used the PHP math functions to achieve a precision that can be set through the scale variable of the Model_Operation_TwoUnknownsLinearEquationSolver by default it is five, you can see it in action in the second example. It is also interesting that in the second example online solvers (that I used to test) resulted wrong results.

class Model_Object_TwoUnknownsLinearEquation
{
    private $firstUnknownMultiplier; 
    private $secondUnknownMultiplier;
    private $result; 

    public function setFirstUnknownMultiplier($firstUnknownMultiplier)
    {
        $this->firstUnknownMultiplier = $firstUnknownMultiplier;
    }

    public function setSecondUnknownMultiplier($secondUnknownMultiplier)
    {
        $this->secondUnknownMultiplier = $secondUnknownMultiplier;
    }

    public function setResult($result)
    {
        $this->result = $result; 
    }

    public function getFirstUnknownMultiplier()
    {
        return $this->firstUnknownMultiplier;
    }

    public function getSecondUnknownMultiplier()
    {
        return $this->secondUnknownMultiplier;
    }

    public function getResult()
    {
        return $this->result;
    }

}

class Model_Operation_TwoUnknownsLinearEquationSolver
{
    private $firstUknown; 
    private $secondUknown; 

    public function __construct(Model_Object_TwoUnknownsLinearEquation $firstEquation
    , Model_Object_TwoUnknownsLinearEquation $secondEquation,$scale = 5)
    {

        // Checking that the equations are formed corectly 
        $this->checkEquation($firstEquation);
        $this->checkEquation($secondEquation);

        // There are many ways to do it we will just get the secondUnknownMultiplier
        // from the second equation and create a factor of it from the secondUnknownMultiplier
        // of the first equation 
        $factor = bcdiv(abs($secondEquation->getSecondUnknownMultiplier()) 
        ,abs($firstEquation->getSecondUnknownMultiplier()),$scale);

        $firstEquation->setFirstUnknownMultiplier(bcmul($firstEquation->getFirstUnknownMultiplier(),$factor,$scale));
        $firstEquation->setResult(bcmul($firstEquation->getResult(),$factor,$scale));
        // if the seconds equation secondUnknownMultiplier is greater than zero we will subtract it from the first 
        // else we will add it
        if($secondEquation->getSecondUnknownMultiplier() > 0)
        {
            $firstEquation->setFirstUnknownMultiplier(bcsub($firstEquation->getFirstUnknownMultiplier(),$secondEquation->getFirstUnknownMultiplier(),$scale));
            $firstEquation->setResult(bcsub($firstEquation->getResult(),$secondEquation->getResult(),$scale)); 
        }
        else 
        {
            $firstEquation->setFirstUnknownMultiplier(bcadd($firstEquation->getFirstUnknownMultiplier(),$secondEquation->getFirstUnknownMultiplier(),$scale));
            $firstEquation->setResult(bcadd($firstEquation->getResult(),$secondEquation->getResult(),$scale));          
        }

        // now we will just know the first uknown 
        $this->firstUknown = bcdiv($firstEquation->getResult(),$firstEquation->getFirstUnknownMultiplier(),$scale);
        // lets find the second uknown 
        // we could write that in one line but we won't in order to be more clear what we are doing
        $tempFirstPart = bcmul($secondEquation->getFirstUnknownMultiplier(),$this->firstUknown,$scale);
        $tempResult = bcsub($secondEquation->getResult(),$tempFirstPart,$scale);
        $this->secondUknown = bcdiv($tempResult,$secondEquation->getSecondUnknownMultiplier(),$scale);
    }

    /**
     * Check that the equation given is formed corectly
     * @param Model_Object_TwoUnknownsLinearEquation $equation
     * @throws Exception
     */
    private function checkEquation(Model_Object_TwoUnknownsLinearEquation $equation)
    {
        if($equation->getFirstUnknownMultiplier() == null 
            || $equation->getSecondUnknownMultiplier() == null 
            || $equation->getResult() == null 
            || !is_numeric($equation->getFirstUnknownMultiplier())
            || !is_numeric($equation->getSecondUnknownMultiplier())
            || !is_numeric($equation->getResult())
            || $equation->getFirstUnknownMultiplier() == 0 
            || $equation->getSecondUnknownMultiplier() == 0)
        {
            throw new Exception("Mallformed equation {".var_export($equation,true)."}", 5487);
        }
    }

    public function getFirstUknown()
    {
        return $this->firstUknown;
    }

    public function getSecondUknown()
    {
        return $this->secondUknown;
    }
}

// A simple example
// e.g. we have x+y=10 and x-y = 2
$firstEquation = new Model_Object_TwoUnknownsLinearEquation();
$firstEquation->setFirstUnknownMultiplier(1);
$firstEquation->setSecondUnknownMultiplier(1);
$firstEquation->setResult(10);


$secondEquation = new Model_Object_TwoUnknownsLinearEquation();
$secondEquation->setFirstUnknownMultiplier(1);
$secondEquation->setSecondUnknownMultiplier(-1);
$secondEquation->setResult(2);

$solver = new Model_Operation_TwoUnknownsLinearEquationSolver($firstEquation, $secondEquation,1);
echo "x=".$solver->getFirstUknown();
echo "<br/>";
echo "y=".$solver->getSecondUknown();
echo "<br/>";
echo "<br/>";

// And a more advanced example
// e.g. we have 5x+3.4y=54.39 and 3x-6y = 33
$firstEquation = new Model_Object_TwoUnknownsLinearEquation();
$firstEquation->setFirstUnknownMultiplier(5);
$firstEquation->setSecondUnknownMultiplier(3.4);
$firstEquation->setResult(54.39);

$secondEquation = new Model_Object_TwoUnknownsLinearEquation();
$secondEquation->setFirstUnknownMultiplier(3);
$secondEquation->setSecondUnknownMultiplier(-6);
$secondEquation->setResult(33);

$solver = new Model_Operation_TwoUnknownsLinearEquationSolver($firstEquation, $secondEquation,20);
echo "x=".$solver->getFirstUknown();
echo "<br/>";
echo "y=".$solver->getSecondUknown();
echo "<br/>";
commented: Amazing!!!! +2

Wow. I really didn't expect a response like that, it's amazing!

The only problem now is I don't actually understand the code! A bit more advanced than I'm used to, but the math is sound and it does indeed yield the correct result.

I'll spend a bit of time studying this. You've annoted it nicely so I should get through it alright. However, could you explain further the $scale variable and how that works with the PHP math functions to alter precisions?

Thanks again for this amazing contribution!!

Hello James,
Thank you, it was an interesting question
For the "scale" you see more at http://php.net/manual/en/function.bcsub.php and read the examples also. In this example the precision is a great factor, the greater scale you give the most precision result you will get.
I am copying

scale:
This optional parameter is used to set the number of digits after the decimal place in the result.

Thinking about some cases , I made a small improvement and also now it takes account the possibility that the equations are wrong (e.g. 2x+2y=24 and 2x+2y=54)

<?php

class Model_Object_TwoUnknownsLinearEquation
{
    private $firstUnknownMultiplier;
    private $secondUnknownMultiplier;
    private $result;

    public function setFirstUnknownMultiplier($firstUnknownMultiplier)
    {
        $this->firstUnknownMultiplier = $firstUnknownMultiplier;
    }

    public function setSecondUnknownMultiplier($secondUnknownMultiplier)
    {
        $this->secondUnknownMultiplier = $secondUnknownMultiplier;
    }

    public function setResult($result)
    {
        $this->result = $result;
    }

    public function getFirstUnknownMultiplier()
    {
        return $this->firstUnknownMultiplier;
    }

    public function getSecondUnknownMultiplier()
    {
        return $this->secondUnknownMultiplier;
    }

    public function getResult()
    {
        return $this->result;
    }

}

class Model_Operation_TwoUnknownsLinearEquationSolver
{
    private $firstUknown;
    private $secondUknown;

    public function __construct(Model_Object_TwoUnknownsLinearEquation $firstEquation
    , Model_Object_TwoUnknownsLinearEquation $secondEquation,$scale = 5)
    {

        // Checking that the equations are formed corectly
        $this->checkEquation($firstEquation);
        $this->checkEquation($secondEquation);

        // There are many ways to do it we will just get the secondUnknownMultiplier
        // from the second equation and create a factor of it from the secondUnknownMultiplier
        // of the first equation
        $factor = bcdiv($secondEquation->getSecondUnknownMultiplier()
        ,$firstEquation->getSecondUnknownMultiplier(),$scale);

        // Dividing ther other parts of the first equation with the factor
        $firstEquation->setFirstUnknownMultiplier(bcmul($firstEquation->getFirstUnknownMultiplier(),$factor,$scale));
        $firstEquation->setResult(bcmul($firstEquation->getResult(),$factor,$scale));

        // Subtract the first unknown of the second equation from the first
        // unknown of the first equation (to leave the second unknown of the first equation zero)
        //. The same for the results
        $firstEquation->setFirstUnknownMultiplier(bcsub($firstEquation->getFirstUnknownMultiplier(),$secondEquation->getFirstUnknownMultiplier(),$scale));
        $firstEquation->setResult(bcsub($firstEquation->getResult(),$secondEquation->getResult(),$scale));

        // In the case the equatuins doesn't make any sense
        // e.g. 1x + 1y = 5 and 1x+1y = 43
        if($firstEquation->getFirstUnknownMultiplier() == 0)
        {
            throw new Exception("Non computable equations", 5487);
        }

        // now we will just know the first uknown
        $this->firstUknown = bcdiv($firstEquation->getResult(),$firstEquation->getFirstUnknownMultiplier(),$scale);
        // lets find the second uknown
        // we could write that in one line but we won't in order to be more clear what we are doing
        $tempFirstPart = bcmul($secondEquation->getFirstUnknownMultiplier(),$this->firstUknown,$scale);
        $tempResult = bcsub($secondEquation->getResult(),$tempFirstPart,$scale);
        $this->secondUknown = bcdiv($tempResult,$secondEquation->getSecondUnknownMultiplier(),$scale);
    }

    /**
     * Check that the equation given is formed corectly
     * @param Model_Object_TwoUnknownsLinearEquation $equation
     * @throws Exception
     */
    private function checkEquation(Model_Object_TwoUnknownsLinearEquation $equation)
    {
        if($equation->getFirstUnknownMultiplier() == null
        || $equation->getSecondUnknownMultiplier() == null
        || $equation->getResult() == null
        || !is_numeric($equation->getFirstUnknownMultiplier())
        || !is_numeric($equation->getSecondUnknownMultiplier())
        || !is_numeric($equation->getResult())
        || $equation->getFirstUnknownMultiplier() == 0
        || $equation->getSecondUnknownMultiplier() == 0)
        {
            throw new Exception("Mallformed equation {".var_export($equation,true)."}", 5487);
        }
    }

    public function getFirstUknown()
    {
        return $this->firstUknown;
    }

    public function getSecondUknown()
    {
        return $this->secondUknown;
    }
}

// A simple example
// e.g. we have x+y=10 and x-y = 2
$firstEquation = new Model_Object_TwoUnknownsLinearEquation();
$firstEquation->setFirstUnknownMultiplier(1);
$firstEquation->setSecondUnknownMultiplier(-1);
$firstEquation->setResult(2);


$secondEquation = new Model_Object_TwoUnknownsLinearEquation();
$secondEquation->setFirstUnknownMultiplier(1);
$secondEquation->setSecondUnknownMultiplier(1);
$secondEquation->setResult(10);

$solver = new Model_Operation_TwoUnknownsLinearEquationSolver($firstEquation, $secondEquation,10);
echo "x=".$solver->getFirstUknown();
echo "<br/>";
echo "y=".$solver->getSecondUknown();
echo "<br/>";
echo "<br/>";

// And a more advanced example
// e.g. we have 5x+3.4y=54.39 and 3x-6y = 33
$firstEquation = new Model_Object_TwoUnknownsLinearEquation();
$firstEquation->setFirstUnknownMultiplier(5);
$firstEquation->setSecondUnknownMultiplier(3.4);
$firstEquation->setResult(54.39);

$secondEquation = new Model_Object_TwoUnknownsLinearEquation();
$secondEquation->setFirstUnknownMultiplier(3);
$secondEquation->setSecondUnknownMultiplier(-6);
$secondEquation->setResult(33);

$solver = new Model_Operation_TwoUnknownsLinearEquationSolver($firstEquation, $secondEquation,20);
echo "x=".$solver->getFirstUknown();
echo "<br/>";
echo "y=".$solver->getSecondUknown();
echo "<br/>";


?>

Although I am happy with my previous answer , it gives correct results and is a great way to understand more about OOP , I thought it once again and realized that there isn't any need for an algorithmic approach in that problem. If we have
a1*x + b1*y = c1 and a2*x + b2*y = c2
then
x = ( c1 - ( c2 * ( b2 / b1 ) ) ) / ( a1 - ( a2 * ( b2 / b1 ) ) )
and
y = ( c1 - ( c2 * ( a2 / a1 ) ) ) / ( b1 – ( b2 * ( a2 / a1 ) ) )

putting it in PHP we have:

$a = [
                [1,1,10],
                [1,-1,2]
         ];

$x = ( $a[0][2] - ( $a[1][2] * ( $a[0][1] / $a[1][1] ) ) ) /
         ( $a[0][0] - ( $a[1][0] * ( $a[0][1] / $a[1][1] ) ) ) ;

$y = ( $a[0][2] - ( $a[1][2] * ( $a[0][0] / $a[1][0] ) ) ) /
         ( $a[0][1] - ( $a[1][1] * ( $a[0][0] / $a[1][0] ) ) ) ;

echo "x=".$x;
echo "<br/>";
echo "y=".$y;        
echo "<br/>";        
echo "<br/>";    

That works great for x+y=10and x-y = 2 but if you test it for : 5x+3.4y=54.39and 3x-6y = 33 you will get low precision results because PHP Floating point precision (@see http://php.net/manual/en/language.types.float.php) . So we will need PHP math functions

$a = [
                [5,3.4,54.39],
                [3,-6 ,33]
         ];
$s = 30; // scale
$x = 
bcdiv(
    bcsub($a[0][2],bcmul($a[1][2], bcdiv($a[0][1], $a[1][1],$s),$s) ,$s)
    ,bcsub($a[0][0],bcmul($a[1][0], bcdiv($a[0][1], $a[1][1],$s),$s) ,$s)
    ,$s
);

$y = 
bcdiv(
    bcsub($a[0][2],bcmul($a[1][2], bcdiv($a[0][0], $a[1][0],$s),$s) ,$s)
    ,bcsub($a[0][1],bcmul($a[1][1], bcdiv($a[0][0], $a[1][0],$s),$s) ,$s)
    ,$s
);
echo "x=".$x;
echo "<br/>";
echo "y=".$y;        
echo "<br/>";    

My previous answer with objects still has advantages , first of all it demonstrates how we do it , it also take in account cases where the given equations make no sense (e.g. 2x + 2y = 10and 4x + 4y = 37). Moreover it is something really needed when we must have an algorithmic approach. One more intresting problem that would benefit from the OOP approach would be if we had nth unknowns in nth linear equations (there we would use matrix algorithms)

I hope this is my last post in this ;)
Correction where I explained the issue:
a1x + b1y = c1 and a2x + b2y = c2
then
x = ( c1 - ( c2 * ( b1 / b2 ) ) ) / ( a1 - ( a2 * ( b1 / b2 ) ) )
and
y = ( c1 - ( c2 * ( a1 / a2 ) ) ) / ( b1 - ( b2 * ( a1 / a2 ) ) )

Also to cover all possibilities if b2 is 0 and b1 is not zero or a2 is 0 and a1 is not 0 the solution is a bit more complicated (notice that without the first OOP approach if the system has infinite or none solution will just get a warning from PHP bcdiv(): Division by zero)

$a = [
                [4,4,12],
                [0,2,4]

         ];
$s = 30; // scale
$x = 
    $a[1][1] == 0 
    ? 
    bcdiv(
        bcsub($a[1][2],bcmul($a[0][2], bcdiv($a[1][1], $a[0][1],$s),$s) ,$s)
        ,bcsub($a[1][0],bcmul($a[0][0], bcdiv($a[1][1], $a[0][1],$s),$s) ,$s)
        ,$s
    )
    :
    bcdiv(
        bcsub($a[0][2],bcmul($a[1][2], bcdiv($a[0][1], $a[1][1],$s),$s) ,$s)
        ,bcsub($a[0][0],bcmul($a[1][0], bcdiv($a[0][1], $a[1][1],$s),$s) ,$s)
        ,$s
    );

$y = 
        $a[1][0] == 0 
        ?
            bcdiv(
                bcsub($a[1][2],bcmul($a[0][2], bcdiv($a[1][0], $a[0][0],$s),$s) ,$s)
                ,bcsub($a[1][1],bcmul($a[0][1], bcdiv($a[1][0], $a[0][0],$s),$s) ,$s)
                ,$s
            )       
        :
        bcdiv(
            bcsub($a[0][2],bcmul($a[1][2], bcdiv($a[0][0], $a[1][0],$s),$s) ,$s)
            ,bcsub($a[0][1],bcmul($a[1][1], bcdiv($a[0][0], $a[1][0],$s),$s) ,$s)
            ,$s
        );
echo "x=".$x;
echo "<br/>";
echo "y=".$y;        
echo "<br/>";    

I hope know to stop thinking about it ;) ;)

Nice. I like that new approach to dealing with it!

One semi-related question though: what happens when x or y is a irrational number and you want to use it in a new calculation - can you store numbers in PHP as frations, or would you simply have to make do with a margin of error? e.g. scale = 1000 or something.

Any calculation with irrational numbers can't be 100% accurate (even if we define a way to store them as fractions , when we will use those in calculations the same thing will happen). As you correct mentioned , you have to live with it and accept a margin of error , the greater the scale factor – precision - number of digits after the decimal place - is , the lower that error becomes. I think that are rare cases where that scale has meaning to be 1000 (or more). What we normally do is round the number to x precision @see http://php.net/manual/en/function.round.php.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.