I have written a Python function called closest_multiple_10()
which takes an integer as input and returns the closest multiple of 10. The function works as expected, but its execution time is quite long, and I'm looking for ways to optimize it. Below is my current implementation:
def closest_multiple_10(i):
a = i
b = i
j = 0
c = 0
while a/10 != a//10:
j+=1
a+=1
while b/10 != b//10:
c+=1
b-=1
if j>c:
return i-c
else:
return i+j
I'm seeking suggestions for improvements in the code to make it more efficient and reduce the execution time. Any tips or alternative solutions would be greatly appreciated.
5 Answers 5
There is a very easy solution to this problem. The algorithm is:
- Get remainder of number with modulo by 10
- If remainder is less than 5, return number minus remainder
- Else, return number + (10 - remainder)
def closest_multiple_10(x):
rem = x % 10
return x - rem if rem < 5 else x + (10 - rem)
You can even expand this function to take in any multiple.
def closest_multiple(x, mul):
rem = x % mul
return x - rem if rem < (mul / 2) else x + (mul - rem)
-
1\$\begingroup\$
mul - rem
in the generic version. Also mul must be > 1. \$\endgroup\$slepic– slepic2023年05月09日 04:16:31 +00:00Commented May 9, 2023 at 4:16 -
\$\begingroup\$ Thank you very much, a wonderful solution from a creative developer \$\endgroup\$TAHER El Mehdi– TAHER El Mehdi2023年05月09日 13:29:18 +00:00Commented May 9, 2023 at 13:29
Check out the built-in function round(number, [ndigits])
. ndigits can be a negative number, which causes it to round to the left of the decimal point. For example,
round(127, -1) # returns 130
If the most significant digit of the part being rounded is a 5 round()
returns the nearest even value
round(125, -1) # returns 120
whereas your code always rounds up, so it returns 130.
If the rounding mode matters, the Decimal module provides a way to set the rounding mode using the context
.
import decimal
D = decimal.Decimal
getcontext().rounding = decimal.ROUND_UP
n = D(125)
round(n, -1) # returns D(1.3E+2)
Let us list the differences between the successive integers and their next multiple:
0 0 0
1 0 1
2 0 2
3 0 3
4 0 4
5 10 -5
6 10 -4
7 10 -3
8 10 -2
9 10 -1
10 10 0
11 10 1
12 10 2
13 10 3
14 10 4
15 20 -5
16 20 -4
17 20 -3
18 20 -2
...
and so on, periodically with period 10
. If you add 5 and shift up by 5 positions, you get a repetition of the integers 0 to 9. As you can see, the difference can be expressed as (n + 5) % 10 - 5
. So the rounding you are after can be written
(n + 5) - (n + 5) % 10.
More generally,
n+= m >> 1
n-= n % m
I don't know how you want to handle the negatives, so I did not care about this case.
The function has no docstring. I recommend adding one:
def closest_multiple_10(i):
"""Return the nearest multiple of 10 to i,
rounding up if halfway between two multiples."""
The algorithm is inefficient. Instead of a linear search up and down for a multiple of 10, we can use the %
operator to determine exactly how far above an exact multiple we begin. The expression i - (i % 10)
will round down to the next lowest multiple. To adapt that to round to the nearest, we can add 10/2 (i.e. 5) before rounding down:
i += 5
return i - i % 10
To round it all off, we can add type hints, and some unit tests:
def closest_multiple_10(i: int) -> int:
""" Return the nearest multiple of 10 to i, rounding up if halfway
between two multiples.
>>> closest_multiple_10(0)
0
>>> closest_multiple_10(1)
0
>>> closest_multiple_10(4)
0
>>> closest_multiple_10(5)
10
>>> closest_multiple_10(105)
110
>>> closest_multiple_10(114)
110
>>> closest_multiple_10(-106)
-110
>>> closest_multiple_10(-115)
-110
"""
i += 5
return i - i % 10
if __name__ == '__main__':
import doctest
doctest.testmod()
Exercise for the reader: add a parameter m
to be used instead of the fixed value 10
.
-
\$\begingroup\$ peps.python.org/pep-0257 : "For consistency, always use """triple double quotes""" around docstrings." - it's a bit long, too. \$\endgroup\$AcK– AcK2023年05月09日 08:09:28 +00:00Commented May 9, 2023 at 8:09
I suspect built-in's like round will always be faster, but a simple way to deal with negative numbers (but not negative or zero multipliers) is, with rounding away from 0:
def closest_multiple( number, base ):
if number < 0: return -closest_multiple( -number, base )
return base * ( ( number + base // 2 ) // base )
-
1\$\begingroup\$ Welcome to Code Review! You have presented an alternative solution, but haven't reviewed the code. Please edit to show what aspects of the question code prompted you to write this version, and in what ways it's an improvement over the original. It may be worth (re-)reading How to Answer. \$\endgroup\$Toby Speight– Toby Speight2023年05月09日 09:45:57 +00:00Commented May 9, 2023 at 9:45
-
1\$\begingroup\$ @TobySpeight: Seems to me that the actual problem is the question being posted on the wrong site: it's asking for a better algorithm, not really for a code-review. It's written as a Stack Overflow question. \$\endgroup\$Peter Cordes– Peter Cordes2023年05月09日 16:40:41 +00:00Commented May 9, 2023 at 16:40
Explore related questions
See similar questions with these tags.
return ((a+5) // 10) * 10
? \$\endgroup\$a+5
and round down.) \$\endgroup\$