Implement weighted job scheduling

This commit is contained in:
Dennis Eichhorn 2019-10-17 10:49:52 +02:00
parent 4e3678d498
commit a504f6bd0e
2 changed files with 258 additions and 0 deletions

View File

@ -0,0 +1,123 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package phpOMS\Algorithm\JobScheduling
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\Algorithm\JobScheduling;
/**
* Job for scheduling
*
* @package phpOMS\Algorithm\JobScheduling
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
class Job
{
/**
* Value of the job
*
* @var float
* @since 1.0.0
*/
private float $value = 0.0;
/**
* Start time of the job
*
* @var \DateTime
* @since 1.0.0
*/
private \DateTime $start;
/**
* End time of the job
*
* @var \DateTime
* @since 1.0.0
*/
private ?\DateTime $end = null;
/**
* Name of the job
*
* @var string
* @since 1.0.0
*/
private string $name = '';
/**
* Cosntructor.
*
* @param float $value Value of the job
* @param \DateTime $start Start time of the job
* @param null|\DateTime $end End time of the job
*
* @since 1.0.0
*/
public function __construct(float $value, \DateTime $start, ?\DateTime $end, string $name = '')
{
$this->value = $value;
$this->start = $start;
$this->end = $end;
$this->name = $name;
}
/**
* Get value of the job
*
* @return float
*
* @since 1.0.0
*/
public function getValue() : float
{
return $this->value;
}
/**
* Get start time of the job
*
* @return \DateTime
*
* @since 1.0.0
*/
public function getStart() : \DateTime
{
return $this->start;
}
/**
* Get end time of the job
*
* @return \DateTime
*
* @since 1.0.0
*/
public function getEnd() : ?\DateTime
{
return $this->end;
}
/**
* Get the name of the job
*
* @return string
*
* @since 1.0.0
*/
public function getName() : string
{
return $this->name;
}
}

View File

@ -0,0 +1,135 @@
<?php
/**
* Orange Management
*
* PHP Version 7.4
*
* @package phpOMS\Algorithm\JobScheduling
* @copyright Dennis Eichhorn
* @license OMS License 1.0
* @version 1.0.0
* @link https://orange-management.org
*/
declare(strict_types=1);
namespace phpOMS\Algorithm\JobScheduling;
/**
* Job scheduling algorithm with no overlapping jobs
*
* @package phpOMS\Algorithm\JobScheduling
* @license OMS License 1.0
* @link https://orange-management.org
* @since 1.0.0
*/
final class Weighted
{
/**
* Sort jobs by end date.
*
* @param Jobs $j1 Job 1
* @param Jobs $j2 Job 2
*
* @return int
*
* @since 1.0.0
*/
private static function sortByEnd(Job $j1, Job $j2) : int
{
if ($j1->getEnd() === null && $j2->getEnd() !== null) {
return 1;
}
if ($j1->getEnd() === null && $j2->getEnd() === null) {
return 0;
}
if ($j1->getEnd() !== null && $j2->getEnd() === null) {
return -1;
}
return $j1->getEnd()->getTimestamp() <=> $j2->getEnd()->getTimestamp();
}
/**
* Search for a none-conflicting job that comes befor a defined job
*
* @param Job[] $jobs List of jobs
* @param int $pivot Job to find the previous job to
*
* @return int
*
* @since 1.0.0
*/
private static function binarySearch(array $jobs, int $pivot) : int
{
$lo = 0;
$hi = $pivot - 1;
while ($lo <= $hi) {
$mid = (int) (($lo + $hi) / 2);
if ($jobs[$mid]->getEnd() !== null
&& $jobs[$mid]->getEnd()->getTimestamp() <= $jobs[$pivot]->getStart()->getTimestamp()
) {
if ($jobs[$mid + 1]->getEnd() !== null
&& $jobs[$mid + 1]->getEnd()->getTimestamp() <= $jobs[$pivot]->getStart()->getTimestamp()
) {
$lo = $mid + 1;
} else {
return $mid;
}
} else {
$hi = $mid - 1;
}
}
return -1;
}
/**
* Maximize the value of the job execution without overlapping jobs
*
* @param Job[] $jobs Jobs to filter
*
* @return Job[]
*
* @since 1.0.0
*/
public static function solve(array $jobs) : array
{
$n = \count($jobs);
if ($n < 2) {
return $jobs;
}
\usort($jobs, [self::class, 'sortByEnd']);
$valueTable = [$jobs[0]->getValue()];
$resultTable = [];
$resultTable[0] = [$jobs[0]];
for ($i = 1; $i < $n; ++$i) {
$value = $jobs[$i]->getValue();
$jList = [$jobs[$i]];
$l = self::binarySearch($jobs, $i);
if ($l != -1) {
$value += $valueTable[$l];
$jList = \array_merge($resultTable[$l], $jList);
}
if ($value > $valueTable[$i - 1]) {
$valueTable[$i] = $value;
$resultTable[$i] = $jList;
} else {
$valueTable[$i] = $valueTable[$i - 1];
$resultTable[$i] = $resultTable[$i - 1];
}
}
return $resultTable[$n - 1];
}
}