From 462194f0489f5287bcd913112e922cf6eaa0937f Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Wed, 4 Oct 2023 12:49:35 +0200 Subject: [PATCH 01/10] Add the algorithm to search the minimum element of an array using the divide-and-conquer algorithm for selection sort --- .../minimum_element_of_array.py | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 divide_and_conquer/minimum_element_of_array.py diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py new file mode 100644 index 000000000000..78517b41eb92 --- /dev/null +++ b/divide_and_conquer/minimum_element_of_array.py @@ -0,0 +1,111 @@ +""" +Return the minimum element of an array using the divide-and-conquer algorithm for selection sort. +Like quick sort algorithm it partitions the input array recursively. +But unlike quicksort, which recursively processes both sides of the partition, +this algorithm works on only one side of the partition. +The expected running time of this selection sort algorithm is 0(n), assuming that the elements are distinct. +It returns the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1. +(From Introduction to Algorithms, Fourth Edition, Cormen, 2022: Chapter 9.2) +""" +from __future__ import annotations +import random +from typing import Any + + +def partition(array, p, r): + """ + Partition the array. + Args: + array: list of elements + p: starting index of the array + r: ending index of the array + + Returns: + index of the pivot + + """ + pivot = array[r] + i = p - 1 + for j in range(p, r): + if array[j] <= pivot: + i += 1 + array[i], array[j] = array[j], array[i] + array[i + 1], array[r] = array[r], array[i + 1] + return i + 1 + + +def randomized_partition(array, p, r): + """ + Randomized partition of the array. + Args: + array: list of elements + p: starting index of the array + r: ending index of the array + + Returns: + call to partition function + + """ + + rand_idx = random.randint(p, r) + array[rand_idx], array[r] = array[r], array[rand_idx] + return partition(array, p, r) + + +def selection_sort(array: list, p, r, i) -> list | None | Any: + """ + Returns a list of sorted array elements using selection sort. + Args: + array: list of elements + p: starting index of the array + r: ending index of the array + i: the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1 + + Returns: + sorted array + + >>> from random import shuffle + >>> arr = [-2, 3, -10, 11, 99, 100000, 100, -200] + >>> shuffle(arr) + >>> selection_sort(arr, 0, len(arr) - 1, 1) + -200 + + >>> shuffle(arr) + >>> selection_sort(arr, 0, len(arr) - 1, 1) + -200 + + >>> arr = [-200] + >>> selection_sort(arr, 0, len(arr) - 1, 1) + -200 + + >>> arr = [-2] + >>> selection_sort(arr, 0, len(arr) - 1, 1) + -2 + + >>> arr = [] + >>> selection_sort(arr, 0, len(arr) - 1, 1) + [] + """ + + if array is None or len(array) == 0: + return array + + if p == r: + return array[p] # 1 <= i <= r - p + 1 when p == r means that i == 1 + + q = randomized_partition(array, p, r) + + k = q - p + 1 + + if i == k: + return array[q] # the pivot value is the answer + elif i < k: + return selection_sort(array, p, q - 1, i) + else: + return selection_sort(array, q + 1, r, i - k) + + +if __name__ == "__main__": + import doctest + + doctest.testmod() From 1ffd68de3a7cc7997bee55cf843800dd5f598b71 Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:03:01 +0200 Subject: [PATCH 02/10] Update minimum_element_of_array.py Resolved Ruff warning --- divide_and_conquer/minimum_element_of_array.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index 78517b41eb92..a9dd90d91042 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -1,9 +1,12 @@ """ -Return the minimum element of an array using the divide-and-conquer algorithm for selection sort. +Return the minimum element of an array using the +divide-and-conquer algorithm for selection sort. Like quick sort algorithm it partitions the input array recursively. -But unlike quicksort, which recursively processes both sides of the partition, +But unlike quicksort, +which recursively processes both sides of the partition, this algorithm works on only one side of the partition. -The expected running time of this selection sort algorithm is 0(n), assuming that the elements are distinct. +The expected running time of this selection sort algorithm is 0(n), +assuming that the elements are distinct. It returns the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1. (From Introduction to Algorithms, Fourth Edition, Cormen, 2022: Chapter 9.2) """ From 815971a279aca18fcb4daf32be156eca036fbde8 Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:07:10 +0200 Subject: [PATCH 03/10] Update minimum_element_of_array.py Add type hint for every function --- divide_and_conquer/minimum_element_of_array.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index a9dd90d91042..79e1fce0ae3b 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -15,7 +15,7 @@ from typing import Any -def partition(array, p, r): +def partition(array, p, r) -> int: """ Partition the array. Args: @@ -37,7 +37,7 @@ def partition(array, p, r): return i + 1 -def randomized_partition(array, p, r): +def randomized_partition(array, p, r) -> int: """ Randomized partition of the array. Args: @@ -55,7 +55,7 @@ def randomized_partition(array, p, r): return partition(array, p, r) -def selection_sort(array: list, p, r, i) -> list | None | Any: +def selection_sort(array: list, p: int, r: int, i: int) -> list | None | Any: """ Returns a list of sorted array elements using selection sort. Args: From 64f7e5c3f7f0008b36f285440132bb6ea64ff2f5 Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:10:08 +0200 Subject: [PATCH 04/10] Update minimum_element_of_array.py Resolve Ruff error: Error: divide_and_conquer/minimum_element_of_array.py:13:1: I001 Import block is un-sorted or un-formatted --- divide_and_conquer/minimum_element_of_array.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index 79e1fce0ae3b..2a3890d75782 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -11,8 +11,8 @@ (From Introduction to Algorithms, Fourth Edition, Cormen, 2022: Chapter 9.2) """ from __future__ import annotations -import random from typing import Any +import random def partition(array, p, r) -> int: From 695a25cf52c9053d58d8547965e1c55a0b148698 Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:14:50 +0200 Subject: [PATCH 05/10] Update minimum_element_of_array.py Resolve Ruff errors --- divide_and_conquer/minimum_element_of_array.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index 2a3890d75782..6bbfa53ba62a 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -11,8 +11,9 @@ (From Introduction to Algorithms, Fourth Edition, Cormen, 2022: Chapter 9.2) """ from __future__ import annotations -from typing import Any + import random +from typing import Any def partition(array, p, r) -> int: From 095d128c2fce66f7b12cb55752ba0dba5d1ef15b Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:48:20 +0200 Subject: [PATCH 06/10] Update minimum_element_of_array.py --- divide_and_conquer/minimum_element_of_array.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index 6bbfa53ba62a..c9096ab65ac5 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -16,7 +16,7 @@ from typing import Any -def partition(array, p, r) -> int: +def partition(array: list, p: int, r: int) -> int: """ Partition the array. Args: @@ -27,6 +27,11 @@ def partition(array, p, r) -> int: Returns: index of the pivot + + >>> arr = [-2, 3, -10, 11, 99, 100000, 100, -200] + >>> partition(arr, 0, len(arr) - 1) + 0 + """ pivot = array[r] i = p - 1 @@ -38,7 +43,7 @@ def partition(array, p, r) -> int: return i + 1 -def randomized_partition(array, p, r) -> int: +def randomized_partition(array: list, p: int, r: int) -> int: """ Randomized partition of the array. Args: @@ -49,6 +54,12 @@ def randomized_partition(array, p, r) -> int: Returns: call to partition function + + >>> arr = [-2, 3, -10, 11, 99, 100000, 100, -200] + >>> arr1 = randomized_partition(arr, 0, len(arr) - 1) + >>> arr == arr1 + False + """ rand_idx = random.randint(p, r) From a26451078617fee0a2167c0cb10790f33cc6ed8d Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:55:07 +0200 Subject: [PATCH 07/10] Update minimum_element_of_array.py Update name of variables --- .../minimum_element_of_array.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index c9096ab65ac5..d05767873dd9 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -16,13 +16,13 @@ from typing import Any -def partition(array: list, p: int, r: int) -> int: +def partition(array: list, starting_index: int, ending_index: int) -> int: """ Partition the array. Args: array: list of elements - p: starting index of the array - r: ending index of the array + starting_index: starting index of the array + ending_index: ending index of the array Returns: index of the pivot @@ -33,23 +33,23 @@ def partition(array: list, p: int, r: int) -> int: 0 """ - pivot = array[r] - i = p - 1 - for j in range(p, r): + pivot = array[ending_index] + i = starting_index - 1 + for j in range(starting_index, ending_index): if array[j] <= pivot: i += 1 array[i], array[j] = array[j], array[i] - array[i + 1], array[r] = array[r], array[i + 1] + array[i + 1], array[ending_index] = array[ending_index], array[i + 1] return i + 1 -def randomized_partition(array: list, p: int, r: int) -> int: +def randomized_partition(array: list, starting_index: int, ending_index: int) -> int: """ Randomized partition of the array. Args: array: list of elements - p: starting index of the array - r: ending index of the array + starting_index: starting index of the array + ending_index: ending index of the array Returns: call to partition function @@ -62,19 +62,19 @@ def randomized_partition(array: list, p: int, r: int) -> int: """ - rand_idx = random.randint(p, r) - array[rand_idx], array[r] = array[r], array[rand_idx] - return partition(array, p, r) + rand_idx = random.randint(starting_index, ending_index) + array[rand_idx], array[ending_index] = array[ending_index], array[rand_idx] + return partition(array, starting_index, ending_index) -def selection_sort(array: list, p: int, r: int, i: int) -> list | None | Any: +def selection_sort(array: list, starting_index: int, ending_index: int, smallest_element: int) -> list | None | Any: """ Returns a list of sorted array elements using selection sort. Args: array: list of elements - p: starting index of the array - r: ending index of the array - i: the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1 + starting_index: starting index of the array + ending_index: ending index of the array + smallest_element: the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1 Returns: sorted array @@ -105,19 +105,19 @@ def selection_sort(array: list, p: int, r: int, i: int) -> list | None | Any: if array is None or len(array) == 0: return array - if p == r: - return array[p] # 1 <= i <= r - p + 1 when p == r means that i == 1 + if starting_index == ending_index: + return array[starting_index] # 1 <= i <= r - p + 1 when p == r means that i == 1 - q = randomized_partition(array, p, r) + q = randomized_partition(array, starting_index, ending_index) - k = q - p + 1 + k = q - starting_index + 1 - if i == k: + if smallest_element == k: return array[q] # the pivot value is the answer - elif i < k: - return selection_sort(array, p, q - 1, i) + elif smallest_element < k: + return selection_sort(array, starting_index, q - 1, smallest_element) else: - return selection_sort(array, q + 1, r, i - k) + return selection_sort(array, q + 1, ending_index, smallest_element - k) if __name__ == "__main__": From 6b3d3cfa045913e7688421188c88c02e7a6c2fcc Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 14:57:57 +0000 Subject: [PATCH 08/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- divide_and_conquer/minimum_element_of_array.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index d05767873dd9..7e3f81ccabf7 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -67,7 +67,9 @@ def randomized_partition(array: list, starting_index: int, ending_index: int) -> return partition(array, starting_index, ending_index) -def selection_sort(array: list, starting_index: int, ending_index: int, smallest_element: int) -> list | None | Any: +def selection_sort( + array: list, starting_index: int, ending_index: int, smallest_element: int +) -> list | None | Any: """ Returns a list of sorted array elements using selection sort. Args: @@ -106,7 +108,9 @@ def selection_sort(array: list, starting_index: int, ending_index: int, smallest return array if starting_index == ending_index: - return array[starting_index] # 1 <= i <= r - p + 1 when p == r means that i == 1 + return array[ + starting_index + ] # 1 <= i <= r - p + 1 when p == r means that i == 1 q = randomized_partition(array, starting_index, ending_index) From 1b83b6e4d50cfd43c95e7f4a96c5935fd059e8ad Mon Sep 17 00:00:00 2001 From: Raffaele Grieco <62102593+Baraff24@users.noreply.github.com> Date: Thu, 5 Oct 2023 16:59:34 +0200 Subject: [PATCH 09/10] Update minimum_element_of_array.py Update line lenght --- .../minimum_element_of_array.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index 7e3f81ccabf7..d9a32ec80fef 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -16,7 +16,9 @@ from typing import Any -def partition(array: list, starting_index: int, ending_index: int) -> int: +def partition(array: list, + starting_index: int, + ending_index: int) -> int: """ Partition the array. Args: @@ -43,7 +45,9 @@ def partition(array: list, starting_index: int, ending_index: int) -> int: return i + 1 -def randomized_partition(array: list, starting_index: int, ending_index: int) -> int: +def randomized_partition(array: list, + starting_index: int, + ending_index: int) -> int: """ Randomized partition of the array. Args: @@ -67,16 +71,18 @@ def randomized_partition(array: list, starting_index: int, ending_index: int) -> return partition(array, starting_index, ending_index) -def selection_sort( - array: list, starting_index: int, ending_index: int, smallest_element: int -) -> list | None | Any: +def selection_sort(array: list, + starting_index: int, + ending_index: int, + smallest_element: int) -> list | None | Any: """ Returns a list of sorted array elements using selection sort. Args: array: list of elements starting_index: starting index of the array ending_index: ending index of the array - smallest_element: the ith smallest element of the array A[p: r], where 1 ≤ i ≤ r-p+1 + smallest_element: the ith smallest element of + the array A[p: r], where 1 ≤ i ≤ r-p+1 Returns: sorted array @@ -108,9 +114,8 @@ def selection_sort( return array if starting_index == ending_index: - return array[ - starting_index - ] # 1 <= i <= r - p + 1 when p == r means that i == 1 + # 1 <= i <= r - p + 1 when p == r means that i == 1 + return array[starting_index] q = randomized_partition(array, starting_index, ending_index) From 18c9a3388bc1635627d041da0d08ee30ec5ed53c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:01:10 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- divide_and_conquer/minimum_element_of_array.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/divide_and_conquer/minimum_element_of_array.py b/divide_and_conquer/minimum_element_of_array.py index d9a32ec80fef..98d4c30b7c43 100644 --- a/divide_and_conquer/minimum_element_of_array.py +++ b/divide_and_conquer/minimum_element_of_array.py @@ -16,9 +16,7 @@ from typing import Any -def partition(array: list, - starting_index: int, - ending_index: int) -> int: +def partition(array: list, starting_index: int, ending_index: int) -> int: """ Partition the array. Args: @@ -45,9 +43,7 @@ def partition(array: list, return i + 1 -def randomized_partition(array: list, - starting_index: int, - ending_index: int) -> int: +def randomized_partition(array: list, starting_index: int, ending_index: int) -> int: """ Randomized partition of the array. Args: @@ -71,10 +67,9 @@ def randomized_partition(array: list, return partition(array, starting_index, ending_index) -def selection_sort(array: list, - starting_index: int, - ending_index: int, - smallest_element: int) -> list | None | Any: +def selection_sort( + array: list, starting_index: int, ending_index: int, smallest_element: int +) -> list | None | Any: """ Returns a list of sorted array elements using selection sort. Args: