Skip to content

Commit 03e8a32

Browse files
committed
refactor(arrays, matrix, puzzles): simulation algorithm for lucky numbers
1 parent 1e71628 commit 03e8a32

File tree

4 files changed

+123
-4
lines changed

4 files changed

+123
-4
lines changed

puzzles/arrays/lucky_numbers_in_a_matrix/README.md

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Constraints
1818
![Example 2](./images/examples/lucky_numbers_in_a_matrix_example_2.png)
1919
![Example 3](./images/examples/lucky_numbers_in_a_matrix_example_3.png)
2020

21-
## Solution
21+
## Solution: Greedy
2222

2323
The core idea behind the solution is to recognize that there can be, at most, one lucky number in the matrix. This is
2424
proven by contradiction, as having two such numbers would violate the unique conditions for being a lucky number.
@@ -45,7 +45,11 @@ proven by contradiction, as having two such numbers would violate the unique con
4545
> Therefore `y` > `x`
4646
>
4747
> This leads to a contradiction, as we deduced `y<x` and `y>x`. This inconsistency implies that our initial assumption—
48-
> that y is a lucky number—is incorrect. Therefore, only x can be the lucky number in this configuration.
48+
> that y is a lucky number—is incorrect. Therefore, only x can be the lucky number in this configuration.
49+
>
50+
> Visually, this looks like this:
51+
>
52+
> ![Proof by contradiction](./images/solutions/lucky_numbers_proof_of_contradiction.png)
4953
5054
This problem can be solved using a greedy algorithm that analyzes the matrix row by row and column by column.
5155

@@ -72,7 +76,8 @@ Following are the detailed steps of the algorithm that we have just discussed:
7276

7377
- We calculate the maximum value of the column (c_max) by iterating over all rows.
7478
- Next, we update c_max_min to the minimum of c_smallest_max and c_max.
75-
- The above steps ensure we consider only maximum values in their columns, further narrowing the candidate set for a lucky number.
79+
- The above steps ensure we consider only maximum values in their columns, further narrowing the candidate set for a
80+
lucky number.
7681

7782
4. Finally, we compare whether r_largest_min equals c_smallest_max. If TRUE, we return the value stored in [r_largest_min].
7883
Otherwise, we return an empty array []. The comparison ensures that the identified value satisfies both conditions of
@@ -103,3 +108,39 @@ rows in the matrix.
103108
### Space Complexity
104109

105110
The solution’s space complexity is O(1) as no extra space is required apart from the few variables.
111+
112+
## Solution: Simulation
113+
114+
We are given a matrix of size MXN with distinct integers. We need to return the list of lucky numbers in the matrix.
115+
An integer in the matrix is lucky if it is the maximum integer in its column and it is the minimum value in its row.
116+
117+
In this approach, we will simulate the process by iterating over each integer in the matrix, checking if it is the
118+
maximum in its row and the minimum in its column. If it meets both criteria, we will add it to the list of lucky numbers,
119+
luckyNumbers.
120+
121+
The naive approach to check the criteria for each integer involves iterating over each integer in the current row and
122+
column to verify the minimum and maximum criteria, requiring M+N operations per integer. A more efficient method is to
123+
precompute the minimum of each row and the maximum of each column before processing the matrix. This allows us to check
124+
the criteria for each integer in constant time. We iterate over each row to store the minimum in rowMin and each column
125+
to store the maximum in colMax.
126+
127+
### Algorithm
128+
129+
1. Iterate over each row and store the minimum of the ith row at the ith position in the list rowMin.
130+
2. Iterate over each column and store the maximum of the ith column at the ith position in the list colMax.
131+
3. Iterate over each integer in the matrix and for each integer at (i, j), check if the integer is equal to rowMin[i]
132+
and colMax[j]. If yes, add it to the list luckyNumbers.
133+
4. Return luckyNumbers.
134+
135+
### Complexity Analysis
136+
137+
Here, N is the number of rows in the matrix and M is the number of columns in the matrix.
138+
139+
#### Time complexity: O(N*M).
140+
141+
To store the maximum of each row, we require N*M operations and the same for strong the maximum of each column. In the
142+
end, to find the lucky numbers we again iterate over each integer. Hence, the total time complexity is equal to O(N*M).
143+
144+
#### Space complexity: O(N+M).
145+
146+
We require two lists, rowMin and colMax of size N and M respectively. Hence the total space complexity is equal to O(N+M).

