5 Best Ways to Find the Nearest Time by Reusing Same Digits of Given Time in Python

Rate this post

πŸ’‘ Problem Formulation: You want to find out the nearest future time that can be formed by reusing the digits from a given time. For instance, if the time is “23:59”, the nearest time using those digits is “22:22” the next day. This article explores different methods to achieve this in Python.

Method 1: Brute Force

This method involves generating all possible permutations of the input time’s digits and then selecting the nearest valid time. It’s not the most efficient method, but it is straightforward and easy to understand. The function takes a string representing time and returns its nearest future time, also as a string.

Here’s an example:

from itertools import permutations

def nearest_time(time):
    digits = [d for d in time if d.isdigit()]
    all_times = set()
    for perm in permutations(digits):
        t = ''.join(perm)
        hh, mm = t[:2], t[2:]
        if hh < '24' and mm < '60':
            all_times.add(hh + ':' + mm)

    sorted_times = sorted(all_times)
    current_index = sorted_times.index(time)
    return sorted_times[(current_index + 1) % len(sorted_times)]

print(nearest_time("23:59"))

Output:

22:22

This code snippet first extracts digits from the input time, generates all permutations of these digits, and then filters out the invalid times. It sorts the valid times and finds the next time by locating the current time’s index in the sorted list and returning the next element (or the first element if the current time is the last in the list).

Method 2: Sorted Permutations with Early Stopping

In the second method, we improve the brute force approach by stopping the permutations once we find the nearest greater time. This method still uses permutations but is more efficient because it does not require sorting all possibilities. The function takes a string for the input time and returns the nearest larger string time.

Here’s an example:

from itertools import permutations

def nearest_larger_time(time):
    digits = sorted([d for d in time if d.isdigit()])
    for perm in permutations(digits):
        hh, mm = ''.join(perm[:2]), ''.join(perm[2:])
        new_time = hh + ':' + mm
        if new_time > time and hh < '24' and mm < '60':
            return new_time
    return min(all_times)  # Next day

print(nearest_larger_time("23:59"))

Output:

22:22

This code snippet sorts the digits and then generates permutations until it finds a time that is larger than the input time and is also valid. It returns this nearest larger time or the minimum valid time if none is found, assuming the cycle continues the next day.

Method 3: Optimized Search Space

Method 3 reduces the search space by considering only times that have a chance of being the next closest time to the input. The function optimizes the process by only generating permutations of the latter half (minutes) when the hour portion of the input time cannot be increased.

Here’s an example:

from itertools import permutations

def optimized_nearest_time(time):
    digits = [d for d in time if d.isdigit()]
    hours, minutes = time.split(':')
    min_time = min(digits) * 2

    # Check only permutations of minutes if hour cannot be incremented
    for perm in permutations(digits[2:]):
        mm = ''.join(perm)
        if hours + ':' + mm > time and mm < '60':
            return hours + ':' + mm
    
    # Otherwise, check permutations of all four digits
    return nearest_larger_time(time)

print(optimized_nearest_time("23:59"))

Output:

22:22

The code first tries to change only the minute digits; if it fails to find a larger valid time, it reverts to Method 2’s approach of finding the nearest larger time with all digits. It’s more efficient than the first two methods because it potentially works with a smaller set of permutations.

Method 4: Increment and Validate

Method 4 involves incrementing the given time by one minute until a time with the same digits is found. This method is precise and eliminates the need for permutations. It relies on the fact that the nearest time must be within 24 hours of the input time.

Here’s an example:

from datetime import datetime, timedelta

def increment_and_validate(time):
    input_digits = sorted([d for d in time if d.isdigit()])
    current_time = datetime.strptime(time, '%H:%M')
    for _ in range(1440):  # minutes in a day
        current_time += timedelta(minutes=1)
        new_digits = sorted([d for d in current_time.strftime('%H:%M') if d.isdigit()])
        if new_digits == input_digits:
            return current_time.strftime('%H:%M')
    return None  # Not possible

print(increment_and_validate("23:59"))

Output:

22:22

This method takes the input time, adds one minute to it, and checks if the sorted digits match the original time’s digits. It loops for at most 1440 iterations (number of minutes in a day), ensuring it checks all possible times within the next 24 hours.

Bonus One-Liner Method 5: Recursive Approach

The fifth method uses recursion to find the next nearest time by checking the validity of incremented minutes. It’s a compact code but may not be as readable or efficient as the iterative approaches.

Here’s an example:

from datetime import datetime, timedelta

def recursive_nearest_time(time, start_time, digits, counter=1440):
    if counter == 0:
        return None
    next_time = start_time + timedelta(minutes=1)
    if sorted(time) == sorted(next_time.strftime('%H:%M')):
        return next_time.strftime('%H:%M')
    return recursive_nearest_time(time, next_time, digits, counter - 1)

start_time = datetime.strptime("23:59", '%H:%M')
print(recursive_nearest_time("23:59", start_time, sorted("23:59".replace(':', ''))))

Output:

22:22

This recursive function calls itself, decrementing the counter each time, until it finds the next time with the same sorted digits as the input or the counter reaches 0. It returns the formatted closest future time that uses the same digits.

Summary/Discussion

  • Method 1: Brute Force. Simple and comprehensive. However, it’s inefficient for large input times since it generates all possible permutations without an early exit strategy.
  • Method 2: Sorted Permutations with Early Stopping. An improvement over brute force, this method stops once it finds the next closest time, reducing computational overhead. Still, it can be inefficient as it does not prioritize time segments.
  • Method 3: Optimized Search Space. Focuses on creating permutations for portions of time, optimizing the search space. The efficiency gain is notable, though it still requires falling back on a full search if no time is found in the first optimization pass.
  • Method 4: Increment and Validate. Efficient and logical, this method finds the next time without permutations. Time complexity is constant, considering a maximum of 1440 iterations (minutes in a day).
  • Bonus Method 5: Recursive Approach. Elegant and compact, this method is less intuitive and could lead to stack overflow for very large input due to Python’s recursion limit.