简体   繁体   English

按数字对数字进行排序

[英]Sort a number by its digits

I have to sort a vector of integers (all integers have the same length).我必须对整数向量进行排序(所有整数都具有相同的长度)。 Integers with the same first digit must be sorted in relation to the second digits, and numbers with the same: first and second digits are sorted by third digit etc. Also, the subsequent digits are sorted alternately (once ascending and once descending)具有相同第一位数字的整数必须相对于第二位数字进行排序,并且具有相同的数字:第一位和第二位数字按第三位数字等排序。此外,后续数字交替排序(一次升序和一次降序)

So when I have lis = [137, 944, 972, 978, 986] , I should get sorted_lis = [137, 986, 972, 978, 944]所以当我有lis = [137, 944, 972, 978, 986]时,我应该得到sorted_lis = [137, 986, 972, 978, 944]

I know how to sort by selecting digit (a)我知道如何通过选择数字进行排序 (a)

lis.sort(key=lambda x: int(str(x)[a]))

I've tried using insertion sort, (since I have to use a stable sorting algorithm)我试过使用插入排序,(因为我必须使用稳定的排序算法)

def insertion_sort(list):
    for i in range(len(list)):
        key = list[i]
        j = i-1
        while j >= 0 and key < list[j]:
            list[j+1] = list[j]
            j -= 1
        list[j+1] = key

    return list

You can define a key as follows:您可以按如下方式定义密钥:

lis = [137, 944, 972, 978, 986]

def alternate_digits(x):
    return [-d if i % 2 else d for i, d in enumerate(map(int, str(x)))]

output = sorted(lis, key=alternate_digits)
print(output) # [137, 986, 972, 978, 944]

The key alternate_digits , for example, converts 12345 into [1, -2, 3, -4, 5] .例如,键alternate_digits12345转换为[1, -2, 3, -4, 5]

Solution 1: multisort with ints解决方案 1:使用整数进行多排序

A fun multisort taking advantage of list.sort being stable (as explained in that sorting howto section):一个有趣的list.sort 稳定性(如排序方法部分所述):

lis = [137, 944, 972, 978, 986]

pow10 = 1
while pow10 <= lis[0]:
    lis.reverse()
    lis.sort(key=lambda x: x // pow10 % 10)
    pow10 *= 10

print(lis)  # [137, 986, 972, 978, 944]

This first sorts by last digit, then by second-to-last, etc, until sorting by first digit.这首先按最后一个数字排序,然后按倒数第二个等,直到按第一个数字排序。 And reverse between the sorts.并在种类之间反转。

Solution 2: "negate" every second digit解决方案 2:每隔一个数字“否定”一次

Another method, turning for example 1234 into 1735 (every second digit gets "negated", ie, subtracted from 9):另一种方法,例如将 1234 转换为 1735(每个第二个数字都被“否定”,即从 9 中减去):

def negate_every_second_digit(x):
    result = 0
    pow10 = 1
    while x:
        result = (x % 10 + 1) * pow10 - (result + 1)
        pow10 *= 10
        x //= 10
    return result
lis.sort(key=negate_every_second_digit)

Solution 3: multisort with characters解决方案 3:使用字符进行多重排序

Similar to your attempt, but converting to strings only once (at the expense of once converting back to ints at the end):与您的尝试类似,但仅转换为字符串一次(以最后一次转换回整数为代价):

lis[:] = map(str, lis)
for i in reversed(range(len(lis[0]))):
    lis.reverse()
    lis.sort(key=itemgetter(i))
lis[:] = map(int, lis)

Your solution, completed您的解决方案,已完成

Like you said you already know how to sort by a certain digit.就像你说的,你已经知道如何按某个数字排序。 You just need to do that for each:您只需要为每个执行此操作:

digits = len(str(lis[0]))
for a in reversed(range(digits)):
    lis.sort(key=lambda x: int(str(x)[a]),
             reverse=a % 2)

Benchmark基准

Benchmark with 100,000 random six-digit numbers:以 100,000 个随机六位数为基准:

Kelly1  201 ms  192 ms  196 ms
Kelly2  160 ms  154 ms  157 ms
Kelly3  248 ms  237 ms  243 ms
j1_lee  394 ms  396 ms  404 ms
OSA     409 ms  405 ms  419 ms

Benchmark code ( Try it online! ):基准代码( 在线试用! ):

def Kelly1(lis):
    pow10 = 1
    while pow10 <= lis[0]:
        lis.reverse()
        lis.sort(key=lambda x: x // pow10 % 10)
        pow10 *= 10

def Kelly2(lis):
    def negate_every_second_digit(x):
        result = 0
        pow10 = 1
        while x:
            result = (x % 10 + 1) * pow10 - (result + 1)
            pow10 *= 10
            x //= 10
        return result
    lis.sort(key=negate_every_second_digit)

def Kelly3(lis):
    lis[:] = map(str, lis)
    for i in reversed(range(len(lis[0]))):
        lis.reverse()
        lis.sort(key=itemgetter(i))
    lis[:] = map(int, lis)

# Modified by Kelly to sort in-place, as the question and my solutions do
def j1_lee(lis):
    def alternate_digits(x):
        return [-d if i % 2 else d for i, d in enumerate(map(int, str(x)))]
    lis.sort(key=alternate_digits)

# The question's attempt, completed by Kelly.
def OSA(lis):
    digits = len(str(lis[0]))
    for a in reversed(range(digits)):
        lis.sort(key=lambda x: int(str(x)[a]),
                 reverse=a % 2)

sorts = Kelly1, Kelly2, Kelly3, j1_lee, OSA

from timeit import timeit
import random
from operator import itemgetter

n = 100_000
digits = 6

times = {sort: [] for sort in sorts}
for _ in range(3):
    lis = random.choices(range(10**(digits-1), 10**digits), k=n)
    expect = None
    for sort in sorts:
        copy = lis.copy()
        time = timeit(lambda: sort(copy), number=1)
        times[sort].append(time)
        if expect is None:
            expect = copy
        else:
            assert copy == expect
for sort in sorts:
    print(f'{sort.__name__:6}',
          *(' %3d ms' % (t * 1e3) for t in times[sort]))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM