From 047d8c3028d62f84004bfe92ff8a3ffcf267c907 Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 17 Aug 2019 22:33:09 +0200 Subject: [PATCH] Implement more sorting algorithms --- Algorithm/Sort/BitonicSort.php | 63 ++++++++++++++++++ Algorithm/Sort/BubbleSort.php | 4 ++ Algorithm/Sort/BucketSort.php | 43 +++++++++++++ Algorithm/Sort/BurstSort.php | 0 Algorithm/Sort/CocktailShakerSort.php | 4 ++ Algorithm/Sort/CombSort.php | 4 ++ Algorithm/Sort/CountingSort.php | 0 Algorithm/Sort/CycleSort.php | 82 ++++++++++++++++++++++++ Algorithm/Sort/GnomeSort.php | 4 ++ Algorithm/Sort/OddEvenSort.php | 4 ++ Algorithm/Sort/SelectionSort.php | 4 ++ Algorithm/Sort/SortableInterface.php | 6 ++ Math/Matrix/Matrix.php | 6 +- tests/Algorithm/Sort/BitonicSortTest.php | 56 ++++++++++++++++ tests/Algorithm/Sort/BucketSortTest.php | 57 ++++++++++++++++ tests/Algorithm/Sort/CycleSortTest.php | 57 ++++++++++++++++ tests/Algorithm/Sort/NumericElement.php | 25 ++++++++ 17 files changed, 417 insertions(+), 2 deletions(-) delete mode 100644 Algorithm/Sort/BurstSort.php delete mode 100644 Algorithm/Sort/CountingSort.php create mode 100644 tests/Algorithm/Sort/BitonicSortTest.php create mode 100644 tests/Algorithm/Sort/BucketSortTest.php create mode 100644 tests/Algorithm/Sort/CycleSortTest.php diff --git a/Algorithm/Sort/BitonicSort.php b/Algorithm/Sort/BitonicSort.php index e69de29bb..727ec53c1 100644 --- a/Algorithm/Sort/BitonicSort.php +++ b/Algorithm/Sort/BitonicSort.php @@ -0,0 +1,63 @@ +compare($list[$i + $dist], $order)) { + $old = $list[$i]; + $list[$i] = $list[$i + $dist]; + $list[$i + $dist] = $old; + } + } + + $first = self::merge(\array_slice($list, 0, $n / 2), $order); + $second = self::merge(\array_slice($list, $n / 2), $order); + + return \array_merge($first, $second); + } +} diff --git a/Algorithm/Sort/BubbleSort.php b/Algorithm/Sort/BubbleSort.php index 82f599bb4..ede4ce25a 100644 --- a/Algorithm/Sort/BubbleSort.php +++ b/Algorithm/Sort/BubbleSort.php @@ -28,6 +28,10 @@ class BubbleSort implements SortInterface { $n = \count($list); + if ($n < 2) { + return $list; + } + do { $newN = 0; diff --git a/Algorithm/Sort/BucketSort.php b/Algorithm/Sort/BucketSort.php index e69de29bb..ae8cfdb8b 100644 --- a/Algorithm/Sort/BucketSort.php +++ b/Algorithm/Sort/BucketSort.php @@ -0,0 +1,43 @@ +getValue() / $M)][] = $element; + } + + $sorted = []; + foreach ($buckets as $bucket) { + $sorted[] = $algo::sort($bucket, SortOrder::ASC); + } + + return $order === SortOrder::ASC ? \array_merge(...$sorted) : \array_reverse(\array_merge(...$sorted), false); + } +} diff --git a/Algorithm/Sort/BurstSort.php b/Algorithm/Sort/BurstSort.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/Algorithm/Sort/CocktailShakerSort.php b/Algorithm/Sort/CocktailShakerSort.php index cfc679692..de09a2a4e 100644 --- a/Algorithm/Sort/CocktailShakerSort.php +++ b/Algorithm/Sort/CocktailShakerSort.php @@ -29,6 +29,10 @@ class CocktailShakerSort implements SortInterface $start = 0; $end = \count($list) - 1; + if ($end < 1) { + return $list; + } + while ($start <= $end) { $newStart = $end; $newEnd = $start; diff --git a/Algorithm/Sort/CombSort.php b/Algorithm/Sort/CombSort.php index d4bd3df6d..e2427480c 100644 --- a/Algorithm/Sort/CombSort.php +++ b/Algorithm/Sort/CombSort.php @@ -31,6 +31,10 @@ class CombSort implements SortInterface $gap = $n; $shrink = 1.3; + if ($n < 2) { + return $list; + } + while (!$sorted) { $gap = (int) \floor($gap / $shrink); diff --git a/Algorithm/Sort/CountingSort.php b/Algorithm/Sort/CountingSort.php deleted file mode 100644 index e69de29bb..000000000 diff --git a/Algorithm/Sort/CycleSort.php b/Algorithm/Sort/CycleSort.php index e69de29bb..f68b98517 100644 --- a/Algorithm/Sort/CycleSort.php +++ b/Algorithm/Sort/CycleSort.php @@ -0,0 +1,82 @@ +getValue() < $item->getValue()) { + ++$pos; + } + } + + if ($pos === $start) { + continue; + } + + while ($item->getValue() === $list[$pos]->getValue()) { + ++$pos; + } + + $old = $list[$pos]; + $list[$pos] = $item; + $item = $old; + ++$writes; + + while ($pos !== $start) { + $pos = $start; + $length1 = \count($list); + for ($i = $start + 1; $i < $length1; ++$i) { + if ($list[$i]->getValue() < $item->getValue()) { + ++$pos; + } + } + + while ($item->getValue() === $list[$pos]->getValue()) { + ++$pos; + } + + $old = $list[$pos]; + $list[$pos] = $item; + $item = $old; + ++$writes; + } + } + + return $order === SortOrder::ASC ? $list : \array_reverse($list, false);; + } +} diff --git a/Algorithm/Sort/GnomeSort.php b/Algorithm/Sort/GnomeSort.php index 89cb4d7ce..5e5511957 100644 --- a/Algorithm/Sort/GnomeSort.php +++ b/Algorithm/Sort/GnomeSort.php @@ -28,6 +28,10 @@ class GnomeSort implements SortInterface { $n = \count($list); + if ($n < 2) { + return $list; + } + for ($i = 1; $i < $n; ++$i) { $j = $i; diff --git a/Algorithm/Sort/OddEvenSort.php b/Algorithm/Sort/OddEvenSort.php index 076ac36df..a7dc88f01 100644 --- a/Algorithm/Sort/OddEvenSort.php +++ b/Algorithm/Sort/OddEvenSort.php @@ -29,6 +29,10 @@ class OddEvenSort implements SortInterface $sorted = false; $n = \count($list); + if ($n < 2) { + return $list; + } + while (!$sorted) { $sorted = true; diff --git a/Algorithm/Sort/SelectionSort.php b/Algorithm/Sort/SelectionSort.php index f3851dd25..d740087a6 100644 --- a/Algorithm/Sort/SelectionSort.php +++ b/Algorithm/Sort/SelectionSort.php @@ -28,6 +28,10 @@ class SelectionSort implements SortInterface { $n = \count($list); + if ($n < 2) { + return $list; + } + for ($i = 0; $i < $n - 1; ++$i) { $min = $i; diff --git a/Algorithm/Sort/SortableInterface.php b/Algorithm/Sort/SortableInterface.php index 71ab63491..9f803b3a0 100644 --- a/Algorithm/Sort/SortableInterface.php +++ b/Algorithm/Sort/SortableInterface.php @@ -25,4 +25,10 @@ namespace phpOMS\Algorithm\Sort; interface SortableInterface { public function compare(self $obj, int $order = SortOrder::ASC) : bool; + + public function getValue(); + + public static function max(array $list); + + public static function min(array $list); } diff --git a/Math/Matrix/Matrix.php b/Math/Matrix/Matrix.php index a54a529b9..edf044c05 100644 --- a/Math/Matrix/Matrix.php +++ b/Math/Matrix/Matrix.php @@ -307,8 +307,9 @@ class Matrix implements \ArrayAccess, \Iterator for ($i = 0; $i < $nDim; ++$i) { $j; for ($j = 0; $j < $mDim; ++$j) { - if (!$selected[$j] && \abs($matrix[$j][$i]) > 0.0001) + if (!$selected[$j] && \abs($matrix[$j][$i]) > 0.0001) { break; + } } if ($j === $mDim) { @@ -321,8 +322,9 @@ class Matrix implements \ArrayAccess, \Iterator for ($k = 0; $k < $mDim; ++$k) { if ($k !== $j && \abs($matrix[$k][$i]) > 0.0001) { - for ($p = $i + 1; $p < $nDim; ++$p) + for ($p = $i + 1; $p < $nDim; ++$p) { $matrix[$k][$p] -= $matrix[$j][$p] * $matrix[$k][$i]; + } } } } diff --git a/tests/Algorithm/Sort/BitonicSortTest.php b/tests/Algorithm/Sort/BitonicSortTest.php new file mode 100644 index 000000000..b6bf27425 --- /dev/null +++ b/tests/Algorithm/Sort/BitonicSortTest.php @@ -0,0 +1,56 @@ +list = [ + new NumericElement(5), + new NumericElement(1), + new NumericElement(4), + new NumericElement(2), + ]; + } + + public function testSortASC() : void + { + $newList = BitonicSort::sort($this->list); + self::assertEquals( + [1, 2, 4, 5], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value] + ); + } + + public function testSortDESC() : void + { + $newList = BitonicSort::sort($this->list, SortOrder::DESC); + self::assertEquals( + [5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value] + ); + } +} diff --git a/tests/Algorithm/Sort/BucketSortTest.php b/tests/Algorithm/Sort/BucketSortTest.php new file mode 100644 index 000000000..2ac3c5d93 --- /dev/null +++ b/tests/Algorithm/Sort/BucketSortTest.php @@ -0,0 +1,57 @@ +list = [ + new NumericElement(5), + new NumericElement(1), + new NumericElement(4), + new NumericElement(2), + new NumericElement(8), + ]; + } + + public function testSortASC() : void + { + $newList = BucketSort::sort($this->list, 2, \phpOMS\Algorithm\Sort\SelectionSort::class); + self::assertEquals( + [1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + ); + } + + public function testSortDESC() : void + { + $newList = BucketSort::sort($this->list, 2, \phpOMS\Algorithm\Sort\SelectionSort::class, SortOrder::DESC); + self::assertEquals( + [8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + ); + } +} diff --git a/tests/Algorithm/Sort/CycleSortTest.php b/tests/Algorithm/Sort/CycleSortTest.php new file mode 100644 index 000000000..65072acdc --- /dev/null +++ b/tests/Algorithm/Sort/CycleSortTest.php @@ -0,0 +1,57 @@ +list = [ + new NumericElement(5), + new NumericElement(1), + new NumericElement(4), + new NumericElement(2), + new NumericElement(8), + ]; + } + + public function testSortASC() : void + { + $newList = CycleSort::sort($this->list); + self::assertEquals( + [1, 2, 4, 5, 8], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + ); + } + + public function testSortDESC() : void + { + $newList = CycleSort::sort($this->list, SortOrder::DESC); + self::assertEquals( + [8, 5, 4, 2, 1], [$newList[0]->value, $newList[1]->value, $newList[2]->value, $newList[3]->value, $newList[4]->value,] + ); + } +} diff --git a/tests/Algorithm/Sort/NumericElement.php b/tests/Algorithm/Sort/NumericElement.php index 299a26283..c70d6f561 100644 --- a/tests/Algorithm/Sort/NumericElement.php +++ b/tests/Algorithm/Sort/NumericElement.php @@ -32,4 +32,29 @@ class NumericElement implements SortableInterface { return $order === SortOrder::ASC ? $this->value > $obj->value : $this->value < $obj->value; } + + public function getValue() + { + return $this->value; + } + + public static function max(array $list) + { + $values = []; + foreach ($list as $element) { + $values[] = $element->value; + } + + return \max($values); + } + + public static function min(array $list) + { + $values = []; + foreach ($list as $element) { + $values[] = $element->value; + } + + return \min($values); + } }