영원할 것 같았던 시간(정확히 말하면 5시간)을 지나 Day 20 파트 2는 마침내 협력하기로 결정했습니다. 아직도 기다리느라 약간 멍한 상태지만 의무가 있습니다! 오늘 우리는 Advent of Code의 7일차를 다루고 있으며, 지난주 6일차에 이어서 시작합니다. 오늘 우리의 임무는 다리를 수리하여 다리를 건널 수 있도록 하고 수석 역사가를 계속 찾는 것입니다.
Microsoft Copilot으로 제작한 귀여운 일러스트
오늘의 챌린지는 우리에게 다른 종류의 문제를 제시합니다. 특정 형식으로 배열된 숫자 목록이 제공됩니다(다행히 오늘은 2D 퍼즐이 아닙니다...). 각 줄은 방정식을 형성하도록 설계되었지만 연산자는 없습니다. 우리의 임무는 결과 방정식이 참인지 확인하기 위해 다양한 연산자를 테스트하는 것입니다.
입력을 처리하기 위해 두 가지 구문 분석 기능을 사용합니다. 구문 분석 기능은 먼저 콜론(:) 문자로 각 줄을 나눕니다.
def parse(input: str) -> tuple[tuple[int, tuple[int, ...]], ...]: return tuple( parse_line(*line.strip().split(":")) for line in input.strip().splitlines() )
parse_line은 예상되는 문자열과 피연산자 문자열을 정수로 변환하여 예상되는 정수와 정수 피연산자의 튜플을 포함하는 튜플을 반환합니다
def parse_line(expected: str, operands: str) -> tuple[int, tuple[int, ...]]: return int(expected), tuple(int(item) for item in operands.strip().split(" "))
저는 함수형 프로그래밍 스타일을 선호하며 Python의 명령형 특성에도 불구하고 toolz/cytoolz와 같은 라이브러리는 매우 유용합니다. 오늘은 toolz.functoolz의 thread_first를 사용하고 있습니다. thread_first 작동 방식은 다음과 같습니다. 초기 값을 가져온 다음 일련의 함수-인수 쌍을 적용하여 각 단계를 통해 결과를 스레딩합니다.
>>> from operator import add, mul >>> assert thread_first(1, (add, 2), (mul, 2)) == mul(add(1, 2), 2)
이 예에서 thread_first는 1로 시작한 다음 2로 add를 적용하고(결과 3) 마지막으로 2로 mul을 적용합니다(결과 6). 이는 mul(add(1, 2), 2)와 동일합니다.
이제 연산을 적용하기 위한 계산 함수를 정의합니다. 입력으로 함수(funcs) 튜플과 피연산자(operands) 튜플을 사용합니다.
def calculate( funcs: tuple[Callable[[int, int], int], ...], operands: tuple[int, ...] ) -> int: assert len(operands) - len(funcs) == 1 return thread_first(operands[0], *(zip(funcs, operands[1:])))
assert는 함수보다 피연산자를 하나 더 보장합니다. Operands[1:]는 함수에 대한 피연산자를 제공합니다. zip 및 *는 연결된 계산을 수행하는 thread_first에 대한 함수-피연산자 쌍을 생성합니다.
예:
>>> from operator import add, mul >>> calculate((add, mul), (2,3,4)) 20
이제 check_can_calibrate 함수를 사용하여 입력의 각 라인을 확인할 수 있습니다. 이 함수는 예상 결과, 피연산자 및 가능한 함수의 튜플을 입력으로 사용하고, 함수 조합이 예상 결과를 생성하면 True를 반환하고, 그렇지 않으면 False를 반환합니다.
def check_can_calibrate( expected: int, operands: tuple[int, ...], funcs: tuple[Callable[[int, int], int], ...], ) -> bool: return next( filter( None, ( calculate(funcs, operands) == expected for funcs in product(funcs, repeat=len(operands) - 1) ), ), False, )
itertools.product는 모든 기능 조합을 생성합니다. 생성기 표현식은 예상 결과와 일치하는 조합이 있는지 확인합니다. filter(None, ...) 및 next(..., False)는 효율적으로 첫 번째 True 결과를 찾고, 결과가 없으면 False를 반환합니다.
1부에서는 곱셈과 덧셈 연산자만 제공됩니다. 퍼즐은 기대값의 합을 묻습니다. 여기서 이러한 연산자를 사용하여 유효한 방정식을 구성할 수 있습니다. 이 합계를 계산하기 위해 평가 함수를 구현합니다.
def parse(input: str) -> tuple[tuple[int, tuple[int, ...]], ...]: return tuple( parse_line(*line.strip().split(":")) for line in input.strip().splitlines() )
파싱된 입력을 반복하고 check_can_calibrate가 True를 반환하는 예상 값을 합산합니다.
마지막으로 지금까지 구축한 것 중 1부를 조립합니다
def parse_line(expected: str, operands: str) -> tuple[int, tuple[int, ...]]: return int(expected), tuple(int(item) for item in operands.strip().split(" "))
이 함수는 구문 분석을 사용하여 입력을 구문 분석한 다음 구문 분석된 데이터와 곱셈과 덧셈을 각각 나타내는 함수 튜플(operator.mul, Operator.add)로 평가를 호출합니다.
2부에서는 두 숫자를 결합하는 연결 연산자를 만나게 됩니다. Python에서 이는 f-문자열을 사용하는 것과 동일합니다.
>>> from operator import add, mul >>> assert thread_first(1, (add, 2), (mul, 2)) == mul(add(1, 2), 2)
이렇게 하면 첫 번째 숫자 끝에 두 번째 숫자의 숫자를 추가하여 효과적으로 새 숫자를 생성할 수 있습니다.
또는 수학 공식을 사용하여 연결을 수행할 수도 있습니다. 양의 정수 x의 자릿수는 다음 공식을 사용하여 계산할 수 있습니다.
이것은 log₁₀(x)가 x를 얻기 위해 10을 올려야 하는 거듭제곱을 제공하기 때문에 작동합니다. Floor 함수는 이를 가장 가까운 정수로 내림하고 1을 더하면 자릿수가 제공됩니다. 123을 예로 들어보겠습니다.
이를 int_concat 함수로 구현:
def calculate( funcs: tuple[Callable[[int, int], int], ...], operands: tuple[int, ...] ) -> int: assert len(operands) - len(funcs) == 1 return thread_first(operands[0], *(zip(funcs, operands[1:])))
정수 연결을 수학적으로 수행하면 문자열 변환으로 인한 오버헤드가 방지됩니다. 문자열 연결에는 메모리 할당 및 조작이 포함되며, 이는 특히 숫자가 많거나 연결이 많은 경우 직접 정수 산술보다 효율성이 떨어집니다. 따라서 이 수학적 접근 방식은 일반적으로 더 빠르고 메모리 효율적입니다.
마지막으로 파트 2를 구현합니다. 파트 1과 비교했을 때 유일한 차이점은 int_concat 연산자가 추가되었다는 것입니다.
>>> from operator import add, mul >>> calculate((add, mul), (2,3,4)) 20
짜잔! 우리는 7일차를 마쳤습니다. 이는 특히 나중 날에 비해 상대적으로 간단한 과제였습니다(20일차 최적화가 여전히 골치 아픈가요?). 가장 성능이 좋지는 않더라도 가독성을 최우선으로 생각했습니다.
오늘은 여기까지입니다. 즐거운 명절 보내시고 새해 복 많이 받으세요?! 내년에는 더 나은 직장 상황을 바라며(여전히 #OpenToWork, 협업을 요청합니다!) 다음 주에 다시 글을 쓰겠습니다.
위 내용은 다리를 수리하는 방법, 코드의 출현 7의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!