avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • Resume
  • Archives
  • Categories
  • Photos
  • Music



{{ date }}

{{ time }}

avatar
Siz Long

My name is Siz. I am a computer science graduate student specializing in backend development with Golang and Python, seeking opportunities in innovative tech projects. My personal website is me.longsizhuo.com .Connect with me on LinkedIn: https://www.linkedin.com/in/longsizhuo/.

  • 主页
  • Resume
  • Archives
  • Categories
  • Photos
  • Music

9021_TUT_6

  2025-03-02        
字数统计: 2.7k字   |   阅读时长: 16min

Exercise 1

Problem description

It is hard to find the pattern of the input and output. But through this example:

1
2
3
4
5
6
statements = 'from exercise_6_1 import f1; '\
'L = [[4, 8], [6, 3, 0], [7]]; print(f1(L))'

%%run_and_test python3 -c "$statements"

'([0, 3, 4, 6, 7, 8], [[0, 3], [4, 6, 7], [8]])\n'

We need to sort the elements into a one-dimensional list and then split them according to the lengths of the sublists in the input.

My Solution

So, the function f1 should be like this:

1
2
3
4
5
6
7
8
9
10
11
12
def f1(L):
# Calculate the length of each sublist
lengths = [len(i) for i in L]
# Split the list into a one-dimensional list
P = [i for j in L for i in j]
P.sort()
ans = []
flag = 0
for i in range(len(L)):
ans.append(P[flag: flag + lengths[i]])
flag += lengths[i]
return P, ans

Standard Solution

1
2
3
4
5
6
7
8
def f1(L):
lengths = [len(L1) for L1 in L]
F = sorted(e for L1 in L for e in L1)
R = []
i = 0
for n in range(len(L)):
R.append(F[i : (i := i + lengths[n])])
return F, R

Explanation

The difference between the two solutions is that the standard solution uses the walrus operator := to simplify the code. The walrus operator is a new feature in Python 3.8. It is used to assign a value to a variable as part of an expression. It is useful when you want to assign a value to a variable and use that value in the same expression.

Exercise 2

Problem description

This exercise ask us to output the sum of diagonal elements of a matrix. Based on the line where i, j are located.
We can use Numpy to solve this problem. It has a function numpy.diagonal() that can return the diagonal elements of a matrix.

My Solution

1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

def f2(L, i, j, major=True):
# Convert L to a numpy array for easier indexing
L = np.array(L)
i -= 1
j -= 1
if major:
dia = sum(np.diag(L, k= j - i))
else:
dia = sum(np.diag(np.fliplr(L), k= (len(L) - 1 - j) - i))
return dia

Explanation

In a normal matrix, the np.diag() function allows extracting diagonals at different offsets, where the offset k=0 represents the main diagonal.

When we flip the matrix, the coordinates for each cell change in such a way that we need to adjust our calculation of the offset accordingly.

Specifically, for a matrix of size n, flipping it means that the column index j of the original matrix transforms to (n - 1 - j) in the flipped version. Hence, when we want to calculate the offset of the diagonal that passes through (i, j) in the original matrix, we need to determine the offset using (n - 1 - j) - i to correctly represent the position in the flipped version.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def f2(L, i, j, major=True):
i1 = i = i - 1
j1 = j = j - 1
the_sum = L[i1][j1]
if major:
while (i1 := i1 - 1) >= 0 and (j1 := j1 - 1) >= 0:
the_sum += L[i1][j1]
i1, j1 = i, j
while (i1 := i1 + 1) < len(L) and (j1 := j1 + 1) < len(L):
the_sum += L[i1][j1]
else:
while (i1 := i1 - 1) >= 0 and (j1 := j1 + 1) < len(L):
the_sum += L[i1][j1]
i1, j1 = i, j
while (i1 := i1 + 1) < len(L) and (j1 := j1 - 1) >= 0:
the_sum += L[i1][j1]
return the_sum

Explanation

The standard solution uses a while loop to iterate through the elements of the matrix based on the given indices i and j. It calculates the sum of the diagonal elements by moving in the specified direction (major or minor diagonal) and updating the sum accordingly.

Exercise 3

Problem description

