Constructing the largest number possible by rearranging a list

前端 未结 8 580
后悔当初
后悔当初 2020-12-16 11:53

Say I have an array of positive whole integers; I\'d like to manipulate the order so that the concatenation of the resultant array is the largest number possible. For exampl

相关标签:
8条回答
  • 2020-12-16 12:24
    import itertools
    def largestInt(a):
        b = list(itertools.permutations(a))
        c = []
        x = ""
        for i in xrange(len(b)):
            c.append(x.join(map(str, b[i])))
        return max(c)
    
    0 讨论(0)
  • 2020-12-16 12:26

    Intuitively, we can see that a reverse sort of single digit numbers would lead to the higest number:

    >>> ''.join(sorted(['1', '5', '2', '9'], reverse=True))
    '9521'
    

    so reverse sorting should work. The problem arises when there are multi-digit snippets in the input. Here, intuition again lets us order 9 before 95 and 17 before 1, but why does that work? Again, if they had been the same length, it would have been clear how to sort them:

    95 < 99
    96 < 97
    14 < 17
    

    The trick then, is to 'extend' shorter numbers so they can be compared with the longer ones and can be sorted automatically, lexicographically. All you need to do, really, is to repeat the snippet to beyond the maximum length:

    • comparing 9 and 95: compare 999 and 9595 instead and thus 999 comes first.
    • comparing 1 and 17: compare 111 and 1717 instead and thus 1717 comes first.
    • comparing 132 and 13: compare 132132 and 1313 instead and thus 132132 comes first.
    • comparing 23 and 2341: compare 232323 and 23412341 instead and thus 2341 comes first.

    This works because python only needs to compare the two snippets until they differ somewhere; and it's (repeating) matching prefixes that we need to skip when comparing two snippets to determine which order they need to be in to form a largest number.

    You only need to repeat a snippet until it is longer than the longest snippet * 2 in the input to guarantee that you can find the first non-matching digit when comparing two snippets.

    You can do this with a key argument to sorted(), but you need to determine the maximum length of the snippets first. Using that length, you can 'pad' all snippets in the sort key until they are longer than that maximum length:

    def largestpossible(snippets):
        snippets = [str(s) for s in snippets]
        mlen = max(len(s) for s in snippets) * 2  # double the length of the longest snippet
        return ''.join(sorted(snippets, reverse=True, key=lambda s: s*(mlen//len(s)+1)))
    

    where s*(mlen//len(s)+1) pads the snippet with itself to be more than mlen in length.

    This gives:

    >>> combos = {
    ...     '12012011': [1201, 120, 1],
    ...     '87887': [87, 878],
    ...     '99713': [97, 9, 13],
    ...     '9955171': [9, 1, 95, 17, 5],
    ...     '99799713': [97, 9, 13, 979],
    ...     '10100': [100, 10],
    ...     '13213': [13, 132],
    ...     '8788717': [87, 17, 878],
    ...     '93621221': [936, 21, 212],
    ...     '11101110': [1, 1101, 110],
    ... }
    >>> def test(f):
    ...     for k,v in combos.items():
    ...         print '{} -> {} ({})'.format(v, f(v), 'correct' if f(v) == k else 'incorrect, should be {}'.format(k))
    ... 
    >>> test(largestpossible)
    [97, 9, 13] -> 99713 (correct)
    [1, 1101, 110] -> 11101110 (correct)
    [936, 21, 212] -> 93621221 (correct)
    [13, 132] -> 13213 (correct)
    [97, 9, 13, 979] -> 99799713 (correct)
    [87, 878] -> 87887 (correct)
    [1201, 120, 1] -> 12012011 (correct)
    [100, 10] -> 10100 (correct)
    [9, 1, 95, 17, 5] -> 9955171 (correct)
    [87, 17, 878] -> 8788717 (correct)
    

    Note that this solution is a) 3 lines short and b) works on Python 3 as well without having to resort to functools.cmp_to_key() and c) does not bruteforce the solution (which is what the itertools.permutations option does).

    0 讨论(0)
提交回复
热议问题