对列表进行排序以形成最大可能的数字

时间:2015-05-09 13:59:35

标签: python python-2.7

我正在尝试编写一个给出非负整数列表的函数,将它们排列成最大可能的数字。

例如,给定[50, 2, 1, 9],最大的数字是95021

以下是我尝试解决问题的代码:

a = [50, 2, 1, 9]
a.sort()
ans = []
for i in range(len(a)-1,-1,-1):
    ans.append(a[i])

print ''.join(map(str,ans))

但是,我得到50921,因为50是最大的,但它应首先显示9

8 个答案:

答案 0 :(得分:18)

在Python 2中,您可以使用传递给sort的适当比较函数来执行此操作。

#!/usr/bin/env python

''' Sort a list of non-negative integers so that
    if the integers were converted to string, concatenated 
    and converted back to int, the resulting int is the highest
    possible for that list

    From http://stackoverflow.com/q/30140796/4014959

    Written by PM 2Ring 2015.05.10

    Python 2 version
'''

data = [
    [50, 2, 1, 9],
    [10, 1],
    [2, 23, 21],
]

def mycmp(a, b):
    a, b = str(a), str(b)
    ab, ba = a + b, b + a
    if ab == ba:
        return 0
    if ab < ba:
        return -1
    return 1

for a in data:
    print 'In: ', a
    a.sort(cmp=mycmp, reverse=True)
    print 'Out:', a
    print

<强>输出

In:  [50, 2, 1, 9]
Out: [9, 50, 2, 1]

In:  [10, 1]
Out: [1, 10]

In:  [2, 23, 21]
Out: [23, 2, 21]

在Python 3中,sort不再需要自定义比较功能。 scpio的答案显示了如何使用functools将比较功能转换为关键功能,但这并不难做到&#34;手工&#34;。

#!/usr/bin/env python

''' Sort a list of non-negative integers so that
    if the integers were converted to string, concatenated 
    and converted back to int, the resulting int is the highest
    possible for that list

    From http://stackoverflow.com/q/30140796/4014959

    Written by PM 2Ring 2015.05.10

    Python 3 compatible version
'''

from __future__ import print_function

class cmpclass(object):
    def __init__(self, n):
        self.n = str(n)

    def __str__(self):
        return self.n

    def _cmp(self, other):
        a, b = self.n, str(other)
        ab, ba = a + b, b + a
        if ab == ba:
            return 0
        if ab < ba:
            return -1
        return 1

    def __lt__(self, other): return self._cmp(other) == -1
    def __le__(self, other): return self._cmp(other) <= 0
    def __eq__(self, other): return self._cmp(other) == 0
    def __ne__(self, other): return self._cmp(other) != 0
    def __gt__(self, other): return self._cmp(other) == 1
    def __ge__(self, other): return self._cmp(other) >= 0


data = [
    [50, 2, 1, 9],
    [10, 1],
    [2, 23, 21],
]

for a in data:
    print('In: ', a)
    a.sort(key=cmpclass, reverse=True)
    print('Out:', a)
    print('')

<强>输出

In:  [50, 2, 1, 9]
Out: [9, 50, 2, 1]

In:  [10, 1]
Out: [1, 10]

In:  [2, 23, 21]
Out: [23, 2, 21]

我发布的以前的Python 3兼容版本实际上并不适用于Python 3:oops :!这是因为Python 3不再支持__cmp__方法。因此,我已将旧的__cmp__方法更改为_cmp并将其用于实现所有6个方法rich comparison methods

重要提示

我必须提到这个比较函数有点奇怪:它是不可传递的,换句话说,a> b和b&gt; c不必必然暗示a> c。这意味着在.sort()中使用它的结果是不可预测的。它似乎对我测试过的数据做了正确的事情,例如,它为[1, 5, 10]的所有排列返回了正确的结果,但我想它真的不应该被信任到为所有输入都这样做。

保证工作的另一种策略是强力:生成输入列表的所有排列&amp;找到产生最大结果的排列。但希望有更高效的算法,因为生成大型列表的所有排列都相当慢。

正如Antti Haapala在评论中指出的那样,当比较由重复数字的相同序列组成的不同数字时,我的旧比较函数是不稳定的,例如123123和123123123.这些序列应该相等,我的旧函数没有&#39;那样做。最新修改解决了这个问题。

<强>更新

事实证明mycmp() / _cmp()实际 是传递性的。它现在也很稳定,因为它可以正确处理ab == ba案例,所以使用TimSort(或任何其他排序算法)是安全的。并且可以证明它与Antti Haapala的fractionalize()关键函数具有相同的结果。

