1. 문제
12727번: Numbers (Small) (acmicpc.net)
12727번: Numbers (Small)
The first line of input gives the number of cases, T. T test cases follow, each on a separate line. Each test case contains one positive integer n. Limits 1 <= T <= 100 2 <= n <= 30
www.acmicpc.net
2. 풀이
문제 자체는 상당히 간단하다
(3+√5)n의 정수부분 뒤에서 3자리까지를 정확하게 구하라
여기서 n은 2부터 30까지이다.
그러니까 n은 2부터 30까지 미리 구해놓고, 테스트케이스에 대응하면 될 것이다
근데 막상 해보면 간단하지가 않다..
√5가 실수라, n이 커질수록 오차가 생긴다
이는 컴퓨터가 실수를 2진수로 저장해서 그렇다. 만약 10진수 형태로 산술연산을 할 수 있다면 오차가 생기지 않을 것이다.
이를 도와주는 파이썬의 모듈이 decimal의 Decimal 객체이다.
decimal — 십진 고정 소수점 및 부동 소수점 산술 — Python 3.11.3 문서
decimal — Decimal fixed point and floating point arithmetic
Source code: Lib/decimal.py The decimal module provides support for fast correctly rounded decimal floating point arithmetic. It offers several advantages over the float datatype: Decimal “is based...
docs.python.org
- Decimal “is based on a floating-point model which was designed with people in mind, and necessarily has a paramount guiding principle – computers must provide an arithmetic that works in the same way as the arithmetic that people learn at school.” – excerpt from the decimal arithmetic specification.
"컴퓨터는 사람이 학교에서 배운 산술 연산 방식과 동일한 방식으로 작동하는 산술 연산을 제공해야한다는 원리에 입각하여 만들어진 floating-point model"
파이썬 소수 연산 - float 타입과 decimal 모듈 | Engineering Blog by Dale Seo
파이썬 소수 연산 - float 타입과 decimal 모듈
Engineering Blog by Dale Seo
www.daleseo.com
019 소수점을 정확하게 계산하려면? ― decimal.Decimal - 점프 투 파이썬 - 라이브러리 예제 편 (wikidocs.net)
019 소수점을 정확하게 계산하려면? ― decimal.Decimal
decimal.Decimal은 숫자를 10진수로 처리하여 정확한 소수점 자릿수를 표현할 때 사용하는 모듈이다. ## 문제 다음은 파이썬에서 볼 수 있는 이상한 연산 결과의 예…
wikidocs.net
Decimal은 실수 연산을 우리가 아는 10진수 연산으로 정확하게 해준다
Decimal을 쓸때는 문자열 형태로 넣어줘야 한다.
1.1이라는 값을 정확하게 1.1로 나타내고 싶다면...

먼저 (3+√5)가 계산해보니 5.23606797749979래

그래서 다이나믹 프로그래밍?방식으로 30승까지 Decimal을 이용해서 다음과 같이 구해봄
from decimal import *
A = [0]*31
A[1] = Decimal('5.23606797749979')
for i in range(2,31):
A[i] = A[i-1]*A[1]
A
대충 이런 느낌으로 결과가 나오는데

그러면 int()를 씌우면 정수부분만 가지고오더라고..?
그래서 int()를 씌워서 정수부분만 가지고 온 다음에... 1000으로 나눠주면 정수부분에서 뒤에서 3자리만 가지고올거다
이때, 3자리가 안된다면, 앞에 0을 붙여야하니까 str()로 바꿔주자고
from decimal import *
A = [0]*31
A[1] = Decimal('5.23606797749979')
B = [0]*31
B[1] = str(int(A[1])%1000)
while len(B[1]) != 3:
B[1] = '0' + B[1]
for i in range(2,31):
A[i] = A[i-1]*A[1]
B[i] = str(int(A[i])%1000)
while len(B[i]) != 3:
B[i] = '0' + B[i]
B
이렇게 계산하면...
[0,
'005',
'027',
'143',
'751',
'935',
'607',
'903',
'991',
'335',
'047',
'943',
'471',
'055',
'447',
'463',
'991',
'095',
'608',
'264',
'152',
'857',
'536',
'789',
'602',
'504',
'862',
'435',
'876',
'668',
'564']
위와 같이 나와서.. 해보면 오답임
왜 오답인지 보면.. 아래 그림에 답이 있다

n이 커질수록 소수점 이하 자리수가 줄어드는거 볼 수 있다
그러니 누적오차가 커지면서, 정수부분이 부정확해질 것이다
그러면.. 이번엔 Decimal(3+5**(1/2))로 실수를 Decimal()에 그대로 넣어서 계산을 해봄
from decimal import *
A = [0]*31
A[1] = Decimal(3+5**(1/2))
B = [0]*31
B[1] = str(int(A[1])%1000)
while len(B[1]) != 3:
B[1] = '0' + B[1]
for i in range(2,31):
A[i] = A[i-1]*A[1]
B[i] = str(int(A[i])%1000)
while len(B[i]) != 3:
B[i] = '0' + B[i]
B
이렇게 해서 사용해보면 오답
[0,
'005',
'027',
'143',
'751',
'935',
'607',
'903',
'991',
'335',
'047',
'943',
'471',
'055',
'447',
'463',
'991',
'095',
'607',
'264',
'152',
'856',
'531',
'760',
'441',
'625',
'075',
'408',
'550',
'250',
'167']
그러면 이번엔... (3+√5)를 Decimal()을 이용해서 그대로 계산해보자
Decimal(a).sqrt()는 a의 제곱근을 완전한 정밀도로 구해준다네

그리고 Decimal()은 그 자체로 산술연산이 가능

앞에서 쓴거랑 비슷해보이는데..?
그래서 비교해봄

그러면 왠지 3번째가 정확하게 계산을 해주는 것 일것 같다
이렇게 계산해보면 실제로 정답이다
from sys import stdin
T = int(stdin.readline())
compute = [0,
'005',
'027',
'143',
'751',
'935',
'607',
'903',
'991',
'335',
'047',
'943',
'471',
'055',
'447',
'463',
'991',
'095',
'607',
'263',
'151',
'855',
'527',
'743',
'351',
'135',
'407',
'903',
'791',
'135',
'647']
for t in range(1,T+1):
n = int(stdin.readline())
print(f'Case #{t}: {compute[n]}')
'알고리즘 > 정밀도' 카테고리의 다른 글
되도록이면 실수로 알고리즘을 풀지 말아야하는 이유1 (0) | 2023.04.08 |
---|---|
[Java]분수를 소수점 20째자리까지 출력하는 방법 (0) | 2023.02.21 |