5 Best Ways to Implement Grid Illumination in Python

πŸ’‘ Problem Formulation: Imagine you have a grid with lamps at certain coordinates that illuminate their row, column, and diagonals. Given the position of the lamps and queries, you want to check if certain points are illuminated, and then toggle the lamps on or off accordingly. For instance, if lamps are at [(1,1), (4,4)], and a query asks about (1,4), the desired output would tell us the point is illuminated.

Method 1: Using Dictionary to Track Lamps’ States and Calculating Illuminations On-the-Fly

This method involves using a dictionary to keep track of the on/off state of each lamp. Queries are processed by checking rows, columns, and diagonals relative to each query point to determine if the point is illuminated. This method is flexible and allows for dynamic updates to lamp states.

Here’s an example:

def is_illuminated(grid_size, lamps, queries):
    illuminated = {}

    # Set initial states of lamps
    for lamp in lamps:
        illuminated[lamp] = True

    def check_illumination(query):
        row, col = query
        for dr in (-1, 0, 1):
            for dc in (-1, 0, 1):
                if (row+dr, col+dc) in illuminated:
                    return True
        return False

    # Process queries
    results = []
    for query in queries:
        results.append(check_illumination(query))

    return results

# Define lamps and queries
lamps = [(1, 1), (4, 4)]
queries = [(1, 1), (1, 4), (4, 4), (3, 2)]
print(is_illuminated(5, lamps, queries))

The output of this code snippet would be:

[True, True, True, False]

This code snippet defines a function is_illuminated that takes the size of the grid, a list of lamps, and a list of queries. It checks whether each query point is illuminated by any lamp. The output is a list of boolean values corresponding to each query point’s illumination status.

Method 2: Precomputing Illumination for All Cells

Precomputing the illumination implies that before processing any queries, the algorithm determines the illumination state of every cell based on the initial lamp positions. This approach is efficient for grids with many queries, as the actual query handling becomes a simple lookup task.

Here’s an example:

def precompute_illumination(grid_size, lamps):
    grid = [[False] * grid_size for _ in range(grid_size)]
    for lamp in lamps:
        x, y = lamp
        for i in range(grid_size):
            grid[x][i] = grid[i][y] = True
        for i in range(-grid_size, grid_size):
            if 0 <= x + i < grid_size and 0 <= y + i < grid_size:
                grid[x + i][y + i] = True
            if 0 <= x + i < grid_size and 0 <= y - i < grid_size:
                grid[x + i][y - i] = True
    return grid

# Define lamps
lamps = [(1, 1), (4, 4)]
grid = precompute_illumination(5, lamps)

# Example query
print(grid[1][1], grid[1][4])

The output of this code snippet will be:

True True

This code snippet computes the illumination status for an entire grid based on the initial positions of the lamps. The resultant grid is a 2D list where each cell’s value indicates whether it is illuminated. The output confirms that both points from the example query are indeed illuminated.

Method 3: Using Set Operations for Fast Illumination Detection

A set-based approach leverages the fast lookup, insertion, and deletion properties of sets in Python. Illuminations are tracked using multiple sets for rows, columns, and diagonals, which allows for efficient query processing by checking membership in these sets.

Here’s an example:

def set_based_illumination(grid_size, lamps, queries):
    rows, cols, diag1, diag2 = set(), set(), set(), set()

    for x, y in lamps:
        rows.add(x)
        cols.add(y)
        diag1.add(x - y)
        diag2.add(x + y)

    def is_lit(query):
        x, y = query
        return any([
            x in rows,
            y in cols,
            x - y in diag1,
            x + y in diag2
        ])

    return [is_lit(query) for query in queries]

# Define lamps and queries
lamps = [(1, 1), (4, 4)]
queries = [(1, 1), (1, 4), (4, 4), (3, 2)]
print(set_based_illumination(5, lamps, queries))

The output of this code snippet would be:

[True, True, True, False]

In this code snippet, we first prepare sets for the rows, columns, and diagonals that are illuminated. We then define a function is_lit which uses these sets to determine if a query point is illuminated. We use list comprehension to prepare the final output that indicates the status of each query.

Method 4: Object-Oriented Approach with Lamp Class

The object-oriented approach encapsulates the lamp’s properties and behaviors within a class. Each lamp object knows its position and can determine if it illuminates a given point. This method is highly readable and extendable, suitable for complex logic involving lamps’ behaviors.

Here’s an example:

class Lamp:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def illuminates(self, qx, qy):
        return self.x == qx or self.y == qy or abs(self.x - qx) == abs(self.y - qy)

def object_oriented_illumination(lamps, queries):
    lamp_objects = [Lamp(x, y) for x, y in lamps]
    return [any(lamp.illuminates(qx, qy) for lamp in lamp_objects) for qx, qy in queries]

# Define lamps and queries
lamps = [(1, 1), (4, 4)]
queries = [(1, 1), (1, 4), (4, 4), (3, 2)]
print(object_oriented_illumination(lamps, queries))

The output of this code snippet would be:

[True, True, True, False]

The code snippet introduces a Lamp class that encapsulates the logic to determine if a given point is illuminated. We create a list of lamp objects and then use list comprehension to determine the illumination status for each query.

Bonus One-Liner Method 5: Lambda Functions and Map

For a more functional programming approach, lambda functions can be used in conjunction with the map function to apply a simple illumination check over all queries. This concise method is good for simple checks that don’t require state changes.

Here’s an example:

lamps = {(1, 1), (4, 4)}
queries = [(1, 1), (1, 4), (4, 4), (3, 2)]

illuminated = list(map(
    lambda q: any(lamp[0] == q[0] or lamp[1] == q[1] or
                  abs(lamp[0] - q[0]) == abs(lamp[1] - q[1])
                  for lamp in lamps),
    queries))

print(illuminated)

The output will be:

[True, True, True, False]

This one-liner uses a lambda function and the map construct to apply the illumination check to each query. The logic for determining if a point is illuminated remains the same, but the lambda expression makes the code very compact. It’s efficient and elegant for simple scenarios.

Summary/Discussion

  • Method 1: Using Dictionary to Track Lamps’ States. Strengths: Dynamic and straightforward. Weaknesses: Potentially slower for a large number of queries.
  • Method 2: Precomputing Illumination for All Cells. Strengths: Fast for multiple queries. Weaknesses: Memory intensive and not practical for very large grids.
  • Method 3: Using Set Operations for Fast Illumination Detection. Strengths: Efficient and concise. Weaknesses: Can be less readable to those unfamiliar with set operations.
  • Method 4: Object-Oriented Approach with Lamp Class. Strengths: Modular and extendable. Weaknesses: Slightly more overhead and potentially over-engineered for simple problems.
  • Method 5: Lambda Functions and Map. Strengths: Very concise. Weaknesses: Could become unreadable with more complex logic, less control over individual lamp states.