Completed
Pull Request — master (#2)
by Pol
03:22
created

Linear::fillPartitions()   B

Complexity

Conditions 9
Paths 128

Size

Total Lines 83
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 9
eloc 42
nc 128
nop 3
dl 0
loc 83
rs 7.5057
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
        // An array S of non-negative numbers {s1, ... ,sn}
25
        $s = \array_merge([null], $dataset); // adapt indices here: [0..n-1] => [1..n]
26
        // Integer K - number of ranges to split items into
27
        $k = $chunks;
28
        $n = \count($dataset);
29
        // Let M[n,k] be the minimum possible cost over all partitionings of N elements to K ranges
30
        $m = [];
31
        // Let D[n,k] be the position of K-th divider
32
        // which produces the minimum possible cost partitioning of N elements to K ranges
33
        $d = [];
34
        // Note: For code simplicity we don't use zero indices for `m` and `d`
35
        //       to make code match math formulas
36
        // Let pi be the sum of first i elements (cost calculation optimization)
37
        $p = [];
38
        // 1) Init prefix sums array
39
        //    pi = sum of {s1, ..., si}
40
        $p[0] = $this->getPartitionItemFactory()::create(0);
41
        for ($i = 1; $i <= $n; ++$i) {
42
            $p[$i] = $this->getPartitionItemFactory()::create($p[$i - 1]->getWeight() + $s[$i]->getWeight());
43
        }
44
        // 2) Init boundaries
45
        for ($i = 1; $i <= $n; ++$i) {
46
            // The only possible partitioning of i elements to 1 range is a single all-elements range
47
            // The cost of that partitioning is the sum of those i elements
48
            $m[$i][1] = $p[$i]; // sum of {s1, ..., si} -- optimized using pi
49
        }
50
        for ($j = 1; $j <= $k; ++$j) {
51
            // The only possible partitioning of 1 element into j ranges is a single one-element range
52
            // The cost of that partitioning is the value of first element
53
            $m[1][$j] = $s[1];
54
        }
55
        // 3) Main recurrence (fill the rest of values in table M)
56
        for ($i = 2; $i <= $n; ++$i) {
57
            for ($j = 2; $j <= $k; ++$j) {
58
                $solutions = [];
59
                for ($x = 1; ($i - 1) >= $x; ++$x) {
60
                    $solutions[] = [
61
                        0 => $this->getPartitionItemFactory()::create(
62
                            \max(
63
                                $m[$x][$j - 1]->getWeight(),
64
                                $p[$i]->getWeight() - $p[$x]->getWeight()
65
                            )
66
                        ),
67
                        1 => $x,
68
                    ];
69
                }
70
71
                \usort(
72
                    $solutions,
73
                    static function (array $x, array $y) {
74
                        return $x[0] <=> $y[0];
75
                    }
76
                );
77
78
                $best_solution = $solutions[0];
79
                $m[$i][$j] = $best_solution[0];
80
                $d[$i][$j] = $best_solution[1];
81
            }
82
        }
83
84
        // 4) Reconstruct partitioning
85
        $i = $n;
86
        $j = $k;
87
        $partition = [];
88
        while (0 < $j) {
89
            // delimiter position
90
            $dp = $d[$i][$j] ?? 0;
91
            // Add elements after delimiter {sdp, ..., si} to resulting $partition.
92
            $partition[] = \array_slice($s, $dp + 1, $i - $dp);
93
            // Step forward: look for delimiter position for partitioning M[$dp, $j-1]
94
            $i = $dp;
95
            --$j;
96
        }
97
98
        // Fix order as we reconstructed the partitioning from end to start
99
        $partition = \array_reverse($partition);
100
101
        foreach ($partition as $i => $p) {
102
            $partitions->partition($i)->exchangeArray($p);
103
        }
104
    }
105
}
106