puzzles/arrays/lucky_numbers_in_a_matrix/__init__.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,48 @@ def lucky_numbers(matrix: List[List[int]]) -> List[int]:
5252
return [r_largest_min]
5353
# Otherwise, return an empty list indicating no luky number exists
5454
return []
55+
56+
57+
def lucky_numbers_simulation(matrix: List[List[int]]) -> List[int]:
58+
"""
59+
This function takes a matrix as input and returns a list containing all the lucky number(s) if they exist.
60+
61+
A lucky number is a number that is the maximum of the minimum values from each row and the minimum of the maximum
62+
values from each column.
63+
64+
The function first calculates the minimum values from each row and the maximum values from each column, then compares
65+
these two lists to find the lucky number(s).
66+
67+
Time Complexity: O(m * n) where m is the number of columns and n is the number of rows in the matrix.
68+
Space Complexity: O(m + n) as we are using two lists of size m and n respectively.
69+
70+
Args:
71+
matrix (List[List[int]]): The input matrix.
72+
73+
Returns:
74+
List[int]: A list containing all the lucky number(s) if they exist, otherwise an empty list.
75+
"""
76+
row_length = len(matrix)
77+
col_length = len(matrix[0])
78+
79+
row_min = []
80+
for i in range(row_length):
81+
r_min = float('inf')
82+
for j in range(col_length):
83+
r_min = min(r_min, matrix[i][j])
84+
row_min.append(r_min)
85+
86+
col_max = []
87+
for i in range(col_length):
88+
c_max = float('-inf')
89+
for j in range(row_length):
90+
c_max = max(c_max, matrix[j][i])
91+
col_max.append(c_max)
92+
93+
result = []
94+
for i in range(row_length):
95+
for j in range(col_length):
96+
if matrix[i][j] == row_min[i] and matrix[i][j] == col_max[j]:
97+
result.append(matrix[i][j])
98+
99+
return result
234 KB
Loading

puzzles/arrays/lucky_numbers_in_a_matrix/test_lucky_numbers_in_a_matrix.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import unittest
22
from typing import List
33
from parameterized import parameterized
4-
from puzzles.arrays.lucky_numbers_in_a_matrix import lucky_numbers
4+
from puzzles.arrays.lucky_numbers_in_a_matrix import lucky_numbers, lucky_numbers_simulation
55

66

77
class LuckyNumbersInAMatrixTestCase(unittest.TestCase):
@@ -27,6 +27,8 @@ class LuckyNumbersInAMatrixTestCase(unittest.TestCase):
2727
([[30, 20, 10], [40, 50, 60], [70, 80, 90]], [70]),
2828
([[5, 1, 9], [10, 8, 2], [7, 3, 6]], []),
2929
([[22, 11], [88, 77], [55, 44]], [77]),
30+
([[1,10,4,2],[9,3,8,7],[15,16,17,12]], [12]),
31+
([[7,8],[1,2]], [7]),
3032
]
3133
)
3234
def test_lucky_numbers_in_matrix(
@@ -35,6 +37,37 @@ def test_lucky_numbers_in_matrix(
3537
actual = lucky_numbers(matrix)
3638
self.assertEqual(expected, actual)
3739

40+
@parameterized.expand(
41+
[
42+
([[3, 7, 8], [9, 11, 13], [15, 16, 17]], [15]),
43+
([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [7]),
44+
(
45+
[
46+
[10, 20, 30, 40],
47+
[5, 25, 35, 50],
48+
[60, 70, 80, 90],
49+
[100, 110, 120, 130],
50+
],
51+
[100],
52+
),
53+
(
54+
[[12, 18, 23, 50], [5, 16, 25, 45], [4, 15, 26, 48], [3, 14, 27, 60]],
55+
[12],
56+
),
57+
([[5]], [5]),
58+
([[30, 20, 10], [40, 50, 60], [70, 80, 90]], [70]),
59+
([[5, 1, 9], [10, 8, 2], [7, 3, 6]], []),
60+
([[22, 11], [88, 77], [55, 44]], [77]),
61+
([[1,10,4,2],[9,3,8,7],[15,16,17,12]], [12]),
62+
([[7,8],[1,2]], [7]),
63+
]
64+
)
65+
def test_lucky_numbers_in_matrix_simulation(
66+
self, matrix: List[List[int]], expected: List[int]
67+
):
68+
actual = lucky_numbers_simulation(matrix)
69+
self.assertEqual(expected, actual)
70+
3871

3972
if __name__ == "__main__":
4073
unittest.main()

0 commit comments

Comments
 (0)