【问题标题】:Sort a list by a key and sort another list in the same way as the first?按键对列表进行排序,然后以与第一个相同的方式对另一个列表进行排序?
【发布时间】:2019-02-21 19:46:16
【问题描述】:

例如有三个列表:

unsorted_key = ['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']
sorted_key = ['e', 'i', 'o', 'p', 'q', 'r', 't', 'u', 'w', 'y']
ciphertext = [
              ['u', 't', 'x', 'e'],
              ['p', 'r', 'k', 'p'],
              ['v', 'n', 'x', 'a'],
              ['n', 'h', 'e', 'x'],
              ['x', 'h', 'm', 's'],
              ['l', 'x', 'c', 'x'],
              ['x', 'c', 'y', 'a'],
              ['t', 'u', 'o', 'x'],
              ['e', 'r', 'm', 'e'],
              ['y', 'y', 'e', 'x']
             ]

是否可以按照sorted_key的顺序排序到unsorted_key中,按照密文的顺序排序呢?

当将 'q' 从 sorted_key[4] 移动到 sorted_key[0] 时,它应该将 ciphertext[4] 移动到 ciphertext[0]。

  • 所有三个列表的长度始终相同。
  • sorted_key 和 unsorted_key 永远不会有重复的元素。
  • sorted_key 将始终是 unsorted_key 的排序版本。

我一直在考虑,我能想到的唯一方法是使用辅助函数从unsorted_key的顺序动态生成并返回一个lambda函数,然后使用类似的东西:

sorted_key, ciphertext = (list(i) for i in zip(*sorted(zip(sorted_key, ciphertext), key=generate(unsorted_key))))

但我真的不知道 zip() 或 lambda 函数是如何工作的,也不知道如何将自定义排序顺序合二为一,或者是否甚至可以返回一个以在 sorted() 中使用。我真的无法解决这个问题,所以任何帮助将不胜感激!

【问题讨论】:

  • x 似乎没有出现在unsorted_key 中,但确实存在于ciphertext 的最后一行。缺失的元素应该如何排序?
  • 它应该将ciphertext[4] 移动到ciphertext[0] - 这些是列表 - 不是字符...
  • @PatrickArtner 'x' 是密文元素(列表)中的一个字符。密文只是一个列表,在这个例子中它恰好是一个列表列表,但它可以是任何东西的列表,重要的是密文中元素的顺序,而不是元素本身的内部顺序。

标签: python python-3.x sorting


【解决方案1】:

在线性时间内解决此问题的一种有效方法是创建一个将键映射到sorted_key 索引的字典,然后创建一个映射字典,将unsorted_key 的索引映射到sorted_key 的索引,基于相同的键,以便您可以通过ciphertext 的长度范围迭代索引以生成映射顺序的列表:

order = dict(map(reversed, enumerate(sorted_key)))
mapping = {i: order[k] for i, k in enumerate(unsorted_key)}
print([ciphertext[mapping[i]] for i in range(len(ciphertext))])

这个输出:

[['x', 'h', 'm', 's'], ['e', 'r', 'm', 'e'], ['u', 't', 'x', 'e'], ['l', 'x', 'c', 'x'], ['x', 'c', 'y', 'a'], ['y', 'y', 'e', 'x'], ['t', 'u', 'o', 'x'], ['p', 'r', 'k', 'p'], ['v', 'n', 'x', 'a'], ['n', 'h', 'e', 'x']]

【讨论】:

  • 谢谢,我对 python 函数和数据结构的不同复杂度了解不多,但你的可能是这里最快的,因为你甚至考虑了时间复杂度。 :)
【解决方案2】:

带有自定义键的内置sorted 可以为您完成:

sorted(ciphertext, key=lambda x: unsorted_key.index(sorted_key[ciphertext.index(x)]))

输出:

[['x', 'h', 'm', 's'], 
 ['e', 'r', 'm', 'e'], 
 ['u', 't', 'x', 'e'], 
 ['l', 'x', 'c', 'x'], 
 ['x', 'c', 'y', 'a'], 
 ['y', 'y', 'e', 'x'], 
 ['t', 'u', 'o', 'x'], 
 ['p', 'r', 'k', 'p'], 
 ['v', 'n', 'x', 'a'], 
 ['n', 'h', 'e', 'x']]

