<?php

namespace mcordingley\Regression\Algorithm\GradientDescent;

use mcordingley\Regression\Observations;

final class Stochastic extends GradientDescent
{
    /**
     * @param Observations $observations
     * @param array $coefficients
     * @return array
     */
    protected function calculateGradient(Observations $observations, array $coefficients)
    {
        $observation = $observations->getObservation(mt_rand(0, count($observations) - 1));
        return $this->gradient->gradient($coefficients, $observation->getFeatures(), $observation->getOutcome());
    }
}

<?php

namespace mcordingley\Regression\Algorithm\GradientDescent;

use mcordingley\Regression\Observation;
use mcordingley\Regression\Observations;

final class Stochastic extends GradientDescent
{
    /** @var array */
    private $shuffled;

    /**
     * @param Observations $observations
     * @param array $coefficients
     * @return array
     */
    protected function calculateGradient(Observations $observations, array $coefficients)
    {
        $observation = $this->getObservation($observations);

        return $this->gradient->gradient($coefficients, $observation->getFeatures(), $observation->getOutcome());
    }

    /**
     * @param Observations $observations
     * @return Observation
     */
    private function getObservation(Observations $observations)
    {
        if (!$this->shuffled) {
            $this->shuffled = range(0, count($observations) - 1);
            shuffle($this->shuffled);
        }

        return $observations->getObservation(array_pop($this->shuffled));
    }
}

Modify the SGD to sweep through the training set in random order instead of doing random draws.
Modify the SGD to sweep through the training set in random order instead of doing random draws.
