Project Euler

프로젝트 오일러 004

앞에서부터 읽을 때나 뒤에서부터 읽을 때나 같은 대칭수 찾기

2분
##project-euler

대칭수

앞에서부터 읽을 때나 뒤에서부터 읽을 때나 모양이 같은 수를 대칭수라고 합니다. 어떤 정수 N이 대칭수인지 알아보려면 각자리 숫자의 리스트로 분해한 후, 그 리스트가 대칭인지를 보면 됩니다. 가장 간편한 방법은 reversed를 사용해서 뒤집은 리스트와 원래의 리스트가 같은지를 보면 됩니다.

그러나 이 문제에서는 이런 연산을 아주 많이 해야 합니다. 따라서 빠른 시간 내에 답을 찾는 코드를 작성하려면 이 연산에 드는 시간을 최소화할 필요가 있습니다. 경험적으로 가장 빠른 방법은 정수를 문자열 s로 변환한 후,이 문자열을 뒤집은 것이 같은지 체크하는 것입니다.

세 자리 정수 2개를 선택하는 방법은 810만 가지가 있지만, a × b = b × a 이므로 뒤에 있는 수가 같거나 더 큰 경우만 생각하면 루프의 횟수를 절반으로 줄일 수 있습니다.

ispal = lambda n: str(n) == ''.join(str(n)[::-1])
pals = set()

for x in range(100, 1000):
	for y in range(y, 1000):
		z = x * y
		if ispal(z):
			pals.add(z)

print(max(pals))

다른 접근

이렇게 간단하게 풀 수 있지만 다른 접근법을 생각해볼 수도 있습니다. 위 풀이는 모든 대칭수를 찾은 후에 가장 큰 값을 찾았습니다. 반대로 가장 큰 수부터 거꾸로 내려오면서 대칭수를 찾는다면 어떨까요? 그리고 그 대칭수가 세자리 자연수 두 개의 곱으로 표현되는지를 찾는 것입니다. 언뜻 생각하기에는 상당히 비효율적일 것 같지만, 조건에 맞는 답을 하나만 찾는다면 그 값이 최대값임이 분명하기 때문에, 결과적으로는 더 빠르게 종료할 수 있습니다.

def check_number(n: int) -> bool:
	for m in range(100, int(n**0.5 + 1)):
		x, y = divmod(n, m)
		if y == 0 and 99 < x < 1000:
			return True
	return False

for x in range(999_999, 10000, -1):
	if ispal(x) and check_number(x):
		print(x)
		break

어떤 수가 세자리 자연수 두 개의 곱인지를 찾는 방법은, 100부터 N의 제곱근 사이의 정수로 나눠보고, 나눌 수 있는 경우 그 몫 역시 100~999 범위에 있는지를 검사하면 됩니다.