Рубрики
Без рубрики

Появление кода 2018 День 6: хрональные координаты

Почему эльфы могут не просто дать нам руководство? Tagged с AOC2018, Python, Puzzle, Challenge.

Этот пост изначально появился на Steadbytes.com

Полное решение можно найти на GitHub

Часть первая

Нас просят найти Самая большая конечная область Окружающая область координаты из списка координат в Ввод головоломки Анкет

Вход в диапазон

Каждая строка входа содержит пару X, y Координаты разделены , :

1, 1
1, 6
8, 3
3, 4
5, 5
8, 9

Чтобы получить список целое число координаты:

  1. Инициализируйте список Чтобы хранить координаты
  2. Для каждой строки ввода:
    • Разделите линию на В
    • Отправить каждый разделенный элемент на инт
    • Добавить кортеж целочисленного координат в список
with open("input.txt") as f:
    coords = []
    for line in f.readlines():
        x, y = line.split(", ")
        coords.append((int(x), int(y)))

Это работает нормально, но может быть очищено и сделано больше Pythonic Использование понимания списка:

with open("input.txt") as f:
    coords = [tuple(int(s) for s in line.split(", ")) for line in f.readlines()]

Расчет области координат

С этого момента термин координата будет ссылаться на одно из положений X, Y, извлеченных из ввода головоломки и Место будет ссылаться на любую позицию x, y в поисковой сети.

Головоломка включает в себя некоторые важные ограничения:

  • Расчеты должны использовать Манхэттенское расстояние
  • область Вокруг координаты это количество целое число X, y locations, которые ближайший к этому

    • X, y местоположения не могут быть Равное Расстояние с другой координат
  • Желаемая координата должна иметь конечный область

    • то есть координата прилагается со всех сторон другой координатой
  1. Найдите мин/макс x, y пределы координат
  2. В этих пределах генерируйте все возможные целые числа X, Y местоположения
  3. Рассчитайте расстояние Манхэттена от каждой координаты до каждого места
  4. Для каждого места определите ближайшую координату
  5. Для каждой координаты вычислите область, суммируя количество мест, для которых она ближе всего
  6. Вернуть самую большую площадь

Поиск пределов координат является случаем поиска минимальных и максимальных значений для компонентов x, y входа головоломки:

def coords_limits(coords):
    min_x = min(coords, key=lambda c: c[0])[0]
    max_x = max(coords, key=lambda c: c[0])[0]
    min_y = min(coords, key=lambda c: c[1])[1]
    max_y = max(coords, key=lambda c: c[1])[1]

    return ((min_x, max_x), (min_y, max_y))

coords_limits(
    [(1, 1), (1, 6), (8, 3), (3, 4), (5, 5), (8, 9)]
)
# ((1, 8), (1, 9))

Теперь вложенная петля между каждым набором пределов может использоваться для перечисления всех возможных мест:

def gen_locations(x_lims, y_lims):
    for x in range(x_lims[0], x_lims[1] + 1):
        for y in range(y_lims[0], y_lims[1] + 1):
            yield (x, y)

Обратите внимание, что я использую генератор Здесь для ленивой оценки. Это означает, что каждое место будет рассчитываться по требованию, как это требуется, вместо того, чтобы создать весь список одновременно. Преимущество этого станет ясной на следующих шагах.

Чтобы рассчитать расстояние между координатами и местоположениями, я сначала создаю кортеж (<местоположение>, ) Для каждой комбинации:

def gen_locations_coords(coords):
    x_lims, y_lims = coords_limits(coords)
    for x, y in gen_locations(x_lims, y_lims):
        for cx, cy in coords:
            yield ((x, y), (cx, cy))

Затем вычислите расстояние Манхэттена для каждого из них, генерируя кортежи ((, <куд>), <расстояние>) :

def manhattan_distance(x1, y1, x2, y2):
    return abs(x1 - x2) + abs(y1 - y2)

def gen_locations_coords_dists(coords):
    for (x, y), (cx, cy) in gen_locations_coords(coords):
        d = manhattan_distance(cx, cy, x, y)
        yield ((x, y), (cx, cy), d)

