有没有办法生成项目列表的所有唯一排列

Posted

技术标签:

【中文标题】有没有办法生成项目列表的所有唯一排列【英文标题】:is there a way to generate all unique permutations of a list of items 【发布时间】:2019-10-10 13:27:58 【问题描述】:

我有一个包含五个属性的列表,每个属性有五个不同的值。我想生成它们的笛卡尔积并过滤所有独特的排列。

一些背景:

我需要它们作为我的输入值来解决逻辑难题。我检查规则以找到正确的解决方案。

from itertools import product

# input
names = ['Dana', 'Ingo', 'Jessica', 'Sören', 'Valerie']
ages = [26, 27, 30, 33, 35]
tops = ['Blouse', 'Poloshirt', 'Pullover', 'Sweatshirt', 'T-Shirt']
colors = ['blue', 'yellow', 'green', 'red', 'black']
sizes = ['XS', 'S', 'M', 'L', 'XL']

all_attributes = [names, ages, tops, colors, sizes]

# cartesian product (superset)
inputs = list(product(*all_attributes))

# the following code you do that...

也许一个简化的例子可以说清楚。

数据:

[['Dana', 'Ingo'], [26, 27]]

数据的笛卡尔积:

[('Dana', 26), ('Dana', 27), ('Ingo', 26), ('Ingo', 27)]

我想要什么:

[[('Dana', 26), ('Ingo', 27)],
 [('Dana', 27), ('Ingo', 26)],
 [('Ingo', 26), ('Dana', 27)],
 [('Ingo', 27), ('Dana', 26)]]

我不想要的:

