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

Linear::fillPartitions()   B

Complexity

Conditions 9
Paths 128

Size

Total Lines 88
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 41
nc 128
nop 3
dl 0
loc 88
rs 7.5217
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types = 1);
4
5
namespace drupol\phpartition;
6
7
use drupol\phpartition\Partition\Partition;
8
use drupol\phpartition\Partitions\Partitions;
9
10
/**
11
 * Class Linear.
12
 */
13
class Linear extends Partitioner
14
{
15
    /**
16
     * @param \drupol\phpartition\Partitions\Partitions $partitions
17
     * @param \drupol\phpartition\Partition\Partition $dataset
18
     * @param int $chunks
19
     */
20
    protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void
21
    {
22
        $dataset = $dataset->getArrayCopy();
23
24
        // See https://github.com/technically-php/linear-partitioning for
25
        // original version of this algorithm.
26
27
        // An array S of non-negative numbers {s1, ... ,sn}
28
        $s = \array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n]
29
30
        // Integer K - number of ranges to split items into
31
        $k = $chunks;
32
        $n = \count($dataset);
33
34
        // Let D[n,k] be the position of K-th divider
35
        // which produces the minimum possible cost partitioning of N elements to K ranges
36
        $d = [];
37
38
        // Let p be the sum of first i elements (cost calculation optimization)
39
        $p = [];
40
41
        // 1) Init prefix sums array
42
        //    pi = sum of {s1, ..., si}
43
        $p[0] = $this->getPartitionItemFactory()::create(0);
44
        for ($i = 1; $i <= $n; ++$i) {
45
            $p[$i] = $this->getPartitionItemFactory()::create($p[$i - 1]->getWeight() + $s[$i]->getWeight());
46
        }
47
48
        // Let M[n,k] be the minimum possible cost over all partitionings of N elements to K ranges
49
        $m = [];
50
51
        // 2) Init boundaries
52
        for ($i = 1; $i <= $n; ++$i) {
53
            // The only possible partitioning of i elements to 1 range is a single all-elements range
54
            // The cost of that partitioning is the sum of those i elements
55
            $m[$i][1] = $p[$i]; // sum of {s1, ..., si} -- optimized using pi
56
        }
57
58
        for ($j = 1; $j <= $k; ++$j) {
59
            // The only possible partitioning of 1 element into j ranges is a single one-element range
60
            // The cost of that partitioning is the value of first element
61
            $m[1][$j] = $s[1];
62
        }
63
        // 3) Main recurrence (fill the rest of values in table M)
64
        for ($i = 2; $i <= $n; ++$i) {
65
            for ($j = 2; $j <= $k; ++$j) {
66
                $solutions = [];
67
                for ($x = 1; ($i - 1) >= $x; ++$x) {
68
                    $solutions[] = [
69
                        0 => $this->getPartitionItemFactory()::create(
70
                            \max(
71
                                $m[$x][$j - 1]->getWeight(),
72
                                $p[$i]->getWeight() - $p[$x]->getWeight()
73
                            )
74
                        ),
75
                        1 => $x,
76
                    ];
77
                }
78
79
                \usort(
80
                    $solutions,
81
                    static function (array $x, array $y) {
82
                        return $x[0] <=> $y[0];
83
                    }
84
                );
85
86
                $best_solution = $solutions[0];
87
                $m[$i][$j] = $best_solution[0];
88
                $d[$i][$j] = $best_solution[1];
89
            }
90
        }
91
92
        // 4) Reconstruct partitioning
93
        $i = $n;
94
        $j = $k;
95
        $partition = [];
96
        while (0 < $j) {
97
            // delimiter position
98
            $dp = $d[$i][$j] ?? 0;
99
            // Add elements after delimiter {sdp, ..., si} to resulting $partition.
100
            $partition[] = \array_slice($s, $dp + 1, $i - $dp);
101
            // Step forward: look for delimiter position for partitioning M[$dp, $j-1]
102
            $i = $dp;
103
            --$j;
104
        }
105
106
        foreach ($partition as $i => $p) {
107
            $partitions->partition($i)->exchangeArray($p);
108
        }
109
    }
110
}
111