Esercizio 6

Mappa d’intensità luminosa con una matrice numpy 2-D

Enunciato del problema

Dato il lato N di una stanza quadrata e le coordinate di più punti-luce, calcolare l’intensità dell’illuminazione su ogni piastrella del pavimento.

Ogni punto emette il seguente schema 5 x 5 centrato sulla propria piastrella.

scarto-riga -2 -1 0 +1 +2
-2 0.2 0.2 0.2 0.2 0.2
-1 0.2 0.5 0.5 0.5 0.2
0 0.2 0.5 1.0 0.5 0.2
+1 0.2 0.5 0.5 0.5 0.2
+2 0.2 0.2 0.2 0.2 0.2

I contributi provenienti da lampade diverse si sommano; le piastrelle sui bordi ricevono solamente la porzione del kernel che rimane dentro la stanza.

Motivazione metodologica

Risoluzione passo-passo

import numpy as np

def load_spot_file(path: str):
	"""Read N and a list of (row, col) integer pairs from text file."""
	with open(path, 'r') as f:
		# Read the first line separately to get N
		N = int(f.readline().strip())
		# Read the rest of the file for coordinates
		coords = np.loadtxt(f, dtype=int)
	return N, coords
	
# 5 x 5 kernel (centre at index [2,2])
KERNEL = np.array([[0.2, 0.2, 0.2, 0.2, 0.2],
								   [0.2, 0.5, 0.5, 0.5, 0.2],
								   [0.2, 0.5, 1.0, 0.5, 0.2],
								   [0.2, 0.5, 0.5, 0.5, 0.2],
								   [0.2, 0.2, 0.2, 0.2, 0.2]], dtype=float)
									 
def light_map(N, spots):
	room = np.zeros((N, N), dtype=float)
	
	for r, c in spots:
		# boundaries of the full 5x5 kernel centred at (r,c)
		r0, r1 = r - 2, r + 3 # Python slices are half-open
		c0, c1 = c - 2, c + 3
		
		# clip to room borders
		rs, re = max(r0, 0), min(r1, N)
		cs, ce = max(c0, 0), min(c1, N)
		
		# corresponding indices inside the kernel
		ks = rs - r0
		ke = re - r0
		ls = cs - c0
		le = ce - c0
		
		room[rs:re, cs:ce] += KERNEL[ks:ke, ls:le]
		
	return room
	
def print_map(M):
	"""Pretty-print with one decimal digit"""
	for row in M:
		print(" ".join(f"{v:0.1f}" for v in row))
		
# Example
N, coords = load_spot_file("spotlights.txt")
print_map(light_map(N, coords))

Punti chiave

  1. Sovrapposizione dei kernel - l’istruzione room[slice] += KERNEL[subkernel] è l’unica operazione vettorializzata; nessun ciclo Python sulle singole piastrelle.
  2. Slicing con controllo dei bordi - il ritaglio impedisce di uscire dall’array; la porzione di kernel sommata è ottenuta con gli stessi offset.
  3. Complessità - Sia S il numero di spot-light; ogni aggiornamento tocca al massimo 25 elementi, quindi l’algoritmo è O(S) in tempo e O(N²) in memoria.

Interpretazione dei risultati

Eseguendo lo script con l’input di esempio

7
0 0
2 3
4 3