Search code examples
pythonprefixes

leading number groups between two numbers


(Python) Given two numbers A and B. I need to find all nested "groups" of numbers:

range(2169800, 2171194)

leading numbers: 21698XX, 21699XX, 2170XX, 21710XX, 217110X, 217111X, 
217112X, 217113X, 217114X, 217115X, 217116X, 217117X, 217118X, 2171190X, 
2171191X, 2171192X, 2171193X, 2171194X

or like this:

range(1000, 1452)

leading numbers: 10XX, 11XX, 12XX, 13XX, 140X, 141X, 142X, 143X, 
144X, 1450, 1451, 1452

Solution

  • Harder than it first looked - pretty sure this is solid and will handle most boundary conditions. :) (There are few!!)

    def leading(a, b):
        # generate digit pairs a=123, b=456 -> [(1, 4), (2, 5), (3, 6)]
        zip_digits = zip(str(a), str(b))
        zip_digits = map(lambda (x,y):(int(x), int(y)), zip_digits)
    
        # this ignores problems where the last matching digits are 0 and 9
        # leading (12000, 12999) is same as leading(12, 12)
        while(zip_digits[-1] == (0,9)):         
            zip_digits.pop()            
    
        # start recursion
        return compute_leading(zip_digits)
    
    def compute_leading(zip_digits):
        if(len(zip_digits) == 1):   # 1 digit case is simple!! :)
            (a,b) = zip_digits.pop()
            return range(a, b+1)
    
        #now we partition the problem
        # given leading(123,456) we decompose this into 3 problems
        # lows    -> leading(123,129)
        # middle  -> leading(130,449) which we can recurse to leading(13,44)
        # highs   -> leading(450,456)
    
        last_digits = zip_digits.pop()
        low_prefix  = reduce(lambda x, y : 10 * x + y, [tup[0] for tup in zip_digits]) * 10     # base for lows e.g. 120
        high_prefix = reduce(lambda x, y : 10 * x + y, [tup[1] for tup in zip_digits]) * 10     # base for highs e.g. 450
        lows = range(low_prefix + last_digits[0], low_prefix + 10)
        highs = range(high_prefix + 0, high_prefix + last_digits[1] + 1)
    
        #check for boundary cases where lows or highs have all ten digits
        (a,b) = zip_digits.pop()    # pop last digits of middle so they can be adjusted
        if len(lows) == 10:
            lows = []
        else:
            a = a + 1
    
        if len(highs) == 10:
            highs = []
        else:
            b = b - 1
    
        zip_digits.append((a,b))    # push back last digits of middle after adjustments
    
        return lows + compute_leading(zip_digits) + highs       # and recurse - woohoo!!
    
    
    
    print leading(199,411)
    
    print leading(2169800, 2171194)
    
    print leading(1000, 1452)