Passed
Pull Request — master (#2)
by Pol
04:33
created

GreedyAlt::findOptimalItemKey()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 25
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 3
dl 0
loc 25
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpartition;
6
7
use drupol\phpartition\Contract\PartitionItem;
8
use drupol\phpartition\Partition\Partition;
9
use drupol\phpartition\Partitions\Partitions;
10
11
/**
12
 * Class GreedyAlt.
13
 */
14
class GreedyAlt extends Partitioner
15
{
16
    /**
17
     * {@inheritdoc}
18
     */
19
    protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void
20
    {
21
        // Edge case handling.
22
        if ($dataset->count() === $chunks) {
23
            foreach ($dataset as $key => $item) {
24
                $partitions->partition((int) $key)->exchangeArray([$item]);
25
            }
26
27
            return;
28
        }
29
30
        // Edge case handling.
31
        if (0 === $chunks) {
32
            throw new \InvalidArgumentException('Chunks must be different from 0.');
33
        }
34
35
        // Greedy needs a dataset sorted DESC.
36
        $dataset->uasort(
37
            static function (PartitionItem $left, PartitionItem $right) {
38
                return $right->getWeight() <=> $left->getWeight();
39
            }
40
        );
41
42
        $best = $dataset->getWeight() / $chunks;
43
44
        for ($p = 1; $p < $chunks; ++$p) {
45
            $partition = $partitions->partition($p - 1);
46
47
            while ($partition->getWeight() < $best && 1 < $dataset->count()) {
48
                $key = $this->findOptimalItemKey($partition, $dataset, $best);
49
                $partition->append($dataset[$key]);
50
                unset($dataset[$key]);
51
            }
52
        }
53
54
        $partitions->partition($p - 1)->exchangeArray($dataset);
55
    }
56
57
    /**
58
     * @param \drupol\phpartition\Partition\Partition $partition
59
     * @param \drupol\phpartition\Partition\Partition $dataset
60
     * @param float $best
61
     *
62
     * @return null|false|int|string
63
     */
64
    private function findOptimalItemKey(Partition $partition, Partition $dataset, $best)
65
    {
66
        // If the current partition is empty, then use the highest value of the
67
        // dataset.
68
        if (0 === $partition->count()) {
69
            // As the dataset is sorted DESC, return the highest value.
70
            return \key($dataset);
0 ignored issues
show
Bug introduced by
$dataset of type drupol\phpartition\Partition\Partition is incompatible with the type array expected by parameter $array of key(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
            return \key(/** @scrutinizer ignore-type */ $dataset);
Loading history...
71
        }
72
73
        // Find which number in the $dataset would be the best fit by checking
74
        // which one is closer to the best solution.
75
        // Best solution is the sum of the dataset values divided by the number
76
        // of chunks we want to have.
77
        $partitionWeightMinusBest = $partition->getWeight() - $best;
78
79
        $solutions = \array_map(
80
            static function (PartitionItem $item) use ($partitionWeightMinusBest) {
81
                return \abs($partitionWeightMinusBest + $item->getWeight());
82
            },
83
            \iterator_to_array($dataset)
84
        );
85
86
        \asort($solutions);
87
88
        return \key($solutions);
89
    }
90
}
91