【问题标题】:Sort a list to form the largest possible number对列表进行排序以形成最大可能的数字
【发布时间】:2015-05-09 13:59:34
【问题描述】:

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

例如,给定[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

【问题讨论】:

标签: python python-2.7


【解决方案1】:

在 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 将比较函数转换为关键函数,但“手动”做起来并不难。

#!/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>c 并不必然隐含 a>c .这意味着在.sort() 中使用它的结果是不可预测的。它似乎确实对我测试过的数据做了正确的事情,例如,它为[1, 5, 10] 的所有排列返回正确的结果,但我想它真的不应该被信任为所有输入这样做。

保证可行的另一种策略是蛮力:生成输入列表的所有排列并找到产生最大结果的排列。但希望有一个更有效的算法,因为生成一个大列表的所有排列是相当慢的。


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


更新

事实证明,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()为关键功能就足够了 找到产生最大可能整数的排列 由数字连接产生。这个论点的细节将是 留给读者作为练习。 :)

【讨论】:

  • Py3 版本不适合我,我得到TypeError: unorderable types: cmpclass() &lt; cmpclass()
  • @StefanPochmann:我已经解决了这个问题。然而,正如我在重要说明中提到的,我的算法存在根本缺陷。幸运的是,Antti Hapala 发布了一个更好的解决方案。
  • 你怎么知道它不是传递的?
  • @StefanPochmann 它是传递的,相当于我的算法,当我问他怎么知道它是传递的时,我只是让 PM 2Ring 有点困惑......
  • @StefanPochmann 有一个小错误,作为彼此前缀的数字,比如 22222 是不稳定的......
【解决方案2】:

利用 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]

【讨论】:

  • 10 ** len(str(n)) - 1 其实好像挺快的
  • @AnttiHaapala 是的,这看起来确实快了一些。已更新,谢谢!
【解决方案3】:

这是一个丑陋的解决方案,它可以在不将cmp 比较函数传递给sorted 的情况下工作。基本上, key 函数获取每个数字并计算一个有理数,该数字为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 在数字中。

【讨论】:

  • 不如用key=lambda n:str(n)*100吧?
  • @AnttiHaapala 好的,但是使用Fraction 怎么样?
  • @StefanPochmann: 1) str(n)*100 有点浪费内存,但我想它不会减慢比较速度。 2) Fraction 很好,因为它允许列表包含对于浮点精度来说太大的 ints。 OTOH,Fraction 比较比 float 比较慢,因为它需要在(可能短路的)减法之上进行两次乘法。
  • 很遗憾整数没有与.bit_length() 等效的十进制。但我想你可以通过将位长除以 log10(2) 来计算 i 中的位数。
  • 除以 log2(10) 你的意思是 ;-)
【解决方案4】:

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

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 还很陌生,我会感谢 cmets/改进。

【讨论】:

    【解决方案5】:

    我很想从这里的所有 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))])
    

    【讨论】:

      【解决方案6】:

      这个版本适合我:

      def arrange(lst):
          for i in range(len(lst)):
              for j in range(i+1,len(lst)):
                  if int(str(lst[j]+lst[i])) > int(str(lst[i]+lst[j])):
                      temp = lst[i]
                      lst[i] = lst[j]
                      lst[j] = temp
          for i in lst:
              print(i, end="")
      
      lst = [i for i in input().split()]
      arrange(lst)
      

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案7】:
      def make_it_large_num(l):
          lst = [str(x) for x in l]
          print(sorted(lst, reverse=True))
          res = ''.join(sorted(lst, reverse=True))
          print(res)
      
      
      lst = [50,2,1,9]
      make_it_large_num(lst)
      

      这对我有用。简单且无需使用任何库(Python 3)。

      【讨论】:

        【解决方案8】:
        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))
        

        【讨论】:

        • 请考虑在此代码中添加一些解释。
        • 我认为这行不通,即使在给定的例子中;您的比较函数没有为 cmp 返回正确的值。
        • 匆匆忙忙地做了这件事,忘记了排序比较器返回 -1 或 1 而不是 0 和 1。但我的答案已经被@PM 2Ring 大致复制了,所以选择其中一个;)
        • @scpio:旧式比较函数需要返回三个不同的值,而不仅仅是两个。它需要分别为(&lt;, ==, &gt;)返回(negative, zero, positive)
        【解决方案9】:

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

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

        【讨论】:

        • a = [50, 5, 51, 59, 2, 1, 9, 98] 给出错误结果
        • Kostas,它会产生正确的结果,但如果在 32 位 Python 2 上运行,则需要 longpermutations 是在 2.6 中引入的,但您必须完成所有回到 2.2 来获得一个不会产生 longint(因此是不正确的)。在 Python 3 中,您将使用 map 而不是 imap,并且 int 已经可以处理任意精度。
        【解决方案10】:

        列表项

        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)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-11
          • 2016-04-30
          • 2022-10-18
          • 2014-06-21
          • 2018-03-08
          • 2013-06-26
          相关资源
          最近更新 更多