Использование генераторов здесь невероятно мощное, что позволяет разделить «цепь» итерационной логики на несколько функций без создания и разпромистики больших структур данных (в данном случае список мест).

Чтобы определить ближайшую координату в каждом месте, я перетекаю на комбинации местоположений, координат и расстояния, создаваемые gen_locations_coords_dists Обновление отображения между спариванием с кратчайшим расстоянием:

# location: closest coordinate
closest_coord_map = {}
# location: shortest distance
closest_coord_dists = defaultdict(lambda: float("inf"))
for (x, y), (cx, cy), d in gen_locations_coords_dists(coords):
    # shortest distance seen so far
    closest = closest_coord_dists[(x, y)]
    if d < closest:
        # current pairing is shorter
        closest_coord_map[(x, y)] = (cx, cy)
        closest_coord_dists[(x, y)] = d
    elif d == closest and closest_coord_map[(x, y)] != (cx, cy):
        # same distance, not the same X,Y values
        # locations with multiple equal distance coords don't count towards area
        closest_coord_map[(x, y)] = None

Используя это ближайший_коорд_мап , область для каждой координаты может быть рассчитана, подсчитывая количество мест, где каждая координата ближайшая:

x_lims, y_lims = coords_limits(coords)
coords_areas = defaultdict(int)
for xy, coord in closest_coord_map.items():
    if coord is None:
        # location was equal distance to coordinates
        continue
    if xy[0] in x_lims or xy[1] in y_lims:
        # location at edge of coordinate limits
        # must have an infinite area
        coords_areas[coord] = float("-inf")
    coords_areas[coord] += 1
largest_area = max(coords_areas.values())

Эти последние два шага могут быть завершены в функцию для части 1:

def part_1(coords):
    closest_coord_map = {}
    closest_coord_dists = defaultdict(lambda: float("inf"))
    for (x, y), (cx, cy), d in gen_locations_coords_dists(coords):
        closest = closest_coord_dists[(x, y)]
        if d < closest:
            closest_coord_map[(x, y)] = (cx, cy)
            closest_coord_dists[(x, y)] = d
        elif d == closest and closest_coord_map[(x, y)] != (cx, cy):
            closest_coord_map[(x, y)] = None

    x_lims, y_lims = coords_limits(coords)
    coords_areas = defaultdict(int)
    for xy, coord in closest_coord_map.items():
        if coord is None:
            continue
        if xy[0] in x_lims or xy[1] in y_lims:
            coords_areas[coord] = float("-inf")
        coords_areas[coord] += 1
    return max(coords_areas.values())

Для ввода моей головоломки результат – 3358 Анкет

Часть вторая

Теперь нас просят найти ** размер региона, содержащей все места, которые имеют общее расстояние до всех заданных координат менее 10000 ‘. Загадка фактически дает нам алгоритм для поиска желаемой области (используя меньший размер 32):

Для каждого места добавьте расстояния ко всем данным координатам; Если общая сумма этих расстояний составляет менее 32, это место в желаемом регионе.

Вся тяжелая работа была проделана в первой части, мы уже можем генерировать расстояния между всеми местами и всеми координатами. Последний шаг состоит в том, чтобы подвести итог на расстояния для каждого места, чтобы найти общее расстояние до всех координат в регионе, отфильтровать их до того места, где общее расстояние составляет менее 10000 и подсчитывает их!

питон def part_2 (координат): (int) для (x, y), (cx, cy), d в gen_locations_coords_dists (координат): # Рассчитайте размер региона для каждого места total_dists_to_coords [(x, y)] # Фильтр и посчитайте Вернуть Лен ([l для L, D в Total_dists_to_coords.items (), если d <10000])

Для ввода моей головоломки результат – 45909

Примечание об ленивой оценке

Эта головоломка отлично подходила для генераторов и ленивой оценки. Он обеспечивает очень интересную парадигму программирования, которая может быть чрезвычайно полезна для больших наборов данных (и, конечно, веселья). Я бы очень поощрял чтение Структура и интерпретация компьютерных программ Глава 3.5 о потоках для превосходного введения в тему.

Ресурсы

Оригинал: “https://dev.to/steadbytes/aoc-2018-day-6-chronal-coordinates-288d”