Constructing the largest number possible by rearranging a list

你离开我真会死。 提交于 2019-12-18 03:06:10

问题


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 example [97, 9, 13] results in 99713; [9,1,95,17,5] results in 9955171. I'm not sure of an answer.


回答1:


sorted(x, cmp=lambda a, b: -1 if str(b)+str(a) < str(a)+str(b) else 1)




回答2:


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).




回答3:


Hint one: you concatenate strings, not integers. Hint two: itertools.permutations().




回答4:


import itertools
nums =  ["9", "97", "13"]
m = max(("".join(p) for p in itertools.permutations(nums)), key = int)

You can use itertools.permutations as hinted and use the key argument of the max function (which tells which function to apply to each element in order to decide the maximum) after you concat them with the join function.

It's easier to work with strings to begin with.




回答5:


I don't like the brute force approach to this. It requires a massive amount of computation for large sets.

You can write your own comparison function for the sorted builtin method, which will return a sorting parameter for any pair, based on any logic you put in the function.

Sample code:

def compareInts(a,b):
    # create string representations
    sa = str(a)
    sb = str(b)

    # compare character by character, left to right
    # up to first inequality
    # if you hit the end of one str before the other, 
    # and all is equal up til then, continue to next step
    for i in xrange(min(len(sa), len(sb))):
        if sa[i] > sb[i]:
            return 1
        elif sa[i] < sb[i]:
            return -1

    # if we got here, they are both identical up to the length of the shorter
    # one.
    # this means we need to compare the shorter number again to the 
    # remainder of the longer
    # at this point we need to know which is shorter
    if len(sa) > len(sb): # sa is longer, so slice it
        return compareInts(sa[len(sb):], sb)
    elif len(sa) < len(sb): # sb is longer, slice it
        return compareInts(sa, sb[len(sa):])
    else:
        # both are the same length, and therefore equal, return 0
        return 0



def NumberFromList(numlist):
    return int(''.join('{}'.format(n) for n in numlist))

nums = [97, 9, 13, 979]
sortednums = sorted(nums, cmp = compareInts, reverse = True)
print nums # [97, 9, 13, 979]
print sortednums # [9, 979, 97, 13]
print NumberFromList(sortednums) # 99799713



回答6:


Well, there's always the brute force approach...

from itertools import permutations
lst = [9, 1, 95, 17, 5]

max(int(''.join(str(x) for x in y)) for y in permutations(lst))
=> 9955171

Or this, an adaptation of @Zah's answer that receives a list of integers and returns an integer, as specified in the question:

int(max((''.join(y) for y in permutations(str(x) for x in lst)), key=int))
=> 9955171



回答7:


You can do this with some clever sorting.

If two strings are the same length, choose the larger of the two to come first. Easy.

If they're not the same length, figure out what would be the result if the best possible combination were appended to the shorter one. Since everything that follows the shorter one must be equal to or less than it, you can determine this by appending the short one to itself until it's the same size as the longer one. Once they're the same length you do a direct comparison as before.

If the second comparison is equal, you've proven that the shorter string can't possibly be better than the longer one. Depending on what it's paired with it could still come out worse, so the longer one should come first.

def compare(s1, s2):
    if len(s1) == len(s2):
        return -1 if s1 > s2 else int(s2 > s1)
    s1x, s2x = s1, s2
    m = max(len(s1), len(s2))
    while len(s1x) < m:
        s1x = s1x + s1
    s1x = s1x[:m]
    while len(s2x) < m:
        s2x = s2x + s2
    s2x = s2x[:m]
    return -1 if s1x > s2x or (s1x == s2x and len(s1) > len(s2)) else 1

def solve_puzzle(seq):
    return ''.join(sorted([str(x) for x in seq], cmp=compare))

>>> solve_puzzle([9, 1, 95, 17, 5])
'9955171'
>>> solve_puzzle([97, 9, 13])
'99713'
>>> solve_puzzle([936, 21, 212])
'93621221'
>>> solve_puzzle([87, 17, 878])
'8788717'
>>> solve_puzzle([97, 9, 13, 979])
'99799713'

This should be much more efficient than running through all the permutations.




回答8:


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)


来源:https://stackoverflow.com/questions/14532105/constructing-the-largest-number-possible-by-rearranging-a-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!