【问题标题】:List comparison of element元素列表比较
【发布时间】:2018-11-02 03:14:29
【问题描述】:

我有一个问题,我很难解释,所以我会用很多例子来帮助大家理解,看看你是否能帮助我。

假设我有两个列表,其中包含两个人从最好到最差的书名。用户1评价lstA,用户2评价lstB

lstA = ['Harry Potter','1984','50 Shades','Dracula']
lstB = ['50 Shades','Dracula','1984','Harry Potter']

用户一认为“哈利波特”比“德古拉”好(HP为索引0,德古拉为索引3)

用户二认为“哈利波特”比德古拉差,(HP是索引3,德古拉是索引1)

在这种情况下,返回一个元组('Harry Potter', 'Dracula') [('Dracula', 'Harry Potter')也可以]

用户 1 的“50 种色调”也比“Dracula”好,用户 2 的“50 种色调”也比“Dracula”好(指数分别为 2、3 和 0、1)。在这种情况下,什么都不会发生。

程序的最终结果应该返回一个元组列表,

[('Harry Potter','50 Shades'), ('Harry Potter','Dracula'), ('Harry Potter','1984'), ('1984', '50 Shades'), ('1984','Dracula')]

有人可以帮我指出正确的方向,以提出一种给出所有元组的算法吗?

【问题讨论】:

  • 你可能想看看这个链接geeksforgeeks.org/counting-inversions它正是你想要的。
  • 您可能需要考虑通过单击旁边的绿色复选标记来选择答案。这会将您的问题从未回答的队列中删除。
  • 您似乎有不选择答案的习惯。您选择的每个答案都会获得一些声誉,并且您的问题将为未来的读者提供一个规范的答案。请选择对您有帮助的答案。

标签: python list sorting


【解决方案1】:

首先以数学方式制定您的逻辑。对于长度为 2 的所有组合,给定索引 idx_a1, idx_a2idx_b1, idx_b2,如果是 sign(idx_a1 - idx_a2) != sign(idx_b1 - idx_b2),则记录组合。

以下内容效率不高,但它显示了将此逻辑转换为代码的一种方法:

from itertools import combinations

lstA = ['Harry Potter','1984','50 Shades','Dracula']
lstB = ['50 Shades','Dracula','1984','Harry Potter']

def sign(x):
    """Return +1 if integer is positive, -1 if negative"""
    return (x > 0) - (x < 0)

res = []
for a, b in combinations(lstA, 2):
    idx_a1, idx_a2 = lstA.index(a), lstA.index(b)
    idx_b1, idx_b2 = lstB.index(a), lstB.index(b)
    if sign(idx_a1 - idx_a2) != sign(idx_b1 - idx_b2):
        res.append((a, b))

[('Harry Potter', '1984'),
 ('Harry Potter', '50 Shades'),
 ('Harry Potter', 'Dracula'),
 ('1984', '50 Shades'),
 ('1984', 'Dracula')]

【讨论】:

  • 我想我找到了一种完全不使用索引的方法。
  • 您好,我对“从 itertools 导入组合”不太熟悉,您能解释一下该功能的工作原理吗?目前,我正在使用嵌套的 for 循环编写,但还不能完全得到结果。
【解决方案2】:

这样做的一种方法是将每个列表中的所有正序累积到一个集合中,然后取这两个集合的差。当a 在其各自列表中位于b 之前时,正序将是(a, b)。这是itertools.combinations保证的顺序:

from itertools import combinations

setA = set(combinations(lstA, 2))
setB = set(combinations(lstB, 2))

result = setA - setB

这将简单地丢弃两组同意的任何顺序。如果两个列表有相同的书,这将几乎相同

result = setB - setA

唯一的区别是所有元组都会被反转。

如果您在每个列表中有不同的书籍,则需要添加几个额外的步骤来清理重复项并合并两组:

resultA = setA - setB
resultB = setB.difference(x[::-1] for x in setA)
result = resultA | resultB

