-
-
Notifications
You must be signed in to change notification settings - Fork 46.6k
Create count negative numbers in matrix algorithm #8813
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
cclauss
merged 21 commits into
TheAlgorithms:master
from
CaedenPH:create-count-negative-numbers-in-matrix
Jun 10, 2023
Merged
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH 05e435f
feat: Count negative numbers in sorted matrix
CaedenPH 83d877c
updating DIRECTORY.md
f0a785c
chore: Fix pre-commit
CaedenPH 8cd5cdc
refactor: Combine functions into iteration
CaedenPH ea5b2d0
style: Reformat reference
CaedenPH 0e346b2
feat: Add timings of each implementation
CaedenPH b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH 3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] fca6e9d
test: Remove doctest from benchmark function
CaedenPH 02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH 63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH 0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH 9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH 7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH 97e2017
refactor: Use sum instead of large iteration
CaedenPH 0f21b1b
refactor: Use len not sum
CaedenPH 605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss 21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
""" | ||
Given an `m x n` matrix grid which is sorted in non-increasing order | ||
both row-wise and column-wise, return the number of negative numbers in grid. | ||
|
||
Leetcode reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix | ||
""" | ||
|
||
|
||
def find_negative_index(array: list[int]) -> int: | ||
""" | ||
Find the smallest negative index | ||
|
||
>>> find_negative_index([0,0,0,0]) | ||
4 | ||
>>> find_negative_index([4,3,2,-1]) | ||
3 | ||
>>> find_negative_index([1,0,-1,-10]) | ||
2 | ||
>>> find_negative_index([0,0,0,-1]) | ||
3 | ||
>>> find_negative_index([11,8,7,-3,-5,-9]) | ||
3 | ||
>>> find_negative_index([-1,-1,-2,-3]) | ||
0 | ||
>>> find_negative_index([5,1,0]) | ||
3 | ||
>>> find_negative_index([-5,-5,-5]) | ||
0 | ||
>>> find_negative_index([0]) | ||
1 | ||
>>> find_negative_index([]) | ||
0 | ||
""" | ||
left = 0 | ||
right = len(array) - 1 | ||
|
||
# Edge cases such as no values or | ||
# all numbers are negative | ||
if not array or array[0] < 0: | ||
return 0 | ||
|
||
while right + 1 > left: | ||
mid = (left + right) // 2 | ||
num = array[mid] | ||
|
||
# Num must be negative and the index about num | ||
# must be greater than or equal to 0 | ||
if num < 0 and array[mid - 1] >= 0: | ||
return mid | ||
|
||
if num >= 0: | ||
left = mid + 1 | ||
else: | ||
right = mid - 1 | ||
# No negative numbers so return the last index | ||
# of the array + 1 which is also the length | ||
return len(array) | ||
|
||
|
||
def count_negatives_binary_search(grid: list[list[int]]) -> int: | ||
""" | ||
An O(m logn) solution that uses binary search | ||
in order to find the boundary between positive and | ||
negative numbers | ||
|
||
>>> count_negatives_binary_search( | ||
... [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]) | ||
8 | ||
>>> count_negatives_binary_search([[3,2],[1,0]]) | ||
0 | ||
>>> count_negatives_binary_search([[7,7,6]]) | ||
0 | ||
>>> count_negatives_binary_search([[7,7,6],[-1,-2,-3]]) | ||
3 | ||
""" | ||
total = 0 | ||
bound = len(grid[0]) | ||
|
||
for i in range(len(grid)): | ||
bound = find_negative_index(grid[i][:bound]) | ||
total += bound | ||
return (len(grid) * len(grid[0])) - total | ||
|
||
|
||
def count_negatives_brute_force(grid: list[list[int]]) -> int: | ||
""" | ||
This solution is O(n^2) because it iterates through | ||
every column and row. | ||
|
||
>>> count_negatives_brute_force( | ||
... [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]) | ||
8 | ||
>>> count_negatives_brute_force([[3,2],[1,0]]) | ||
0 | ||
>>> count_negatives_brute_force([[7,7,6]]) | ||
0 | ||
>>> count_negatives_brute_force([[7,7,6],[-1,-2,-3]]) | ||
3 | ||
""" | ||
total = 0 | ||
for m in range(len(grid)): | ||
for n in range(len(grid[m])): | ||
if grid[m][n] < 0: | ||
total += 1 | ||
return total | ||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int: | ||
""" | ||
Similar to the solution above, however uses break | ||
in order to reduce the number of iterations | ||
|
||
>>> count_negatives_brute_force_with_break( | ||
... [[4,3,2,-1],[3,2,1,-1],[1,1,-1,-2],[-1,-1,-2,-3]]) | ||
8 | ||
>>> count_negatives_brute_force_with_break([[3,2],[1,0]]) | ||
0 | ||
>>> count_negatives_brute_force_with_break([[7,7,6]]) | ||
0 | ||
>>> count_negatives_brute_force_with_break([[7,7,6],[-1,-2,-3]]) | ||
3 | ||
""" | ||
total = 0 | ||
length_of_n = len(grid[0]) | ||
for m in range(len(grid)): | ||
for index, n in enumerate(range(length_of_n)): | ||
if grid[m][n] < 0: | ||
total += length_of_n - index | ||
break | ||
return total | ||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
def generate_large_matrix() -> list[list[int]]: | ||
""" | ||
>>> generate_large_matrix() # doctest: +ELLIPSIS | ||
[[1000, ..., -999], [999, ..., -1001], ..., [2, ..., -1998]] | ||
""" | ||
return [list(range(1000 - i, -1000 - i, -1)) for i in range(1000)] | ||
|
||
|
||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def benchmark() -> None: | ||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"""Benchmark our functions next to each other""" | ||
from timeit import timeit | ||
|
||
print("Running benchmarks") | ||
setup = ( | ||
"from __main__ import count_negatives_binary_search,count_negatives_brute_force" | ||
",count_negatives_brute_force_with_break,generate_large_matrix" | ||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
for func in ( | ||
"count_negatives_binary_search", # 175.51 seconds | ||
"count_negatives_brute_force_with_break", # 271.04 seconds | ||
"count_negatives_brute_force", # 646.65 seconds | ||
): | ||
time = timeit(f"{func}(generate_large_matrix())", setup=setup, number=5000) | ||
CaedenPH marked this conversation as resolved.
Show resolved
Hide resolved
|
||
print(f"{func}() took {time} seconds") | ||
|
||
|
||
if __name__ == "__main__": | ||
import doctest | ||
|
||
doctest.testmod() | ||
|
||
benchmark() |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a
validate_matrix(matrix)
function? We do not need to run it everywhere but we should have it if we want to validate test input.