Search code examples
pythondictionaryintgrid-layout

Can I define a value in a dictionary directly using a for loop within the dictionary itself?


I'm working in some Project Euler problems and have a solution I'd like to make more adaptable. The problem itself isn't important here but for those of you that are curious, it's problem 11.

Currently, I have a grid of 20 by 20 integer values and I'm finding the maximum product of 4 adjacent values. It all works fine and pretty quickly. What I currently have is the following:

maxi = 0
amount = 4
for i in range (0,len(grid) - amount):
    for j in range (0,len(grid) - amount):
        try:
            max_dic = {
            'right':grid[i][j]*grid[i][j+1]*grid[i][j+2]*grid[i][j+3],
            'down':grid[i][j]*grid[i+1][j]*grid[i+2][j]*grid[i+3][j],
            'down_right':grid[i][j]*grid[i+1][j+1]*grid[i+2][j+2]*grid[i+3][j+3],
            'down_left':grid[i][j]*grid[i+1][j-1]*grid[i+2][j-2]*grid[i+3][j-3]
            }
        except IndexError:
            pass
        max_key = str(max(max_dic.items(), key=operator.itemgetter(1))[0])
        if max_dic[max_key] > maxi:
            maxi = max_dic[max_key]

What I would like to do is replace the values in the dictionary by something I can vary (so something in terms of amount) and I thought of using a for loop to range from 0 to amount-1 which would look like this:

'right': for a in range(amount): # Multiply the correct values

However, I'm not sure whether or not this is possible and if so, how to implement it.

Any advice on how I could do this ?


Solution

  • In your current implementation, you won't get a max_dic result at all whenever any of the pieces exceed the grid boundaries. Often in problems like these, you do indeed want a partial result. If so, you probably want the ability to handle the IndexError in a more fine-grained way. For example, you could create a simple helper function that takes a grid and two indexes and returns either the value or some default (a 1 in the case of multiplication).

    def get_val(grid, i, j, default = 1):
        try:
            return grid[i][j]
        except IndexError:
            return default
    

    Once you have that building block, it's just a matter of preparing some lists of indexes and then using a few functions from the standard library:

    from operator import mul
    from functools import reduce
    
    # Inside your two loops over i and j ...
    
    ms = list(range(i, i + amount))
    ns = list(range(j, j + amount))
    rns = list(range(j, j - amount, -1))
    
    max_dic = {
        'right'      : reduce(mul, [get_val(grid, i, n) for n in ns]),
        'down'       : reduce(mul, [get_val(grid, m, j) for m in ns]),
        'down_right' : reduce(mul, [get_val(grid, m, n) for m, n in zip(ms, ns)]),
        'down_left'  : reduce(mul, [get_val(grid, m, n) for m, n in zip(ms, rns)]),
    }