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

Combinations::setTimeout()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
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
use drupol\phpermutations\Generators\Combinations as CombinationsGenerator;
10
11
/**
12
 * Class Combinations.
13
 */
14
class Combinations extends Partitioner
15
{
16
    /**
17
     * @var int
18
     */
19
    private $timeout;
20
21
    /**
22
     * Linear constructor.
23
     *
24
     * @param int $timeout
25
     *
26
     * @throws \Exception
27
     */
28
    public function __construct(int $timeout = 10)
29
    {
30
        if (!\class_exists(CombinationsGenerator::class)) {
31
            throw new \Exception(
32
                \sprintf(
33
                    'The class %s is not available, run "%s" to get it.',
34
                    '\drupol\phpermutations\Generators\Combinations',
35
                    'composer require drupol/phpermutations'
36
                )
37
            );
38
        }
39
40
        $this->timeout = $timeout;
41
    }
42
43
    /**
44
     * @return int
45
     */
46
    public function getTimeout()
47
    {
48
        return $this->timeout;
49
    }
50
51
    /**
52
     * @param int $timeout
53
     *
54
     * @return $this
55
     */
56
    public function setTimeout(int $timeout)
57
    {
58
        $this->timeout = $timeout;
59
60
        return $this;
61
    }
62
63
    /**
64
     * {@inheritdoc}
65
     */
66
    protected function fillPartitions(Partitions $partitions, Partition $dataset, int $chunks): void
67
    {
68
        $partition = new Partition($dataset);
69
        $partition->uasort(function ($left, $right) {
70
            return $left->getWeight() <=> $right->getWeight();
71
        });
72
        $dataset = $partition->getArrayCopy();
73
74
        for ($p = $chunks; 1 < $p; --$p) {
75
            $partition = new Partition($dataset);
76
77
            $best = $partition->getWeight();
78
            $target = $best / $p;
79
            $maxSize = \floor($partition->count() / $p);
80
81
            $goodSubset = $this->getSubsets($maxSize, $dataset, $target, $best);
82
83
            // We cannot use array_udiff() to compare objects because we only need
84
            // to remove one object at a time.
85
            foreach ($goodSubset as $item) {
86
                if (false !== $key = \array_search($item, $dataset, true)) {
87
                    unset($dataset[$key]);
88
                }
89
            }
90
91
            if (!empty($goodSubset)) {
92
                $partitionsArray[] = $goodSubset;
93
            }
94
        }
95
96
        $partitionsArray[] = $dataset;
97
98
        foreach ($partitionsArray as $key => $subset) {
99
            $partitions->partition($key)->exchangeArray($subset);
100
        }
101
    }
102
103
    /**
104
     * @param float $maxSize
105
     * @param array $dataset
106
     * @param float $target
107
     * @param float $best
108
     *
109
     * @return array
110
     */
111
    private function getSubsets($maxSize, $dataset, $target, $best)
112
    {
113
        $start_time = \time();
114
        $goodSubsets = $dataset;
115
        $timeout = $this->getTimeout();
116
117
        for ($i = 1; $i <= $maxSize; ++$i) {
118
            $permutations = new CombinationsGenerator($dataset, $i);
119
            foreach ($permutations->generator() as $subset) {
120
                if (\time() - $start_time > $timeout) {
121
                    return $goodSubsets;
122
                }
123
124
                $x = 0;
125
                foreach ($subset as $item) {
126
                    $x += $item->getWeight();
127
                    if (\abs($x - $target) < \abs($best - $target)) {
128
                        $best = $x;
129
                        $goodSubsets = $subset;
130
                    }
131
                }
132
            }
133
        }
134
135
        return $goodSubsets;
136
    }
137
}
138