나눗셈 실수오차가 생긴다면 직접 나눗셈을 구현하기
n명의 사람이 0점부터 10점까지 정수로 존재하는 설문조사 문항에 점수를 내서 각 문항마다 평균을 낸 점수가
실수로 주어질때, n이 될 수 있는 가장 작은 정수를 구하는 문제
평균이 소수점 셋째자리까지만 주어진다
예를 들어 32/106 = 0.3018867924528302... 인데 0.301만 주어지는
----------------------------------------------------------------------------------------------------------------------------------
만약 설문조사한 사람 수가 x명이고 i번째 문항의 점수 합이 v이면
$$\frac{v}{x} \approx A[i]$$
A[i]가 소수점 넷째자리에서 버려져있으므로
$$A[i] <= \frac{v}{x} < A[i]+0.001$$
그러므로 $$A[i]*x <= v < A[i]*x + 0.001x$$
이 범위를 만족하는 정수 v가 있다면 그러한 정수 v는 i번째 문항의 점수 합이 될 수 있다
여기서 x는 1부터 1000명까지만 가능하다
왜냐하면 소수점 셋째자리까지만 주어지기 때문에 x = 1000이면 점수 합이 무조건 정수이므로 1000명이면 반드시 조건을 만족한다
이제 x = 1부터 1000까지에 대하여 가능한 정수 v로부터 v/x가 A[i]와 같은지 조사하면 된다
그런데 A[i]는 소수점 셋째자리까지만 나와있고 v/x를 하면 그 이상이 나오니까.. 소수점 셋째자리까지만 적당히 비교하면
될 수 있겠지만 부동소수점 오차때문인지 그렇게 하면 틀리는
그래서 v/x를 정확하게 소수점 셋째자리까지만 구한다
실제 나눗셈 하는 방법대로.. 아래 그림을 직접 구현하는 것이다
#a/b를 소수점 셋째자리까지만 정확하게 구하는 나눗셈
def divide(a,b):
answer = []
p,r = a//b, a%b
answer.append(str(p))
answer.append('.')
a = r*10
while len(answer) < 5:
p,r = a//b, a%b
answer.append(str(p))
a = r*10
return float(''.join(answer))
근데 문제는 v를 찾는데 실수오차로 자꾸 틀린다는거
x = 1,2,3,...,1000이고 A[i]가 실수인데 하한 A[i] * x로 실수와 정수의 곱으로 실수이고
A[i]*x + 0.001 * x가 v의 상한인데 A[i] * x + 0.001*x로 실수의 합을 구해보면...
위와 같이 예상하지 못한 값이 나올 수가 있음
예를 들어 131.0이어야하는데 130.9999999999991 이렇게 나올수도 있다는거지
그래서 그냥 Decimal로 해결하는게 마음 편하다
from sys import stdin
from decimal import Decimal
def divide(a,b):
answer = []
p,r = a//b, a%b
answer.append(str(p))
answer.append('.')
a = r*10
while len(answer) < 5:
p,r = a//b, a%b
answer.append(str(p))
a = r*10
return float(''.join(answer))
n = int(stdin.readline())
A = []
for _ in range(n):
s = Decimal(stdin.readline())
A.append(s)
x = 1
for x in range(1,1001):
no = False
for i in range(n):
v1 = A[i]*Decimal(x)
v2 = (A[i] + Decimal('0.001'))*Decimal(x)
if int(v1) == v1:
v1 = int(v1)
else:
v1 = int(v1) + 1
if v1 < v2:
d = divide(v1,x)
if d != float(A[i]):
no = True
break
else:
no = True
break
if no == False:
break
print(x)
아니면 생각하기 싫다면 그냥 v = A[i]*x해놓고 int(v), int(v)+1 두가지 조사해보는것도 AC받기는 하는데
흠 뭔가 좀 아쉬워
n = int(stdin.readline())
A = []
for _ in range(n):
s = float(stdin.readline())
A.append(s)
x = 1
for x in range(1,1001):
no = False
for i in range(n):
v = A[i]*x
v1 = divide(int(v),x)
v2 = divide(int(v)+1,x)
if v1 != A[i] and v2 != A[i]:
no = True
break
if no == False:
break
print(x)
'정수론' 카테고리의 다른 글
10진수를 다른 진법으로 바꾸는 방법 (0) | 2024.09.01 |
---|---|
2^30으로 나눈 나머지로 2^5으로 나눈 나머지를 구할 수 있을까 (0) | 2024.08.10 |
골드바흐의 추측을 이용해 정수 n을 k개의 소수 합으로 표현하기 (0) | 2024.05.24 |
약수를 세는 것보다 배수를 세는 것이 더 쉽다(홀수인 약수의 합, 유일한 소인수의 개수를 구하는 방법) (0) | 2024.04.11 |
팩토리얼(factorial)의 소수 모듈로 곱셈의 역원(modulo inverse)을 구하는 기본적인 테크닉 (0) | 2024.04.05 |