lambda 基本上可以归结为:

  1. 查找当前索引
  2. sorted_key中查找当前索引的值
  3. unsorted_key中查找sorted_key值的索引
  4. 排序

我不清楚的一件事是,如果最终结果与unsorted_key 相同,为什么需要“排序”sorted_key?如果是这样的话,只需sorted_key = unsorted_key[:] 就足够简单了。但是如果你真的也需要对sorted_key 进行排序,你可以这样做(实际上它会使lambda 更简单):

ciphertext, sorted_key = map(list, zip(*sorted(zip(ciphertext, sorted_key), key=lambda x: unsorted_key.index(x[1]))))

ciphertext
[['x', 'h', 'm', 's'], 
 ['e', 'r', 'm', 'e'], 
 ['u', 't', 'x', 'e'], 
 ['l', 'x', 'c', 'x'], 
 ['x', 'c', 'y', 'a'], 
 ['y', 'y', 'e', 'x'], 
 ['t', 'u', 'o', 'x'], 
 ['p', 'r', 'k', 'p'], 
 ['v', 'n', 'x', 'a'], 
 ['n', 'h', 'e', 'x']]

sorted_key
['q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p']

【讨论】:

  • 你说得对,我实际上不需要对任何一个密钥进行任何操作,只要正确的密文就足够了。在解释问题时,我可能有一些不清楚的措辞。你的绝对是最紧凑的解决方案,谢谢:)
【解决方案3】:

我不确定我是否明白这一点,但是...

首先确定动作(可以相反,我不清楚):

moves = [ [i, sorted_key.index(c)] for i, c in enumerate(unsorted_key) ]
#=> [[0, 4], [1, 8], [2, 0], [3, 5], [4, 6], [5, 9], [6, 7], [7, 1], [8, 2], [9, 3]]

可能在[i, sorted_key.index(c)] 中交换元素。

将移动应用到接收器 (res):

res = [ None for _ in range(len(ciphertext))]
for a, b in moves:
  res[a] = ciphertext[b]

所以输出应该是:

for line in res:
  print(line)

# ['x', 'h', 'm', 's']
# ['e', 'r', 'm', 'e']
# ['u', 't', 'x', 'e']
# ['l', 'x', 'c', 'x']
# ['x', 'c', 'y', 'a']
# ['y', 'y', 'e', 'x']
# ['t', 'u', 'o', 'x']
# ['p', 'r', 'k', 'p']
# ['v', 'n', 'x', 'a']
# ['n', 'h', 'e', 'x']


用于测试执行时间
import timeit, functools

def custom_sort(ciphertext, sorted_key, unsorted_key):
  return [ ciphertext[b] for _, b in [ [i, sorted_key.index(c)] for i, c in enumerate(unsorted_key) ] ]


custom_sort = timeit.Timer(functools.partial(custom_sort, ciphertext, sorted_key, unsorted_key))

print(custom_sort.timeit(20000))

【讨论】:

  • 谢谢,对我来说,这是最容易理解的解决方案,因为你把它放在动作的角度,这就是我最初的想法。
  • 很高兴为您提供帮助。它可以重写为一个班轮[ ciphertext[b] for _, b in [ [i, sorted_key.index(c)] for i, c in enumerate(unsorted_key) ] ],但我决定更清楚而不是更短。如果速度是一个问题,我编辑添加了一种测试它的方法。有一个衬垫,但你可以用任何方法做同样的事情。
【解决方案4】:

我不确定我是否正确理解了您的问题,但如果您尝试对未排序的密钥进行排序,并确保对密文进行相应排序,这应该可以满足您的要求:

pairs = zip(unsorted_key, ciphertext)
sorted_key = []
sorted_ciphertexts = []
for t in sorted(pairs):
   sorted_key.append(t[0])
   sorted_ciphertexts.append(t[1])

我确信可能有一种更优雅的方法可以做到这一点,但这将确保密钥和密文放在同一个索引处。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多