본문 바로가기
Project Euler

Problem 6 - Sum square difference

by 청양호박이 2019. 11. 26.

이번 문제는 왠지 간단한 것 같지만, 제약조건을 두자면 한없이 어려워지는 문제인거 같습니다. 우선 아직까지는 basic한 방식으로만 생각이 나서 해당 방법으로 우선 통과를 하고, 제약조건을 두어 Time Limit에 영향이 없도록 고려해서 한번더 올려보도록 하겠습니다.

 

  • 주어진 조건만 만족하도록, 100까지의 Sum square difference 구하기
  • 제약조건을 두어 Test case가 10000개의 Test case에 최대 10000까지 구하기 (time out 발생가능)

 

1. Basic


그냥 단도직입 적으로 구합니다. 단 python을 이용하니... 이 언어에서 가능한 편한 방식으로 구현해 보겠습니다. python에는 배열을 신규로 생성할때, 내부 함수를 통해서 새로운 배열의 생성이 가능합니다. 

 

또한, sum( )함수로 가볍게 더해보도록 하겠습니다.

def calcSquare(target):
    scaleRange = range(1, target+1)
    squareSum = sum(i**2 for i in scaleRange)
    sumSquare = sum(scaleRange)**2
    return sumSquare-squareSum


if __name__== '__main__':
    tc = int(input())
    for i in range(tc):
        target = int(input())
        print(calcSquare(target))

1부터 target까지 순차적으로 증가하는 숫자들을 가진 배열에 대해서, 각 원소들의 제곱값으로 새로운 배열을 생성하는 방식은... [ i**2 for i in scaleRange ] 와 같습니다.

 

나머지는 쉽게 이해할 수 있습니다. 이렇게 해도 일단 단일수의 100까지는 충분한 듯 싶습니다.

 

 

 

2. Unlimited


to be continue.... (라고 쓰고 돌아오기까지 시간이 좀 걸렸네요...)

 

이제는 제약조건을 두어 Test case가 10000개의 Test case에 최대 10000까지 구하기 (time out 발생가능)를 수행해 보겠습니다. 실제로 max를 max번 돌리면... 상당히 오랜 시간이 걸립니다. 

 

그래서 생각한것은 아래의 2가지를 추가하는 것입니다.

  • memoization을 통해서 주기적으로 반복되는 값을 사전에 저장
  • 1~n 까지의 합의 제곱을 구할때 sum( )조차도 오래 걸릴 수 있으니 최적화

사실 매번 1^2, 2^2 .... n^2까지 구한다면, 비효율 적이라고 할 수 있습니다. 따라서 처음에 Test case를 받기전에 1부터 10000까지의 제곱의 합을 순차적으로 구해 Stack에 저장해 놓는 방법입니다. 그럼 다음의 Test case부터는 목표값의 위치에 접근해서 그냥 값을 가져오는 것이지요.

def preCalc(target):
    res = []
    res.append(0)
    res.append(1)
    for i in range(2,target+1):
        res.append(res[i-1]+i**2)
    return res

 

두번째는, sum( )마저도 수학적인 공식으로 최적화 한다. 순차적인 합은 사실 첫값 + 끝값의 반복이라고 할 수 있습니다. 예를들어, 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10의 경우...... 첫값 + 끝값 = 11이 5번 반복되었습니다. 이를 코드로 표현하면...

def calcSumSquare(target):
    res = (target+1) * (target//2)
    if target%2: res += target//2 + 1
    return res**2

 

그렇다면 최종적인 코드는 어떻게 될까요??

 

def preCalc(target):
    res = []
    res.append(0)
    res.append(1)
    for i in range(2,target+1):
        res.append(res[i-1]+i**2)
    return res

def calcSumSquare(target):
    res = (target+1) * (target//2)
    if target%2: res += target//2 + 1
    return res**2


if __name__ == '__main__':
    squareSum = preCalc(10000)

    tc = int(input())
    for i in range(tc):
        target = int(input())
        print(calcSumSquare(target) - squareSum[target])

말 그대로... 처음에 squareSum이라는 list에 preCalc로 누적된 제곱의 합을 저장하고... 실제 loop에서는 입력된 값의 위치에서 단지 꺼내와서 연산만 하는 방식입니다.

 

이렇게 하니... 시간이 획기적으로 단축되네요~^ ^

 

-Ayotera Lab-

 

댓글