对具有更高优先级正数的绝对值的整数列表进行排序

Sort a list of integers on absolute values with higher precedence positive number

如何根据整数的绝对值对整数列表进行排序,以便正数获得更高的优先级 -

样本输入

lst = [1, -3, 3, -3, 12, 10]

预期输出

[1, 3, -3, -3, 10, 12]

我现在可以用这样的代码来完成,但我对函数中的任意 0.1 感到困扰,想知道是否有更简洁的方法

我的代码

sorted(lst, key=lambda x: abs(x) if x >= 0 else abs(x) + 0.1)
# [1, 3, -3, -3, 10, 12]

另一种解决方案是使用元组作为排序键:

sorted(lst, key=lambda x: (abs(x), x < 0))

为了更好地理解它:

 1 ~ (1, False)  ~ (1, 0)
-3 ~ (3, True)   ~ (3, 1)
 3 ~ (3, False)  ~ (3, 0)
-3 ~ (3, True)   ~ (3, 1)
12 ~ (12, False) ~ (12, 0)
10 ~ (10, False) ~ (10, 0)

创建一个自定义比较器函数,其中 returns 一个大小为 2 的元组。元组的第一个值用于绝对值,第二个值用于对具有相同绝对值的负值和正值进行排序。

sorted(lst, key=lambda x: (abs(x), -x))
# [1, 3, -3, -3, 10, 12]

对于列表中的每个值,比较的值将是:

1  -> (1, -1)
3  -> (3, -3)
-3 -> (3,  3)
10 -> (10, -10)
12 -> (12, -12)

这样正值被推到每个组的开头,负值被推到结尾。

通常情况下,两种简单排序在这里可能比一种使用元组的排序更有效:

lst.sort(reverse=True)
lst.sort(key=abs)

这利用了排序功能的稳定性,因此保留了第一次排序的顺序以打破第二次排序中的平局。另请参阅 Python 关于此技术的 Sorting HOW TO

顺便说一句,如果没有不必要的 abs(您已经知道标志),您的会更短更快:

sorted(lst, key=lambda x: x if x >= 0 else 0.5 - x)

此外,排序函数针对所有值具有相同类型的情况进行了优化,因此如果我们简单地将 0.0 添加到 non-negative x 使其也成为 float ,此优化被使用。顺便说一句,在 Jupri 当前删除的答案中添加一个 . 修复了它,然后它非常整洁且同样快速。

诸如“只有 100 个元素长”的列表以及更长的列表之类的基准:

500 random integers from -500 to 500
 82.9 μs ± 0.2 μs  Kelly
113.6 μs ± 0.5 μs  original_mod2
117.5 μs ± 0.4 μs  Jupri
191.7 μs ± 0.3 μs  Yevhen
196.2 μs ± 0.6 μs  Ch3ster
215.0 μs ± 0.7 μs  original_mod1
233.5 μs ± 0.5 μs  original

100,000 random integers from -100,000 to 100,000
 39.4 ms ± 0.1 ms  Kelly
 40.2 ms ± 0.4 ms  original_mod2
 41.6 ms ± 0.3 ms  Jupri
 90.2 ms ± 0.5 ms  original_mod1
 94.9 ms ± 0.4 ms  original
115.9 ms ± 2.7 ms  Yevhen
131.5 ms ± 2.6 ms  Ch3ster

基准代码(Try it online!):

def original(lst):
    return sorted(lst, key=lambda x: abs(x) if x >= 0 else abs(x) + 0.1)

def original_mod1(lst):
    return sorted(lst, key=lambda x: x if x >= 0 else 0.5-x)

def original_mod2(lst):
    return sorted(lst, key=lambda x: x+0.0 if x >= 0 else 0.5-x)

def Kelly(lst):
    lst = sorted(lst, reverse=True)
    lst.sort(key=abs)
    return lst

def Yevhen(lst):
    return sorted(lst, key=lambda x: (abs(x), x < 0))

def Ch3ster(lst):
    return sorted(lst, key=lambda x: (abs(x), -x))

def Jupri(lst):
    return sorted(lst, key=lambda x: abs(x-.1))

funcs = [original, original_mod1, original_mod2, Kelly, Yevhen, Ch3ster, Jupri]

import random
from timeit import default_timer as timer
from statistics import mean, stdev

def test(n, repeats, unit):
    print(f'{n:,} random integers from {-n:,} to {n:,}')

    times = {func: [] for func in funcs}
    def stats(func):
        ts = sorted(times[func])[:3]
        mn = mean(ts)
        sd = stdev(ts)
        return f'{mn*1e3:5.1f} ms ± {sd*1e3:3.1f} ms ' if unit == 'ms' else f'{mn*1e6:5.1f} μs ± {sd*1e6:3.1f} μs '

    for _ in range(15):
        lst = random.choices(range(-n, n+1), k=n)
        expect = original(lst)
        random.shuffle(funcs)
        for func in funcs:
            t = 0
            for _ in range(repeats):
                t0 = timer()
                result = func(lst)
                t += (timer() - t0) / repeats
                assert result == expect
                del result
            times[func].append(t)

    for func in sorted(funcs, key=stats):
        print(stats(func), func.__name__)
    print()

test(500, 500, 'μs')
test(10**5, 1, 'ms')