通过重新排列列表构建尽可能多的数字

时间:2013-01-25 23:44:24

标签: python algorithm list python-2.7 integer

假设我有一组正整数;我想操纵顺序,以便结果数组的串联是可能的最大数字。例如,[97, 9, 13]会产生99713; [9,1,95,17,5]会产生9955171。我不确定答案。

7 个答案:

答案 0 :(得分:11)

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

答案 1 :(得分:8)

直观地说,我们可以看到反向排列的单位数字会导致最大数字:

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

所以反向排序应该有效。当输入中存在多位数片段时会出现问题。在这里,直觉再次允许我们在995之前17之前订购1,但为什么这样做呢?同样,如果它们的长度相同,那么如何对它们进行排序就很清楚了:

95 < 99
96 < 97
14 < 17

然后,诀窍是“扩展”较短的数字,以便可以将它们与较长的数字进行比较,并且可以按字典顺序自动排序。实际上,您需要做的就是重复代码段超出最大长度:

  • 比较995:改为比较9999595,因此999优先。
  • 比较117:改为比较1111717,因此1717优先。
  • 比较13213:改为比较1321321313,因此132132优先。
  • 比较232341:改为比较23232323412341,因此2341优先。

这是有效的,因为python只需比较两个片段,直到它们在某处不同;并且它(重复)在比较两个片段时需要跳过的匹配前缀,以确定他们需要在哪个顺序中形成一个最大的数字。

您只需要重复一个片段,直到它长于输入中的最长片段* 2,以确保在比较两个片段时可以找到第一个不匹配的数字。

您可以使用key的{​​{1}}参数执行此操作,但您需要先确定代码段的最大长度。使用该长度,您可以“填充”排序键中的所有片段,直到它们超过最大长度:

sorted()

其中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))) 将自己的代码段填充为长度超过s*(mlen//len(s)+1)

这给出了:

mlen

请注意,这个解决方案是a)3行简短,b)也适用于Python 3而不必求助于>>> 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) 和c)不强制解决方案(这就是functools.cmp_to_key()选项确实)。

答案 2 :(得分:5)

提示一:你连接字符串,而不是整数。 提示二:itertools.permutations()

答案 3 :(得分:3)

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

你可以使用itertools.permutations作为提示,并在使用join函数连接它们之后使用max函数的key参数(它告诉应用于每个元素的函数以决定最大值)。

开始使用字符串会更容易。

答案 4 :(得分:2)

我不喜欢蛮力的做法。对于大型集合,它需要大量的计算。

您可以为sorted内置方法编写自己的比较函数,该函数将根据您在函数中放入的任何逻辑返回任何对的排序参数。

示例代码:

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

答案 5 :(得分:1)

嗯,总是有蛮力的方法......

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

或者这是@Zah的答案的改编,它接收整数列表并返回一个整数,如问题中所述:

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

答案 6 :(得分:1)

你可以通过一些巧妙的排序来做到这一点。

如果两个字符串长度相同,请选择两个字符串中的较大字符串。容易。

如果它们的长度不同,请确定如果将最佳组合附加到较短的组合中,结果会是什么。由于较短的一切必须等于或小于它,您可以通过将短的一个附加到自身来确定这一点,直到它与较长的一个相同。一旦它们长度相同,就像以前一样进行直接比较。

如果第二个比较相等,你已经证明较短的字符串不可能比较长的字符串更好。根据与它配对的内容,它可能会变得更糟,所以应该首先考虑的时间越长。

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'

这比运行所有排列要有效得多。