在下文中,我将使用大写字母表示列表中的整数,并且我将使用字母的小写字母来表示该整数中的位数。例如,aA中的位数。我将_用作中缀运算符来表示数字连接。例如,A_Bint(str(A)+str(B);请注意A_Ba+b个数字。从算术,
A_B = A * 10**b + B

为了简洁起见,我将使用f()来表示Antti Haapala的fractionalize()关键功能。请注意f(A) = A / (10**a - 1)

现在为一些代数。我将它放在代码块中以保持格式简单。

Let A_B = B_A
A * 10**b + B = B * 10**a + A
A * 10**b - A = B * 10**a - B
A * (10**b - 1) = B * (10**a - 1)
A / (10**a - 1) = B / (10**b - 1)
f(A) = f(B)

So A_B = B_A if & only if f(A) = f(B)

Similarly,
A_B > B_A if & only if f(A) > f(B)
This proves that using mycmp() / _cmp() as the sort comparison function
is equivalent to using fractionalize() as the sort key function.

Note that
f(A_B) = (A * 10**b + B) / (10**(a+b)-1)
and
f(B_A) = (B * 10**a + A) / (10**(a+b)-1)

So f(A_B) = f(B_A) iff A_B = B_A, and f(A_B) > f(B_A) iff A_B > B_A

Let's see what happens with 3 integers.

f(A), f(B), f(C) are just real numbers, so comparing them is
transitive. 
And so if f(A) > f(B) and f(B) > f(C) then f(A) > f(C). 
This proves that mycmp() / _cmp() is also transitive.

Clearly, if f(A) > f(B) > f(C) then
A_B > B_A, B_C > C_B, A_C > C_A

Let B_C > C_B
For any A,
A * 10**(b+c) + B_C > A * 10**(b+c) + C_B
So A_B_C > A_C_B
i.e. adding the same integer to the beginning of B_C and C_B preserves
the inequality.

Let A_B > B_A
For any C,
(A_B) * 10**c + C > (B_A) * 10**c + C
So A_B_C > B_A_C,
i.e. adding the same integer to the end of A_B and B_A preserves the
inequality.

Using these results, we can show that
if f(A) > f(B) > f(C) then
A_B_C > A_C_B > C_A_B > C_B_A and
A_B_C > B_A_C > B_C_A > C_B_A.

This covers all 6 permutations of [A, B, C] and shows that A_B_C is the
largest possible integer for that list.

数学归纳式参数显示对任何列表进行排序 有限长度使用成对比较mycmp() / _cmp()作为 比较函数或fractionalize()作为关键函数就足够了 找到产生最大可能整数的排列 由数字串联产生。这个论点的细节将是 留给读者的练习。 :)

答案 1 :(得分:9)

One-liner使用Antti Haapala,PM 2Ring和Stefan Pochmann的见解:

from fractions import Fraction
sorted(a, key=lambda n: Fraction(n, 10**len(str(n))-1), reverse=True)

鉴于a = [50, 5, 51, 59, 2, 1, 9, 98]

[9, 98, 59, 5, 51, 50, 2, 1]

答案 2 :(得分:8)

这是一个丑陋的解决方案,无需将cmp比较函数传递给sorted即可运行。基本上,关键函数接受每个数字并计算具有该数字的有理数repeating decimals;

0   => 0
100 => 100/999 == 0.100100100...
10  => 10/99   == 0.1010101010...
1   => 1/9     == 0.1111111111...
11  => 11/99   == 0.1111111111...
12  => 12/99   == 0.1212121212...
9   => 9/9     == 1
99  => 99/99   == 1
999 => 999/999 == 1

0用排序键0排序为最小值,1后跟大多数零,键最接近0.1,因此排序第二小。由数字9组成的数字都具有等于1的排序键;如果您在9之前或之后对99进行排序并不重要。

使用这些值作为键进行排序必然会提供正确的输出,除非您使用的数字对于浮点精度来说太大了。 (可能早于2 ** 53

因此我们得到以下程序:

# for Python 2, not needed in Python 3
from __future__ import division

a = [50, 5, 51, 59, 2, 1, 9, 98]

def fractionalize(i):
    divisor = 9
    while divisor < i:
        divisor = 10 * divisor + 9 

    return i / divisor

print(sorted(a, key=fractionalize, reverse=True))

哪个产生

[9, 98, 59, 5, 51, 50, 2, 1]

由于我们在这里基本上计算i / (10 ** ceil(log10(i + 1)) - 1),所以还可以编写以下oneliner:

from math import ceil, log10

print(sorted(a, key=lambda i: i and i/(10**ceil(log10(i+1))-1), reverse=True))

i and部分保护除以零错误,以防0为数字。

答案 3 :(得分:1)

我希望我在这方面的变化不大。我的输入被转换为字符串列表。我生成排列列表,创建列表列表,然后将子列表从最小到最大排序。最后,我采用排序列表的最后一个元素。

import itertools

digits = ['50', '2', '1', '9']
perms = itertools.permutations(digits)
sorted_numlist = sorted(perms)
print sorted_numlist[-1]

如果您更愿意拥有数字而不是元素列表......

import itertools

digits = ['11', '68', '4', '12']
perms = itertools.permutations(digits)
numlist = []
for sublist in perms:
    permutated_num = "".join(sublist)
    numlist.append(int(permutated_num))

sorted_numlist = sorted(numlist)
print sorted_numlist[-1]

第二个实际上也用于显示第一个是在列表上正确排序。

我对Python很陌生,并希望得到评论/改进。

答案 4 :(得分:0)

import functools

def cmpr(x, y):
    xy = str(x) + str(y)
    yx = str(y) + str(x)
    return -1 if (xy > yx) else 1

a = [50, 2, 1, 9]
a.sort(key=functools.cmp_to_key(cmpr))

答案 5 :(得分:0)

最直接的方法是使用itertools.permutations()来模拟如何手动解决这个问题:

>>> from itertools import permutations, imap
>>> a = [50, 2, 1, 9]
>>> int(max(imap(''.join, permutations(map(str, a)))))
95021

答案 6 :(得分:0)

我很想从这里的所有python专家那里了解我的一线解决方案出了什么问题。 Leet代码网站不断拒绝失败的tcs,这在我的本地环境中效果很好。

from itertools import permutations as pm

def max_number(lst):

    if all(v == 0 for v in nums):
        return "0"

    lst1 = [str(item) for item in lst]
    return max([int(''.join(list(perm))) for perm in pm(lst, len(lst1))])

答案 7 :(得分:-1)


列表项

def create_largest_number(number_list):
    res=''
    for i in number_list:
        res= res+ str(i)
        new=''.join(sorted(res))
    return new[::-1]       

number_list=[23,45,67]
largest_number=create_largest_number(number_list)
print(largest_number)