【问题标题】:How to use Python3 sort key if the compare function requires values of items being compared?如果比较函数需要比较项目的值,如何使用 Python3 排序键?
【发布时间】:2017-06-21 21:38:05
【问题描述】:

下面是一个示例,其中我使用了一个比较器函数,该函数明确要求两个项目都存在 (x+y < y+x) 来提供比较。问题是我如何在不使用cmp_to_key 函数的情况下编写以下内容,因为key 只接受一个输入。

以下程序是该问题的解决方案:

给定一个非负整数列表,将它们排列成最大的数。

例如,给定[3, 30, 34, 5, 9],最大的形成数是9534330

from functors import cmp_to_key

def largestNumber(self, nums):
    numStr = [str(i) for i in nums]

    def str_cmp(x, y):
        if y+x < x+y: return -1
        elif y+x > x+y: return 1
        else: return 0

    numStr.sort(key=cmp_to_key(str_cmp))

    return "".join(numStr).lstrip('0') or '0'

【问题讨论】:

标签: python python-3.x sorting


【解决方案1】:

您可以编写一个自定义类,以您需要的方式实现__lt__(实现&lt; 比较的方法):

class Comp(object):
    def __init__(self, value):
        self.value = str(value)

    def __lt__(self, other):
        return other.value + self.value <= self.value + other.value

这应该产生相同的“排序”:

>>> sorted([3, 30, 34, 5, 9], key=Comp)
[9, 5, 34, 3, 30]

但我不确定这是否真的提供了 "total ordering"(它可以,我只是有些怀疑),如果不是,它实际上会产生意想不到的结果(在任何独立于 key 的 Python 版本中)或cmp 参数)。

【讨论】:

  • 这基本上是在重写cmp_to_key。也可以直接使用。
  • @wim 这个问题实际上只是要求一种不需要cmp_to_key 的方法。我有一个替代方案,但不幸的是 - 失败了。它仍然应该比cmp_to_key 更好,因为它不需要在进行排序之前将所有内容都转换为字符串,并且它可能会更快,因为它只需要 2 个字符串连接而不是 4 个(原始问题中的最坏情况)。但是,是的,基本上你是对的,只是重写cmp_to_key...
【解决方案2】:

只是为了另一种方式:

from itertools import cycle, islice

def extender(n):
    def extend(x):
        s = str(x)
        return "".join(islice(cycle(s), n))
    return extend

def biggest_number(input):
    if (len(input) == 0):
        return 0
    extend = extender(len(str(max(input))) * 2)
    s = sorted(input, key=extend, reverse=True)
    return int("".join(map(str, s)))

本质上,您获取数组的每个元素并根据需要通过重复使它们具有相同的长度。然后你做一个字典排序。 (此时数字排序是相同的,但完成后我们需要字符串。)

例如,对于[3, 30, 34, 5, 9],我们发现最长的数字是 2 位,因此我们通过根据需要重复数字来将所有数字扩展为 3 位。这些是使用的键:

[333, 303, 343, 555, 999]

然后我们对结果进行排序、降序和组装:

[9, 5, 34, 3, 30]
9534330

直觉来自于“选择具有最大前导数字的数字”。问题在于我们应该如何处理领带。例如,为什么我们要在 30 之前选择 3?答案是,在 3 之后可能出现的最大数字是另一个 3。(如果有更大的数字可用,我们早就选择了。)因此,将 3 视为“333333...”有助于我们选择正确对象,真爱。类似的问题:为什么我们会选择 10 而不是 100?这使我们意识到 10 之后的最佳结果是另一个以 10 开头的数字。(我们已经选择了 11 或更多。)因此将其视为“10101010...”和“100100100100...”。原来,你只需要扩展到n*2个数字,其中n是最长数字的长度。

我意识到这有点令人困惑。我写了一个测试来确保这一切都是正确的。 (它与您的原始代码进行比较。)

from functools import cmp_to_key
import random

def largestNumber(nums):
    numStr = [str(i) for i in nums]

    def str_cmp(x, y):
        if y+x < x+y: return -1
        elif y+x > x+y: return 1
        else: return 0

    numStr.sort(key=cmp_to_key(str_cmp))

    return "".join(numStr).lstrip('0') or '0'

for i in range(1000000):
    input = [random.randint(0, 1000) for _ in range(random.randint(0, 100))]
    if biggest_number(input) != int(largestNumber(input)):
        print("FAILED: {}".format(input))
    if i % 100 == 0:
        print(i)

我还没有找到不起作用的输入。我相当确信这段代码是正确的。

说了这么多,我不知道你为什么不想只使用cmp_to_key。 :-)

【讨论】:

  • 我想我发现了一个失败的输入,实际上。等我调查。 (此代码似乎不正确。)
  • 编辑代码以使用n*2 而不是n+1。我相信它现在是正确的。
  • 如果你有一个失败案例会很有趣,部分原因是我不确定原始代码是否真的提供了“总排序”。
  • 我已经丢失了我的失败案例,因为我修复了代码,但它类似于1121112...基本上,您需要进一步扩展才能看到差异。 (11211 == 11211,但11211121 &lt; 11211211)我现在认为将所有内容都设置为最大输入数的两倍是正确的。
猜你喜欢
  • 1970-01-01
  • 2014-03-13
  • 2019-03-25
  • 1970-01-01
  • 2012-05-27
  • 1970-01-01
  • 1970-01-01
  • 2021-11-09
  • 1970-01-01
相关资源
最近更新 更多