第一步计算lstAlstB 不同意的所有元素。下一步找到lstB 的元素,这些元素不是我们在resultA 中的反转版本,因为两个列表中书籍的分歧保证在集合中被反转。我在这里使用set.difference 方法而不是- 运算符,因为这样就不需要从生成器表达式创建集合对象。不幸的是,您不能只使用symmetric_difference/^,因为这些元素是相反的。第三步只是计算结果的并集。

IDEOne 链接:https://ideone.com/DuHTed。这演示了问题中的原始案例和非对称列表。

【讨论】:

  • 不错!虽然可以保证您使用combinations(lstA, 2) 生成的所有订单都是“正订单”?
  • @slider。是的,这就是文档似乎要保证的 (docs.python.org/3/library/itertools.html#itertools.combinations),这证实了:ideone.com/dExkt4
  • 太棒了。基于此,我想我也可以再简化一点。
  • 我仍然没有得到 组合按字典排序顺序发出。因此,如果输入的可迭代对象已排序,则组合元组将按排序顺序生成。 显然,这里的列表不是按“字典排序顺序”排序的,据我所知,这意味着字母顺序。
  • @slider:希望有人为我们解决这个问题stackoverflow.com/q/53112861/2988730
【解决方案3】:

@jpp 解决方案的一个高效版本如下:

from itertools import combinations

lstA = ['Harry Potter','1984','50 Shades','Dracula']
lstB = ['50 Shades','Dracula','1984','Harry Potter']

bIndices = {b: i for i, b in enumerate(lstB)}
aPairs = [sorted(c) for c in combinations(enumerate(lstA), 2)]

mismatches = [(book1[1], book2[1]) for book1, book2 in aPairs if bIndices[book1[1]] > bIndices[book2[1]]]
print(mismatches)
# [('Harry Potter', '1984'), ('Harry Potter', '50 Shades'), ('Harry Potter', 'Dracula'), ('1984', '50 Shades'), ('1984', 'Dracula')]

请注意,aPairs 是 (index, book) 元组的组合,并且每个组合都按索引排序,这保证在每对书籍中,第一个比下一个“更好”(对于用户 A)。

现在要计算排序不匹配,我们只需要确定lstB 中的相应书籍索引是否也保留此排序。

编辑

正如@MadPhysicist 所指出的,combinations 在每个生成的元组中保留数组中的原始顺序,因此无需将aPairs 创建为已排序(index, book) 元组的列表。我们可以直接用bIndices生成mismatches

lstA = ['Harry Potter','1984','50 Shades','Dracula']
lstB = ['50 Shades','Dracula','1984','Harry Potter']

bIndices = {b: i for i, b in enumerate(lstB)}
mismatches = [(book1, book2) for book1, book2 in combinations(lstA, 2) if bIndices[book1] > bIndices[book2]]

【讨论】:

  • 我认为我的方式可能会进一步清理。
【解决方案4】:

你可以使用iter然后比较索引

res = []  

for i in lstA:
    a = iter(lstB)
    while True:
        try:
            b = next(a)
            if lstA.index(i) < lstA.index(b) and lstB.index(i) > lstB.index(b):
                res.append((i, b))
        except StopIteration:
            break 

print(res)
# [('Harry Potter', '50 Shades'), ('Harry Potter', 'Dracula'), ('Harry Potter', '1984'), ('1984', '50 Shades'), ('1984', 'Dracula')]

【讨论】:

  • 与其他答案相比,这似乎非常低效,但可能更容易理解。
  • @MadPhysicist 这怎么会降低效率,其他方法会创建额外浪费的组合然后过滤它们,这只会创建一个仅包含将使用的对的列表
  • 您正在为一件事对两个列表中的每个元素进行线性搜索。例如,您可以在外循环中使用enumerate 来避免lstA.index(i)。您的算法可能确实节省了一小部分空间,但代价是时间急剧增加。
  • @MadPhysicist 嗯,是的,我想,以前也有类似的问题,我用combinations 忽略了未使用的问题,MartijnPeters 指出它的效率有多低创建各种组合只是为了过滤掉一些
猜你喜欢
  • 2020-02-21
  • 2015-08-01
  • 2023-04-03
  • 2019-03-22
  • 2015-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多