Ответ 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:! Это потому, что метод __cmp__
больше не поддерживается в Python 3. Поэтому я изменил свой старый метод __cmp__
на _cmp
и использовал его для реализации всех 6 богатые методы сравнения.
Важное примечание
Я должен упомянуть, что эта функция сравнения немного странная: она не транзитивна, другими словами, a > b и b > c не обязательно означает a > c. А это означает, что результаты его использования в .sort()
непредсказуемы. Кажется, он делает правильные вещи для данных, которые я тестировал, например, он возвращает правильный результат для всех перестановок [1, 5, 10]
, но я думаю, на самом деле не следует доверять этому для всех входных данных.забастовкa >
Альтернативной стратегией, гарантирующей работу, является грубая сила: сгенерируйте все перестановки входного списка и найдите перестановку, которая дает максимальный результат. Но, надеюсь, существует более эффективный алгоритм, так как генерация всех перестановок большого списка довольно медленная.
Как отмечает Антти Хаапала в комментариях, мои старые функции сравнения были неустойчивыми при сравнении разных чисел, которые состоят из одних и тех же последовательностей повторяющихся цифр, например 123123 и 123123123. Такие последовательности должны сравниваться равными, мои старые функции не выполнялись что. Последняя модификация устраняет эту проблему.
Обновление
Оказывается, что mycmp() / _cmp()
фактически транзитивен. Он также стабилен, теперь он корректно обрабатывает корпус ab == ba
, поэтому он безопасен для использования с TimSort (или любым другим алгоритмом сортировки). И можно показать, что он дает тот же результат, что и ключевая функция Antti Haapala fractionalize()
.
В дальнейшем я буду использовать прописные буквы для представления целых чисел в списке, и я буду использовать строчную версию буквы для представления числа цифр в этом целочисленном. Например, a
- количество цифр в a
. Я буду использовать _
как оператор инфикса для представления конкатенации цифр. Например, A_B
- int(str(A)+str(B)
; обратите внимание, что A_B
имеет a+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 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()
, поскольку ключевой функции достаточно
найти перестановку, которая дает максимально возможное целое число
созданный путем конкатенации цифр. Подробности этого аргумента будут
оставил упражнение для читателя.:)