/** * @param $rideNodes RideNode[] * @return RideConfiguration */ private function buildGenericLeastDurationConfiguration($rideNodes) { $rideConfiguration = new RideConfiguration($this->drivingPools); $workRideNodes = $rideNodes; //always loop all available pools - but stop when no missions are left so it is //possible to have empty rideNodeLists (no driver/vehicle needed for these) //the amount of created rideNodeLists will give the amount of used vehicles foreach ($this->drivingPools as $drivingPool) { if (count($workRideNodes) < 1) { break; } $rideNodeList = new RideNodeList($drivingPool); //set first node on start $rideNodeList->addRideNode(array_shift($workRideNodes)); $poolExtraMinutesPerRide = 0; if ($drivingPool->hasAssociatedDriver()) { $poolExtraMinutesPerRide += $drivingPool->getDriver()->getExtraMinutes(); } $stillFeasible = true; while ($stillFeasible) { $actualNode = $rideNodeList->getActualRideNode(); $bestNode = null; $bestNodeKey = null; $bestEmptyRide = null; $actualDuration = -1; //check all nodes in workSet for feasible and best distance foreach ($workRideNodes as $compareNodeKey => $compareNode) { $emptyRide = $this->adjacenceMatrix[$actualNode->getRideNodeHashId()][$compareNode->getRideNodeHashId()]; //not feasible, get next node if ($emptyRide === -1) { continue; } if ($this->isTaboo($rideNodeList, $compareNode)) { continue; } // feasible $feasibleTimeForNextNode = $actualNode->endMinute + $emptyRide->duration + DispositionVariables::ARRIVAL_BEFORE_PICKUP + $poolExtraMinutesPerRide; //feasible time for node + emptyRide -> to next node if ($feasibleTimeForNextNode <= $compareNode->startMinute) { if ($actualDuration === -1) { $bestNode = $compareNode; $bestNodeKey = $compareNodeKey; $bestEmptyRide = $emptyRide; $actualDuration = $emptyRide->duration; } if ($emptyRide->duration < $actualDuration) { $bestNode = $compareNode; $bestNodeKey = $compareNodeKey; $bestEmptyRide = $emptyRide; $actualDuration = $emptyRide->duration; } } } if ($bestNode && $bestEmptyRide) { $rideNodeList->addRideNode($bestNode); $rideNodeList->addRideNode($bestEmptyRide); unset($workRideNodes[$bestNodeKey]); } else { $stillFeasible = false; } } $rideConfiguration->addRideNodeList($rideNodeList); } $rideConfiguration->setNotFeasibleNodes($workRideNodes); $rideConfiguration->removeEmptyRideNodeLists(); return $rideConfiguration; }