5일, 10일, 20일 이동평균선 값으로 예약 구매를 하는 코드 작성하기 위해 특정 값을 가능한 가까운 호가로 변환하는 함수가 필요했습니다.
여러 생각끝에 다음의 조건을 만족하는 함수를 작성해봤습니다.
1. 입력 받은 price를 호가 단위에 맞는 가격으로 수정해서 return 한다.
2. number를 입력받아 1호가, 2호가, 3호가 등 호가 단위를 수정한다.
처음엔 다음과 같이 코딩을 했었는데
def price_refine(price: int, number: int = 1) -> int:
if price < 2000:
return price
elif price < 5000:
return int(5 * (int(price / 5) + number))
elif price < 20000:
return int(10 * (int(price / 10) + number))
elif price < 50000:
return int(50 * (int(price / 50) + number))
elif price < 200000:
return int(100 * (int(price / 100) + number))
elif price < 500000:
return int(500 * (int(price / 500) + number))
else:
return int(1000 * (int(price / 1000) + number))
이 함수는 호가를 정확히 구할 수 없었습니다.
예를 들어 price=19998, number=5일 경우 19998의 5번째 호가를 구하는 것인데 해당 함수의 return은 20040인데 원하는 결과는 20200입니다.
price가 20000부터 호가 단위가 변경되기 때문에 함수를 수정할 필요가 있었습니다.
def price_refine(price: int, number: int = 0) -> int:
PRICE_LEVELS = [(2000, 1), (5000, 5), (20000, 10), (50000, 50), (200000, 100), (500000, 500), (float('inf'), 1000)]
if number == 0:
for level_price, adjustment in PRICE_LEVELS:
if price < level_price or level_price == float('inf'):
return round(price / adjustment) * adjustment
increase = number > 0
number_of_adjustments = abs(number)
for _ in range(number_of_adjustments):
for level_price, adjustment in PRICE_LEVELS:
if (increase and price < level_price) or level_price == float('inf'):
price = (math.trunc(price / adjustment) + 1) * adjustment
break
elif (not increase and price <= level_price) or level_price == float('inf'):
price = (math.ceil(price / adjustment) - 1) * adjustment
break
return price
함수를 위와 같이 수정했습니다.
기본 가격 조정: number가 0일 경우, price는 가장 가까운 호가로 수정됩니다.
예를 들어, price가 4500일 때 PRICE_LEVELS에 따라 5000 미만이므로, 5로 나눈 후 가장 가까운 정수로 반올림하여 다시 5를 곱해 반올림 처리합니다 (즉, 4500은 4500으로 유지).
가격 증감 조정: number가 0이 아닐 때, number의 절대값만큼 가격을 증가시키거나 감소시킵니다.
number가 양수일 경우 (increase = True), 가격을 증가시킵니다. price를 해당 단계의 adjustment로 나눈 후 내림 처리하고, 1을 더한 후 다시 adjustment를 곱해 증가시킵니다.
number가 음수일 경우 (increase = False), 가격을 감소시킵니다. price를 adjustment로 나눈 후 올림 처리하고, 1을 빼고 adjustment를 곱해 감소시킵니다.
각 반복마다 가격을 새로 조정하고, 새로운 가격이 다음 level_price를 초과하지 않을 때까지 이 과정을 반복합니다.
이 함수도 for문이 중첩되어 있어 효율적이지 않아보였습니다.
PRICE_LEVELS에 따라 adjustment가 결정되면 다음 PRICE_LEVELS까지 같은 값을 올리기 때문에 좀 더 효율적으로 수정해보았습니다.
def price_refine(price: int, number: int = 0) -> int:
PRICE_LEVELS = [(2000, 1), (5000, 5), (20000, 10), (50000, 50), (200000, 100), (500000, 500), (float('inf'), 1000)]
for level_price, adjustment in PRICE_LEVELS:
if price < level_price or level_price == float('inf'):
adjusted_price = round(price / adjustment) * adjustment
if (number > 0 and price < adjusted_price) or (number < 0 and price > adjusted_price):
number += -1 if number > 0 else 1
price = adjusted_price
break
increase = number > 0
number_of_adjustments = abs(number)
index = 0
while number_of_adjustments > 0:
level_price, adjustment = PRICE_LEVELS[index]
if level_price == float('inf') or (increase and price < level_price) or (not increase and price <= level_price):
max_steps_within_level = min(number_of_adjustments, math.ceil((level_price - price) / adjustment))
if not increase:
max_steps_within_level = min(number_of_adjustments, math.ceil((price - PRICE_LEVELS[index - 1][0]) / adjustment))
price += (adjustment * max_steps_within_level * (1 if increase else -1))
number_of_adjustments -= max_steps_within_level
index += 1
return price
위와 같이 수정하여 반복문 횟수를 줄였습니다만 가장 효율적인지는 모르겠습니다.
또한 효율적인 것도 좋지만 가독성도 좋아야 되는데 뭔가 복잡한 느낌입니다.