본문 바로가기

Julia

오일러 프로젝트 11번 (Julia)

20x20 행렬에서 4개씩 가로, 세로, 대각선 방향으로 연속한 숫자들의 곱을 계산하여 그 중 최대값을 찾는다.
파이썬의 경우, 긴 1차원 배열로 취급하여 특정 좌표에서 가로, 세로, 대각선에 위치한 원소를 추출하는 방식으로 계산했으나, 줄리아는 20x20 행렬로 보고, 부분행렬의 가능한 모든 곱을 찾는 방법을 사용해본다. (이쪽이 훨씬 더 깔끔하다.)

먼저 문자열을 모두 split()한 다음 정수로 변환한다. 400개짜리 배열이 만들어질 것이다. 문자열을 정수나 실수등 다른 값 타입으로 변환할 때에는 parse()를 사용한다. 예를 들어 정수를 표현한 문자열을 정수로 변환하려면 다음과 같이 한다.


v = "123"
i = parse(Integer, v)

람다식을 사용해서 맵핑할 수 있는데, .|> 을 사용해서 좀 더 간단히 표현할 수 있다.

s = """08 02 22 .... """
xs = s |> split .|> x -> parse(Integer, x)

이렇게 변환한 배열을 2차원 배열로 만들어보자. 줄리아의 배열은 열방향이 우선이므로 문제와 같은 행방향 우선 배열로 만들려면 전치해야 한다. reshape() 한 다음에 전치하자. 전치는 transpose() 함수도 있지만, T' 와 같이 간단히 표현하여 만들 수 있다.

xs = reshape(xs, 20, 20)'

첫번째 값인 08을 기준으로 4x4 행렬을 생각해보자. 여기서 찾을 수 있는 가능한 곱은 다음과 같다.

  • 행방향의 4개의 숫자의 각각의 곱
  • 열방향의 4개의 숫자의 각각의 곱
  • 오른쪽 아래 방향의 대각선의 곱
  • 왼쪽 아래 방향 대각선의 곱

행, 열 방향의 곱은 prod 함수를 해당 방향으로 맵핑하면 된다. 다차원행렬에서 특정한 축방향으로 각각의 슬라이스를 맵핑하는 것은 mapslices() 함수를 사용한다. 열방향 각각의 곱을 찾아보자

ys = xs[1:4, 1:4]

mapslices(prod, ys, dims=1)

#1×4 Matrix{Int64}:
# 1651104  336140  6414210  6514520

mapslices(prod, ys, dims=2)

#4×1 Matrix{Int64}:
#   34144
# 9507960
# 8981847
# 7953400

대각선의 경우 ys[i:i] for i=1:4와 같은 표현을 써도 되지만, diag()함수를 써서 대각선의 원소를 추려낼 수 있다. 단, 해당 함수는 LinearAlgebra 모듈을 사용해야 한다. 쓰는 것이 간편하니까 해보자.

using LinearAlgegra

ys |> diag |> prod
# 279496

반대쪽 대각선은 행렬을 90도로 왼쪽으로 돌려서 diag()를 적용하면 된다. 회전 변환은 rotl90으로 할 수 있다. 이상의 내용을 조합하여 정리하면 다음과 같다.


using LinearAlgebra

s = """08 02 22 97 38 15 00 40 00 75 04 .... """ # 문제의 텍스트를 붙여넣기

# 텍스트를 20x20 행렬로 변환
xs = s |> split .|> (x -> parse(Int, x)) 
xs = reshape(xs, 20, 20)'

# (1,1) ... (17, 17)에서 가능한 모든 방향의 곱을 계산하고 최대값을 취함
# 그 중에서 다시 최대값을 취해출력
@time begin
    map((a, b) for a=1:17,b=1:17) do (a, b)
        ys = xs[a:a+3,b:b+3]
        map(mapslices(prod, ys, dims=1) |> maximum,
            mapslices(prod, ys, dims=2) |> maximum,
            ys |> diag |> prod,
            ys |> rotl90 |> diag |> prod)
    end |> maximum |> println
end