[[('Dana', 26), ('Ingo', 26)], ...

我不想多次出现相同的值。这个地方很重要,因此它应该具有置换特征,并且对于具有五个元素的列表列表而言。我猜输出大小会很大,可能无法计算,所以最好指定一些固定的位置值。例如,我想将“Dana”设置为第一个元素名称。

输出:

[[('Dana', 26), ('Ingo', 27),
 [('Dana', 27), ('Ingo', 26)]]

也许您可以出于好奇告诉我,这些概念的具体数学名称是什么,我需要哪些?


谜题:

五个朋友(Dana、Ingo、Jessica、Sören、Valerie)在购物中心的收银台前排队等候。他们都是不同年龄的人(26、27、30、33、35)并且想购买不同的上衣(衬衫、马球衫、套头衫、运动衫、T恤)为自己。上衣有不同的颜色(蓝色、黄色、绿色、红色、黑色)和尺寸(XS、S、M、L、XL)

规则:

    “Dana”最想购买的是“XL”。在她身后(但不是直接在后面)是一个穿着“黑色”上衣的人。 “杰西卡”直接在一个想买“Poloshirt”的人面前等着。 排队的第二个人想买一件“黄色”上衣。 “T 恤”不是“红色”。 “Sören”想买一件“运动衫”。直接在他前面等候的人比他后面的人年长。 “Ingo”需要一个“L”号的上衣。 最后一个排队的人是 30 岁。 年纪最大的人会买最小尺寸的上衣。 直接在“Valerie”后面等候的人想买一件比“S”号大的“红色”上衣。 最年轻的人想买一件“黄色”上衣。 杰西卡要买一件“衬衫”。 排队等候的第三个人想买一件“M”码的上衣。 “Poloshirt”是“红色”或“黄色”或“绿色”。

【问题讨论】:

谢谢。让我看看我能不能找到一种不会改变的方式。 第一个简化:再添加一个列表,也就是队列中的点。不再需要排列,只需一个产品。 5层中的每一层都有5个!排列方式:现在搜索空间为5*120。更易于管理。 好的,我知道怎么做,但是处理有点长。我将在 GitHub 上发布一个模块并引用它。它基本上是此类问题的通用求解器。我使用邻接矩阵图表示。问题空间只有几百个元素,因此运行速度非常快。 我已经完成了。在 readthedocs、pypi、github 上得到它。享受吧。 FWIW,我刚刚通过阅读标签建议发现这被称为斑马拼图。 【参考方案1】:

这会做到,但需要很长时间。我减少了列表大小,因为您要求的选项有 24,883,200,000 个排列:

from itertools import permutations, product

names = ['Dana', 'Ingo']
ages = [26, 27]
tops = ['Hemd', 'Poloshirt']
colors = ['blau', 'gelb']
sizes = ['XS', 'S']

options = []

# Generate the Cartesian product of all permutations of the options.
for name,age,top,color,size in product(*map(permutations,[names,ages,tops,colors,sizes])):
    # Build the option list. zip() transposes the individual lists.
    option = list(zip(name,age,top,color,size))
    options.append(option)
    print(option)

输出:

[('Dana', 26, 'Hemd', 'blau', 'XS'), ('Ingo', 27, 'Poloshirt', 'gelb', 'S')]
[('Dana', 26, 'Hemd', 'blau', 'S'), ('Ingo', 27, 'Poloshirt', 'gelb', 'XS')]
[('Dana', 26, 'Hemd', 'gelb', 'XS'), ('Ingo', 27, 'Poloshirt', 'blau', 'S')]
[('Dana', 26, 'Hemd', 'gelb', 'S'), ('Ingo', 27, 'Poloshirt', 'blau', 'XS')]
[('Dana', 26, 'Poloshirt', 'blau', 'XS'), ('Ingo', 27, 'Hemd', 'gelb', 'S')]
[('Dana', 26, 'Poloshirt', 'blau', 'S'), ('Ingo', 27, 'Hemd', 'gelb', 'XS')]
[('Dana', 26, 'Poloshirt', 'gelb', 'XS'), ('Ingo', 27, 'Hemd', 'blau', 'S')]
[('Dana', 26, 'Poloshirt', 'gelb', 'S'), ('Ingo', 27, 'Hemd', 'blau', 'XS')]
[('Dana', 27, 'Hemd', 'blau', 'XS'), ('Ingo', 26, 'Poloshirt', 'gelb', 'S')]
[('Dana', 27, 'Hemd', 'blau', 'S'), ('Ingo', 26, 'Poloshirt', 'gelb', 'XS')]
[('Dana', 27, 'Hemd', 'gelb', 'XS'), ('Ingo', 26, 'Poloshirt', 'blau', 'S')]
[('Dana', 27, 'Hemd', 'gelb', 'S'), ('Ingo', 26, 'Poloshirt', 'blau', 'XS')]
[('Dana', 27, 'Poloshirt', 'blau', 'XS'), ('Ingo', 26, 'Hemd', 'gelb', 'S')]
[('Dana', 27, 'Poloshirt', 'blau', 'S'), ('Ingo', 26, 'Hemd', 'gelb', 'XS')]
[('Dana', 27, 'Poloshirt', 'gelb', 'XS'), ('Ingo', 26, 'Hemd', 'blau', 'S')]
[('Dana', 27, 'Poloshirt', 'gelb', 'S'), ('Ingo', 26, 'Hemd', 'blau', 'XS')]
[('Ingo', 26, 'Hemd', 'blau', 'XS'), ('Dana', 27, 'Poloshirt', 'gelb', 'S')]
[('Ingo', 26, 'Hemd', 'blau', 'S'), ('Dana', 27, 'Poloshirt', 'gelb', 'XS')]
[('Ingo', 26, 'Hemd', 'gelb', 'XS'), ('Dana', 27, 'Poloshirt', 'blau', 'S')]
[('Ingo', 26, 'Hemd', 'gelb', 'S'), ('Dana', 27, 'Poloshirt', 'blau', 'XS')]
[('Ingo', 26, 'Poloshirt', 'blau', 'XS'), ('Dana', 27, 'Hemd', 'gelb', 'S')]
[('Ingo', 26, 'Poloshirt', 'blau', 'S'), ('Dana', 27, 'Hemd', 'gelb', 'XS')]
[('Ingo', 26, 'Poloshirt', 'gelb', 'XS'), ('Dana', 27, 'Hemd', 'blau', 'S')]
[('Ingo', 26, 'Poloshirt', 'gelb', 'S'), ('Dana', 27, 'Hemd', 'blau', 'XS')]
[('Ingo', 27, 'Hemd', 'blau', 'XS'), ('Dana', 26, 'Poloshirt', 'gelb', 'S')]
[('Ingo', 27, 'Hemd', 'blau', 'S'), ('Dana', 26, 'Poloshirt', 'gelb', 'XS')]
[('Ingo', 27, 'Hemd', 'gelb', 'XS'), ('Dana', 26, 'Poloshirt', 'blau', 'S')]
[('Ingo', 27, 'Hemd', 'gelb', 'S'), ('Dana', 26, 'Poloshirt', 'blau', 'XS')]
[('Ingo', 27, 'Poloshirt', 'blau', 'XS'), ('Dana', 26, 'Hemd', 'gelb', 'S')]
[('Ingo', 27, 'Poloshirt', 'blau', 'S'), ('Dana', 26, 'Hemd', 'gelb', 'XS')]
[('Ingo', 27, 'Poloshirt', 'gelb', 'XS'), ('Dana', 26, 'Hemd', 'blau', 'S')]
[('Ingo', 27, 'Poloshirt', 'gelb', 'S'), ('Dana', 26, 'Hemd', 'blau', 'XS')]

【讨论】:

列表项的顺序不应按元素名称排序。 `[('Sören',..), ('Dana',...),...] 也应该是输入的示例。 @Algore87 为什么?对于以不同顺序列出的人员来说,这只是相同的组合。如果您真的想要,只需将名称添加到迭代中。我会用更短的代码来更新,因为这就是你想要的。你的逻辑谜题是否关心人们的排列顺序? 我需要该输入来解决逻辑难题。在那个谜题中,您有一个队列,那些具有不同属性排列的人在其中等待。应该有可能输入像 [('Dana', 33, 'T-Shirt', 'blau', 'XL), ('Sören', 26, 'Sweatshirt', 'gelb', 'S') , ('Jessica', 27, 'Hemd', 'schwarz', 'M'), ('Valerie', 35, 'Poloshirt', 'XS'), ('Ingo', 30, 'Pulover', 'rot ','L')]。我猜输入大小变得非常大,所以我应该指定一些固定值。例如,我想在位置 5 上使用“Ingo”,第二个人的年龄等于 26。这应该会在生成时缩小输入大小 你能把拼图贴出来吗?我现在很好奇 @Algore。您可以将其编辑到问题的末尾。根据我们对彼此语言的相对技能,我认为如果你翻译它会快得多。【参考方案2】:

对于您所拥有的逻辑难题类型使用排列的方法存在一个基本问题。问题甚至不在于它们太多以至于您的求解器不太可能在合理的时间内完成。问题是您没有针对问题检查规则的自动方法:除非您有方法来验证它们,否则将所有可能性摆在您面前是毫无意义的。

为了解决这些问题,我创建了一个名为 Puzzle Solvers 的项目:

https://github.com/madphysicist/puzzle-solvers https://pypi.org/project/puzzle-solvers/ https://puzzle-solvers.readthedocs.io/en/latest/

这是一个小项目,目前包含一个感兴趣的类别:puzzle_solvers.elimination.Solver。此类实现了解决问题中提出的消除过程类型问题所需的大部分操作。所有的逻辑都是documented,tutorial 是您的确切问题的演练。我将在这里解释基础知识,以便您了解我做了什么,并可能改进它。

第 1 步是识别队列中的位置是一个属性,就像年龄、姓名等一样。这意味着顺序不再相关。

第 2 步是认识到这是一个变相的图形问题。您有 30 个节点:五个个体中每个个体的所有可能属性(其中六个)。图表开始时几乎是完整的。仅缺少给定类型的属性之间的边,因此您从 375 条而不是完整的 435 条边开始。

最终目标是在图中的五个连通分量中的每一类属性之间有一条边。因此最终的边数为 5 * 15 = 75。

那么如何去除边缘?像“'T 恤'不是'红色'”这样的简单规则非常简单:只需删除那个边缘。像“排队的最后一个人是 30 岁”这样的规则。也很简单,并且在去除边缘方面更有利可图。年龄不是 30 和位置 5 之间的所有边都被删除,以及位置不是 5 和年龄 30 之间的所有边缘。我添加了几个半生不熟的实用程序包装器来检查大于和小于条件和删除代表不可能组合的边。

求解器最重要的方面是,只要删除一条边,它就会完全遵循该操作的所有逻辑含义。想象一下,你有“‘Poloshirt’是‘红色’或‘黄色’或‘绿色’”,并且你在谜题中达到了一个点,这三种颜色都与 30 岁无关。这意味着无论是谁穿马球衫不能30岁。事实上,'Poloshirt' 不能有 any 边缘,其端点不被这三种颜色共享。如果你递归地遵循这些推论,你就会得到一个完整的解决方案。

我很抱歉我的包裹被无耻地出售了,但出于正当理由,我写它只是为了回答这个问题。

【讨论】:

你说得对,问题演变成拼图问题。一开始我只是想问一下如何生成各种输入。我通过编写我检查的逻辑函数来解决这些难题。我用所有规则函数填充一个容器,遍历它们并检查是否每个人都返回 true。那是我选择的方法,因为这是我想到的第一个方法。我很高兴你构建了那个求解器。很快就会检查出来!非常感谢。 @Algore87。在几乎所有情况下,组合方法都非常昂贵,因此我通常假设在这种情况下存在 XY 问题。这就是图论如此重要的原因。它基本上是一个以立即缩小问题空间的方式表示问题的巨大工具。 @Algore87。我已将我的包更新到 v0.0.1b1。它的大小大约翻了一番,我现在可以在没有任何循环的情况下解决原来的斑马拼图。不过,我仍然没有设法在一个单一的陈述中处理他在你的难题中的第一条规则。在不相关的注释中,您可能应该选择一个答案。

以上是关于有没有办法生成项目列表的所有唯一排列的主要内容,如果未能解决你的问题,请参考以下文章

计算包含某些项目的列表集的总唯一排列的算法

列表理解,检查项目是不是唯一

生成具有重复元素的列表排列

有没有办法通过signalR中的唯一指导正确地将消息发送给所有用户?

如何迭代 Rust 中序列的所有唯一排列?

AppSync GraphQL 模拟解析器映射未生成唯一项目