Advent of Code 2020 (26 части серии)
ОК, вернемся к земле. Тратить слишком длинное здание сложное и ненужное решение, как в последний раз занимает слишком длинное, поэтому я сохраню этот простой.
Ссылка на вызов на Advent of Code 2020 сайта 2020
Задача – это еще одна, где мы должны сломать код. Довольно просто в списке номеров число может быть сделано из добавления двух из предыдущих n
цифры до этого. Найдите тот, который не следует этому правилу.
Повторно использовать день 01
Эта проблема на самом деле довольно похожа на Challenge Day 01, который включает в себя наличие двух чисел из списка, который добавляет до первого. Чтобы сэкономить время, мы можем просто повторно использовать это:
from itertools import combinations for left_number, right_number in combinations(data, 2): if left_number + right_number == 2020: print("Found it!", left_number, right_number) break
Этот код находит пару номеров из списка, который добавляет до 2020. Нам просто нужно отрегулировать это немного. Во-первых, нам нужно заменить данные
с «ломтиком» исходного списка. Если мы называем значение, мы проверяем IDX
Тогда нам нужно нарезать от 25 до этого до 1 до нее. Это можно сделать с помощью нотации нарезки Python Данные [IDX-преамбулы: IDX]
Отказ Во-вторых, нам нужно сравнить числа со значением данные [IDX]
, так:
for left_number, right_number in combinations(data[idx-preamble:idx], 2): if left_number + right_number == data[idx]: break
Далее это просто нужно идти в петлю, и нам нужно обнаружить, когда это не Перерыв, к счастью, легко в Python, как для
петли также имеют еще
пункт, который вызывает, если вы завершите цикл, не нарушая. Поскольку мы ищем номер, где не существует комбинации, она не вырвется от цикла, и поэтому мы можем поймать его.
Это полный код для части 1
data = [int(line) for line in open("input.txt").readlines()] preamble = 25 for idx in range(preamble, len(data)): for left_number, right_number in combinations(data[idx-preamble:idx], 2): if left_number + right_number == data[idx]: break else: print("Did not find it!", start, data[idx])
Часть 2 вызова, просит нас найти сумму какого-либо непрерывного диапазона чисел, которые составляют номер, найденный в части 1, который я имею наличие the_number
Отказ По сути, проблема сводится к этому: у нас есть 1000 номеров в данные
Давайте определим X в качестве исходного положения, а y в качестве окончательного положения, которому нужно суммировать значения. Найти x и y, который приведет к сумме, равной the_number
Отказ
В отличие от предыдущих задач, в которых была вариант грубой силы, это теперь грубо принудительно вдоль двух разных осей. Поскольку у нас есть 1000 номеров, у нас есть чуть ниже 000 итераций, чтобы пройти (это менее половины, потому что одно ограничение X должно быть ниже
Векторное решение
Мы можем рассмотреть пространство поиска большой 2D-массива, со значениями от 0 до 1000 на каждой оси, давая нам чуть более миллиона записей. Около половины этих ценностей недействительна из-за ограничения, которое X Самый простой способ повторной векторизации грузоподъемности этого раствора снова использовать Numpy. Давайте сначала загрузим данные, используя Numpy: Далее я собираюсь определить функцию, которая занимает начальную точку, конечную точку и возвращает значение, которое представляет, насколько близко мы к цели. В основном разница между результатом и числом, которое мы хотим, так что, когда значение равно 0, мы знаем, что у нас есть правильное значение. Здесь я определяю ограничение о том, что начало должно быть ниже конца, и оба должны быть между 0 и размером данных, а в любом месте за пределами ограничения – К сожалению, из-за того, как работает эта функция, она не может принимать векторы в качестве входов, как я могу с арифметикой, например, В то время как Теперь, на вычислении нашей сетки ценностей. Для этого мне просто нужно рассчитать массив входов, просто все значения между 0 и размер наших данных, а затем пройти через наши Наконец, нам просто нужно найти, где Полный код: Вывод Этот код требуется всего несколько секунд (4 секунды на моей машине) благодаря оптимизации Numpy, и нам не нужно было слишком много думать о том, как написать или оптимизировать код. Мы могли бы сделать это быстрее, оптимизируя доступ к данным, а также все варианты, которые я обсуждал в день 01 ( Чаш, нумба и т. Д.) Результатом является два диапазона: 554: 571 и 667: 668. Второй диапазон на самом деле является только решением часть 1 – мы искали какой-либо диапазон, который суммирует в записи, которую мы нашли в части 1. Таким образом, естественно, диапазон, состоящий из самой записи, имеет сумму, равную себе. Здесь я применил пару функций журнала в значения Z, чтобы цвета выглядели более четко. График на самом деле очень крутой, что затрудняет четко видеть, а также представляет проблему для любого метода оптимизации на основе градиента. Я также понизил точку результата как красный знак, чтобы подчеркнуть, где оно есть. Что мы видим вот то, что правильное значение находится в очень узкой долине чисел. Эта долина настолько узкая, что я фактически пробовал некоторые функции оптимизации/минимизации из Вот этот код Brute-Force: Примечание. Это занимает около 20 секунд на моей машине, по сравнению с 4 секундами простых старого векторизованного метода. Использование лучшей глобальной стратегии оптимизации должна работать быстрее, а если хорошо настроено, может легко победить 4 секунды. Кроме того, стоит упомянуть, что 4 секунды сама действительно медленно, и оптимизированная версия даже грубой силы должна решить быстрее, чем это. Причина этого, хотя и векторизированные расчеты Numpy быстро, мы потянув обратно в Python и проходя через функцию Python, поэтому мы несущим большое количество выполнения функции Python Functions. Перепишите, что векторизованный доступ в Numpy позволит нам сделать намного лучше. Далее!import numpy as np
data = np.loadtxt("input.txt")
def sum_range_err(start, end):
if 0 <= start < end < data.size):
return np.abs(the_number - np.sum(data[int(start):int(end)]))
return np.NaN
НП. Нан
ОтказA + B.
Когда оба A и B являются векторами, отлично работает, результатом является еще один вектор, где каждый элемент был добавлен вместе. Тем не менее, Данные [A: B]
не приведет к векторизованному расчету просто потому, что Numpy не знает, как это сделать. Не слишком много думая об этом, я просто собираюсь бросить всю функцию в Numpy’s numpy.vectionize ()
Функция, которая оборачивает мою функцию для меня Поэтому мне не нужно думать об этом. Возвращаемое значение теперь является векторизированной версией моей функции, которая будет работать на каждом участне входных векторов.sre = np.vectorize(sum_range_err)
sum_range_err ()
Может ли только брать целые числа для своих аргументов, эта новая функция Sre ()
может принимать два вектора и вернуть вектор. Это помогает ускорить вещи.СРЕ
Функция, чтобы получить сетку результатов. Это все!xax = yax = np.arange(0, data.size)
zvals = sre(xax[:,None], yax[None,:])
ZVALS
это ноль:print(np.where(zvals == 0))
import numpy as np
data = np.loadtxt("input.txt")
def sum_range_err(start, end):
if 0 <= start < end < data.size:
return np.abs(the_number - np.sum(data[int(start):int(end)]))
return np.NaN
sre = np.vectorize(sum_range_err)
xax = yax = np.arange(0, data.size)
zvals = sre(xax[:,None], yax[None,:])
print(np.where(zvals == 0))
(array([554, 667]), array([571, 668]))
ZVALS
Рассчитано выше – это 2D-сетка значений данных, и мы ищем минимумы этого значения. Мы можем легко визуализировать то, что это выглядит, используя Матплотлиб
путем построения карты цвета:import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(16,12))
cplot = ax.pcolormesh(xax, yax, np.log(np.log(zvals)))
fig.colorbar(cplot)
ax.plot(resultx, resulty, 'r+')
fig.show()
Scipy
Пакет, и большинство из них не смог найти решение. Единственный успех был метод Brute-Force, который занял много раз дольше, чем векторизованная версия выше. Я думаю, что эти функции оптимизации, вероятно, все равно будут работать, но потребуется некоторая настройка, чтобы правильно получить размеры шага.from scipy import optimize
def sum_range_err_reflected(point):
start = np.min(point)
end = np.max(point)
return np.log(np.log(np.abs(the_number - np.sum(data[int(start):int(end)]))))
ranges = (slice(0, data.size, 1), slice(0, data.size, 1))
optimize.brute(sum_range_err_reflected, ranges=ranges)
Advent of Code 2020 (26 части серии)
Оригинал: “https://dev.to/meseta/advent-of-code-day-09-215i”