This exercise involves swapping elements in a square matrix with an even side length such that each element in a specified half (‘top’, ‘bottom’, ‘left’, or ‘right’) is at least equal to its diagonally opposite element.

If the half is ‘top’ or ‘left’, the element in that half should be at least equal to the diagonally opposite one, otherwise they should be swapped. Conversely, if the half is ‘bottom’ or ‘right’, the element in the other half should be swapped if it is greater.

Standard Solution

The solution is implemented by creating a copy of the matrix and then iterating over the relevant half to determine if swapping is necessary based on the given criteria.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Assume that the argument L is a list of lists of integers
# that displays as a square with an even side length
# and the argument half is one of
# 'top', 'bottom', 'left' or 'right'.

# Possibly swaps elements that are diagonally opposite
# so that the element in the "half" part of the square is
# at least equal to the other element.

def f3(L, half='top'):
# Create a copy of the original list to avoid modifying it directly
L1 = [list(row) for row in L]
n = len(L)

# Define ranges based on the specified half
ranges = {
'top': (range(n // 2), range(n)),
'bottom': (range(n // 2, n), range(n)),
'left': (range(n), range(n // 2)),
'right': (range(n), range(n // 2, n))
}

# Iterate through the specified half and possibly swap diagonally opposite elements
for i in ranges[half][0]:
for j in ranges[half][1]:
if L[i][j] < L[-i - 1][-j - 1]:
L1[i][j], L1[-i - 1][-j - 1] = L1[-i - 1][-j - 1], L1[i][j]

return L1

Explanation

The approach in my solution uses a dictionary to define the ranges for each half of the matrix. By iterating over these ranges, it ensures that only the elements in the specified half are considered. If the element in the specified half is smaller than its diagonally opposite counterpart, they are swapped.

The ranges dictionary defines which rows and columns are to be iterated based on the specified half:

  • 'top' considers the top half of the matrix (rows from 0 to n//2)
  • 'bottom' considers the bottom half of the matrix (rows from n//2 to n)
  • 'left' considers the left half of the matrix (columns from 0 to n//2)
  • 'right' considers the right half of the matrix (columns from n//2 to n)

The values are swapped only if they do not meet the condition defined for the given half.

Exercise 4

Problem description

This exercise involves creating a visual pattern on an n x n grid using two different characters to represent black and white cells. The argument n is an integer at least equal to 1, and the argument black_center is either True or False, which influences the color of the center of the grid. The function should use np.full() to create the grid and modify it using simple loops without nested loops.

The function prints the grid instead of returning it.

My Solution

The solution starts by creating an n x n grid initialized entirely to either black or white, depending on whether the center should be black or not. The function then iteratively adjusts concentric squares within the grid, switching the colors as needed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

def black(i, black_centre):
return (i % 4 in {1, 2}) ^ (not black_centre)

def f4(n, black_centre=True):
# Create the initial grid based on the color of the center
grid = np.full((n, n), '⬛') if black(n, black_centre)\
else np.full((n, n), '⬜')
# Adjust concentric squares within the grid
for i in range(1, n // 2 + 1):
grid[i : n - i, i : n - i] = '⬛' if black(n - 2 * i, black_centre)\
else '⬜'
# Print the grid row by row
for row in grid:
print(*row, sep='')

Explanation

  • The function black(i, black_centre) determines the color of the concentric square based on the current size i and whether the center should be black (black_centre argument).
  • The np.full() function is used to create the grid, either filled with black (‘⬛’) or white (‘⬜’), depending on the value of black(n, black_centre), which determines the color of the center.
  • The for loop iterates through half of the matrix (n // 2), adjusting each concentric square layer by layer.
    • The slicing operation grid[i : n - i, i : n - i] selects the relevant inner square and fills it with either black or white, alternating colors as determined by black(n - 2 * i, black_centre).
  • Finally, the grid is printed row by row, with each character printed consecutively for easy visualization.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

def black(i, black_centre):
return (i % 4 in {1, 2}) ^ (not black_centre)

def f4(n, black_centre=True):
# Create the grid, starting with the initial color based on the center
grid = np.full((n, n), '⬛' if black(n, black_centre) else '⬜')
# Iterate to adjust each concentric layer
for i in range(1, n // 2 + 1):
color = '⬛' if black(n - 2 * i, black_centre) else '⬜'
grid[i:n-i, i:n-i] = color
# Print the grid
for row in grid:
print(''.join(row))

Explanation

The standard solution follows a similar approach to my solution but uses slightly different syntax to achieve the same result:

  • The grid is created using np.full(), just like in my solution.
  • The loop iterates through each concentric layer, updating the color accordingly. The variable color is used to determine what color each inner square should be, making the assignment more readable.
  • The grid is printed in a slightly different way by joining the row’s elements into a single string (print(''.join(row))). This makes the output cleaner by removing the spaces between characters, which is purely aesthetic.

The primary differences between my solution and the standard solution are the use of inline expressions for color selection and minor differences in how the final output is formatted when printed.

Exercise 5

Try it!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import numpy as np

# Define a 2D NumPy array (matrix)
array = np.array([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]])

# Sum all elements
total_sum = np.sum(array) # Output: 45

# Sum along rows (axis=1)
row_sums = np.sum(array, axis=1) # Output: [6, 15, 24]

# Sum along columns (axis=0)
column_sums = np.sum(array, axis=0) # Output: [12, 15, 18]

print(total_sum, row_sums, column_sums)

Problem description

The exercise is to compute the sum of elements in a given row and column and display a colored square at their intersection based on the sign of the sum:

  • A blue square (‘🟦’) if the sum is 0.
  • A green square (‘🟩’) if the sum is strictly positive.
  • A red square (‘🟥’) if the sum is strictly negative.

For example, given a list of lists representing the matrix:

1
2
3
.  .  2  .  .
2 0 -3 7 -4
. . -4 . .

The function should compute the sum for each row and column, and update the intersection elements accordingly.

The function should use np.sum() and np.sign() to simplify calculations, and should use at most loops within loops, avoiding deeper nesting.

The output is printed, not returned.

My Solution

The solution uses NumPy to calculate the sum of each row and column. The function then iterates through the given list and determines the sign of the sum at each intersection, displaying the appropriate color.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import numpy as np

def f5(L):
# Convert L to a numpy array for easier manipulation
L = np.array(L)
n_rows, n_cols = L.shape

# Calculate row and column sums
row_sums = np.sum(L, axis=1)
col_sums = np.sum(L, axis=0)

# Iterate through the list and determine the color for each intersection
for i in range(n_rows):
row = []
for j in range(n_cols):
intersection_sum = row_sums[i] + col_sums[j] - L[i][j] # Avoid double counting L[i][j]
if intersection_sum == 0:
row.append('🟦') # Blue square
elif intersection_sum > 0:
row.append('🟩') # Green square
else:
row.append('🟥') # Red square
print(''.join(row))

# Test with the provided statements
statements = 'from exercise_6_5 import f5; '\
'f5([[4, -6, -5], [6, -7, 6]])'

%%run_and_test python3 -c "$statements"

'🟥🟥🟥\n'
'🟩🟥🟦\n'

Explanation

  • NumPy Conversion: The list L is converted to a NumPy array to take advantage of the efficient np.sum() function for computing sums.
  • Row and Column Sums: The sums for rows and columns are computed separately using np.sum() with the appropriate axis argument.
  • Iteration for Colors: The function then iterates over each element in the matrix to determine the color of the intersection based on the calculated row and column sums. The color is chosen as follows:
    • '🟦' (blue) for a sum of 0.
    • '🟩' (green) for a strictly positive sum.
    • '🟥' (red) for a strictly negative sum.
  • Output: Finally, the grid is printed row by row.

Standard Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

def f5(L):
L1 = np.array(L)
for i in range(L1.shape[0]):
for j in range(L1.shape[1]):
L[i][j] = { 0: '🟦',
1: '🟩',
-1: '🟥'
}[np.sign(np.sum(L1[i, :])
+ np.sum(L1[: , j])
- L1[i, j]
)
]
for row in L:
print(*row, sep='')

Explanation

The standard solution follows a similar approach to my solution but uses a dictionary to map the sign of the sum to the corresponding color. The np.sign() function is used to determine the sign of the sum, and the dictionary is used to select the appropriate color based on the sign.

Exercise 6

Problem description

This exercise is similar to Exercise 5, where we compute the sum of elements in a given row and column and display a colored square at their intersection based on the sign of the sum:

  • A blue square (‘🟦’) if the sum is 0.
  • A green square (‘🟩’) if the sum is strictly positive.
  • A red square (‘🟥’) if the sum is strictly negative.

However, the main differences are:

  • Input Structure: The list L must be a square matrix (i.e., the number of rows equals the number of columns).
  • Optimization Requirement: The solution for f6 must avoid manual loops for computation (except for output). Instead, the solution must use vectorized operations in NumPy to enhance computational efficiency.
  • Advanced NumPy Usage: The exercise explicitly suggests using advanced NumPy functions such as np.vectorize(), np.newaxis, and broadcasting.

The output is printed, not returned.

My Solution

The solution for f6 uses advanced NumPy techniques to fully vectorize the computations without using explicit loops (except for printing). It creates the required colored grid by computing the sum for each row and column and then using a vectorized mapping to determine the color.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

def f6(L):
L1 = np.array(L)
# Vectorize the color selection based on the sum of row and column
for row in np.vectorize(lambda e: { 0: '🟦',
1: '🟩',
-1: '🟥'
}[e]
)(np.sign(np.sum(L1, axis=1)[:, np.newaxis]
+ np.sum(L1.T, axis=1)
- L1
)
):
print(*row, sep='')

Explanation

  • Vectorization: The solution uses np.vectorize() to apply a function element-wise over an array, effectively mapping each sum to a specific color.
  • Avoiding Loops: Instead of iterating through each element manually, the solution leverages NumPy’s vectorization to calculate row and column sums simultaneously.
  • Broadcasting and np.newaxis: The use of [:, np.newaxis] helps in broadcasting the row sums across the columns to facilitate efficient addition with the column sums. This allows the entire computation to be performed without explicit looping.
  • Output: Finally, each row is printed with the appropriate color, similar to Exercise 5.

Key Differences from Exercise 5

  1. Input Structure: Exercise 5 allows any list of lists, whereas Exercise 6 requires a square matrix.
  2. Loop Usage: Exercise 5 uses explicit loops to calculate intersection sums, whereas Exercise 6 relies entirely on vectorized operations and avoids manual loops (except for printing).
  3. Efficiency: Exercise 6 is more computationally efficient, as it is designed to handle larger datasets using optimized NumPy operations, whereas Exercise 5 uses loops that can be slower for large matrices.
  • Python
  • 9021

扫一扫,分享到微信

微信分享二维码
9021_TUT_7
9021_TUT_2
目录
  1. 1. Exercise 1
    1. 1.0.1. Problem description
    2. 1.0.2. My Solution
    3. 1.0.3. Standard Solution
    4. 1.0.4. Explanation
  • 2. Exercise 2
    1. 2.0.1. Problem description
    2. 2.0.2. My Solution
    3. 2.0.3. Explanation
    4. 2.0.4. Standard Solution
    5. 2.0.5. Explanation
  • 3. Exercise 3
    1. 3.0.1. Problem description
    2. 3.0.2. Standard Solution
    3. 3.0.3. Explanation
  • 4. Exercise 4
    1. 4.0.1. Problem description
    2. 4.0.2. My Solution
    3. 4.0.3. Explanation
    4. 4.0.4. Standard Solution
    5. 4.0.5. Explanation
  • 5. Exercise 5
    1. 5.0.1. Try it!
    2. 5.0.2. Problem description
    3. 5.0.3. My Solution
    4. 5.0.4. Explanation
    5. 5.0.5. Standard Solution
    6. 5.0.6. Explanation
  • 6. Exercise 6
    1. 6.0.1. Problem description
    2. 6.0.2. My Solution
    3. 6.0.3. Explanation
    4. 6.0.4. Key Differences from Exercise 5

  • 150 篇 | 131.7k
    次 | 人
    这里自动载入天数这里自动载入时分秒
    2022-2025 loong loong | 新南威尔士龙龙号