Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Create count negative numbers in matrix algorithm by CaedenPH · Pull Request #8813 · TheAlgorithms/Python · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e14e0fb
updating DIRECTORY.md
Jun 7, 2023
2ba4830
Merge branch 'TheAlgorithms:master' into master
CaedenPH Jun 8, 2023
05e435f
feat: Count negative numbers in sorted matrix
CaedenPH Jun 8, 2023
83d877c
updating DIRECTORY.md
Jun 8, 2023
f0a785c
chore: Fix pre-commit
CaedenPH Jun 8, 2023
8cd5cdc
refactor: Combine functions into iteration
CaedenPH Jun 8, 2023
ea5b2d0
style: Reformat reference
CaedenPH Jun 8, 2023
0e346b2
feat: Add timings of each implementation
CaedenPH Jun 8, 2023
b58a340
chore: Fix problems with algorithms-keeper bot
CaedenPH Jun 8, 2023
3b16704
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 8, 2023
fca6e9d
test: Remove doctest from benchmark function
CaedenPH Jun 8, 2023
02cd4f3
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
63c5147
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 9, 2023
a55db73
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
0d8a616
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
9a7719c
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
7a2f01e
Update matrix/count_negative_numbers_in_sorted_matrix.py
CaedenPH Jun 10, 2023
97e2017
refactor: Use sum instead of large iteration
CaedenPH Jun 10, 2023
0f21b1b
refactor: Use len not sum
CaedenPH Jun 10, 2023
605be43
Update count_negative_numbers_in_sorted_matrix.py
cclauss Jun 10, 2023
21dbc02
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DIRECTORY.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -679,6 +679,7 @@
## Matrix
* [Binary Search Matrix](matrix/binary_search_matrix.py)
* [Count Islands In Matrix](matrix/count_islands_in_matrix.py)
* [Count Negative Numbers In Sorted Matrix](matrix/count_negative_numbers_in_sorted_matrix.py)
* [Count Paths](matrix/count_paths.py)
* [Cramers Rule 2X2](matrix/cramers_rule_2x2.py)
* [Inverse Of Matrix](matrix/inverse_of_matrix.py)
Expand DownExpand Up@@ -753,6 +754,7 @@
* [Potential Energy](physics/potential_energy.py)
* [Rms Speed Of Molecule](physics/rms_speed_of_molecule.py)
* [Shear Stress](physics/shear_stress.py)
* [Speed Of Sound](physics/speed_of_sound.py)

## Project Euler
* Problem 001
Expand Down
151 changes: 151 additions & 0 deletions matrix/count_negative_numbers_in_sorted_matrix.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
"""
Given an matrix of numbers in which all rows and all columns are sorted in decreasing
order, return the number of negative numbers in grid.

Reference: https://leetcode.com/problems/count-negative-numbers-in-a-sorted-matrix
"""


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)]


grid = generate_large_matrix()
test_grids = (
[[4, 3, 2, -1], [3, 2, 1, -1], [1, 1, -1, -2], [-1, -1, -2, -3]],
[[3, 2], [1, 0]],
[[7, 7, 6]],
[[7, 7, 6], [-1, -2, -3]],
grid,
)


def validate_grid(grid: list[list[int]]) -> None:
"""
Validate that the rows and columns of the grid is sorted in decreasing order.
>>> for grid in test_grids:
... validate_grid(grid)
"""
assert all(row == sorted(row, reverse=True) for row in grid)
assert all(list(col) == sorted(col, reverse=True) for col in zip(*grid))


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 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 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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
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(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
return len([number for row in grid for number in row if number < 0])


def count_negatives_brute_force_with_break(grid: list[list[int]]) -> int:
"""
Similar to the brute force solution above but uses break in order to reduce the
number of iterations.

>>> [count_negatives_brute_force_with_break(grid) for grid in test_grids]
[8, 0, 0, 3, 1498500]
"""
total = 0
for row in grid:
for i, number in enumerate(row):
if number < 0:
total += len(row) - i
break
return total


def benchmark() -> None:
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as resolved.
Comment thread
CaedenPH marked this conversation as 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, grid"
)
for func in (
"count_negatives_binary_search", # took 0.7727 seconds
"count_negatives_brute_force_with_break", # took 4.6505 seconds
"count_negatives_brute_force", # took 12.8160 seconds
):
time = timeit(f"{func}(grid=grid)", setup=setup, number=500)
print(f"{func}() took {time:0.4f} seconds")


if __name__ == "__main__":
import doctest

doctest.testmod()
benchmark()