RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1605218
Accepted
Камиль Газизов
Камиль Газизов
Asked:2025-01-21 00:21:23 +0000 UTC2025-01-21 00:21:23 +0000 UTC 2025-01-21 00:21:23 +0000 UTC

通过 itertools.permutations() 函数生成条件

  • 772

有一个关于使用permutationsitertools 库中的函数进行条件生成的问题:

有如下代码:

combinations = list(itertools.permutations([1, 2, 3], 3))

其执行结果我们得到:

(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)

这些都是给定数字组合的所有可能变化。是否可以生成(即生成,而不是显示所有已生成的变量中所需的变量)指示它们应该开始的条件的特定变量?也就是说,像这样:

combinations = list(itertools.permutations([1, 2, 3], 3, only 1)

结果:

(1, 2, 3), (1, 3, 2)
python
  • 4 4 个回答
  • 70 Views

4 个回答

  • Voted
  1. Stanislav Volodarskiy
    2025-01-21T06:18:40Z2025-01-21T06:18:40Z

    注意:这个想法已经在评论中描述过。在我看来,我只想给出正确的实现。

    我们将“第一个值”从列表中排除(如果存在)。我们生成没有它的排列,将其添加到每个排列的开头:

    def limited_permutations(iterable, first_value, r=None):
        values = list(iterable)
        if first_value in values:
            i = values.index(first_value)
            values = values[:i] + values[i + 1:]
            if r is not None:
                r -= 1
        return (
            (first_value, ) + p
            for p in itertools.permutations(values, r=r)
        )
    
    • 3
  2. LolPopGames
    2025-01-21T03:16:31Z2025-01-21T03:16:31Z

    答案是根据@aleksandrbarakin的评论给出的。它由“General”标志表示。

    # Суть алга - убирать первый элемент,  
    # Генерировать без него, 
    # А затем результаты добовлять этот элемент ко всем сгенерированным результатам 
    
    def permutations(variants: list, v_len: int, first_element) -> list:
        variants.remove(first_element) # Удаляем "first_element"
    
        combinations = list(itertools.permutations(variants, v_len-1)) # Вычисляем
        combinations = [[first_element] + list(x) for x in combinations]
    
        return combinations
    
    • 2
  3. Pak Uula
    2025-01-21T15:45:36Z2025-01-21T15:45:36Z

    由于 itertools 不允许您进入生成器并添加检查,因此您需要自己制作。

    以下是如何使用部分排列测试来制作排列生成器。

    生成器递归地构建排列:

    • 首先前缀为空
    • 如果前缀的长度等于元素集合的长度,则返回前缀——排列构造完成
    • 如果前缀小于元素集合,则返回以给定前缀开始的所有排列,将剩余元素一一添加到前缀中,并使用扩展前缀调用生成器。

    谓词接收排列前缀并返回三个值之一:

    • OH_SHIT:前缀违反了约束,无需构造以此前缀开头的其他排列。
    • SHOOT_IT_BABY:已知以此前缀开头的所有排列都是正确的。无需检查子前缀。
    • SO_FAR_SO_GOOD:前缀正确,但子前缀也需要检查。

    我将从示例开始:

    • 四个元素的所有排列:

      p_gen = permute(list("ABCD"))
      print(list(p_gen))
      
      [('A', 'B', 'C', 'D'), ('A', 'B', 'D', 'C'), ('A', 'C', 'B', 'D'), ('A', 'C', 'D', 'B'), ('A', 'D', 'B', 'C'), ('A', 'D', 'C', 'B'), ('B', 'A', 'C', 'D'), ('B', 'A', 'D', 'C'), ('B', 'C', 'A', 'D'), ('B', 'C', 'D', 'A'), ('B', 'D', 'A', 'C'), ('B', 'D', 'C', 'A'), ('C', 'A', 'B', 'D'), ('C', 'A', 'D', 'B'), ('C', 'B', 'A', 'D'), ('C', 'B', 'D', 'A'), ('C', 'D', 'A', 'B'), ('C', 'D', 'B', 'A'), ('D', 'A', 'B', 'C'), ('D', 'A', 'C', 'B'), ('D', 'B', 'A', 'C'), ('D', 'B', 'C', 'A'), ('D', 'C', 'A', 'B'), ('D', 'C', 'B', 'A')]
      
    • 所有不以“A”开头的排列

      p_gen = permute(list("ABCD"), lambda lst: OH_SHIT if lst[0] == "A" else SHOOT_IT_BABY)
      print(list(p_gen))
      
      [('B', 'A', 'C', 'D'), ('B', 'A', 'D', 'C'), ('B', 'C', 'A', 'D'), ('B', 'C', 'D', 'A'), ('B', 'D', 'A', 'C'), ('B', 'D', 'C', 'A'), ('C', 'A', 'B', 'D'), ('C', 'A', 'D', 'B'), ('C', 'B', 'A', 'D'), ('C', 'B', 'D', 'A'), ('C', 'D', 'A', 'B'), ('C', 'D', 'B', 'A'), ('D', 'A', 'B', 'C'), ('D', 'A', 'C', 'B'), ('D', 'B', 'A', 'C'), ('D', 'B', 'C', 'A'), ('D', 'C', 'A', 'B'), ('D', 'C', 'B', 'A')]
      
    • “B”在“C”之前的所有排列。

      p_gen = permute(
          list("ABCD"),
          lambda lst: OH_SHIT if 'C' in lst else SHOOT_IT_BABY if 'B' in lst else SO_FAR_SO_GOOD
      )
      print(list(p_gen))
      
      [('A', 'B', 'C', 'D'), ('A', 'B', 'D', 'C'), ('A', 'D', 'B', 'C'), ('B', 'A', 'C', 'D'), ('B', 'A', 'D', 'C'), ('B', 'C', 'A', 'D'), ('B', 'C', 'D', 'A'), ('B', 'D', 'A', 'C'), ('B', 'D', 'C', 'A'), ('D', 'A', 'B', 'C'), ('D', 'B', 'A', 'C'), ('D', 'B', 'C', 'A')]
      
    • 以“C”结尾的排列除外

      p_gen = permute(
          list("ABCD"),
          lambda lst: OH_SHIT if (len(lst) == 4 and 'C' == lst[-1]) else SO_FAR_SO_GOOD
      )
      print(list(p_gen))
      
      [('A', 'B', 'C', 'D'), ('A', 'C', 'B', 'D'), ('A', 'C', 'D', 'B'), ('A', 'D', 'C', 'B'), ('B', 'A', 'C', 'D'), ('B', 'C', 'A', 'D'), ('B', 'C', 'D', 'A'), ('B', 'D', 'C', 'A'), ('C', 'A', 'B', 'D'), ('C', 'A', 'D', 'B'), ('C', 'B', 'A', 'D'), ('C', 'B', 'D', 'A'), ('C', 'D', 'A', 'B'), ('C', 'D', 'B', 'A'), ('D', 'A', 'C', 'B'), ('D', 'B', 'C', 'A'), ('D', 'C', 'A', 'B'), ('D', 'C', 'B', 'A')]
      

    重新排列者

    OH_SHIT = 0
    SO_FAR_SO_GOOD = 1
    SHOOT_IT_BABY = 2
    
    def _assert_verdict(v: int) -> None:
        if v == OH_SHIT or v == SO_FAR_SO_GOOD or v == SHOOT_IT_BABY:
            return
        raise ValueError(f"Invalid verdict: {v}")
    
    def _all_is_good(_):
        return SO_FAR_SO_GOOD
    
    class _Permutator[T]:
        """Строит перестановки заданных элементов в соответствии с предикатом.
        
        Предикат получает префикс перестановки и возвращает одно из трёх значений:
        - OH_SHIT: префикс нарушает ограничение, не нужно строить остальные перестановки,
          которые начинаются с данного префикса.
        - SHOOT_IT_BABY: все перестановки, начинающиеся с  данного префикса, заведомо корректны.
          Дочерние префиксы проверять не нужно.
        - SO_FAR_SO_GOOD: префикс корректен, но дочерние префиксы тоже нужно проверять.
        
        Объект типа `_Permutator` является итерируемым. Обратный итератор не определён.
        """
        def __init__(self, lst: list[T], predicate: callable = _all_is_good):
            self.lst = lst
            self.predicate = predicate
            # Состояние перестановщика:
            # - текущий префикс перестановки
            self.prefix: List[T] = []
            # - индексы элементов, включенных в префикс
            self.idx_set : set[int] = set()
            # - состояние предиката
            self.check_state = SO_FAR_SO_GOOD
    
        def _mk_permut_recursive(self) -> Iterator[Tuple[T]]:
            """Рекурсивно строит все перестановки, удовлетворяющие предикату."""
            if len(self.prefix) == len(self.lst):
                # все элементы уже в префиксе, перестановка построена. Префикс проверен выше по стеку,
                # можно возвращать кортеж элементов без проверки предиката
                yield tuple(self.prefix)
                return
            
            for i in range(len(self.lst)):
                # Оптимизация проверки, что элемент `lst[i]` уже включен в префикс
                if i in self.idx_set:
                    continue
    
                old_state = self.check_state
                try:
                    # строим дочерний префикс
                    self.prefix.append(self.lst[i])
                    if self.check_state != SHOOT_IT_BABY:
                        # нужно проверить префикс
                        v = self.predicate(self.prefix)
                        _assert_verdict(v)
                        if v == OH_SHIT:
                            # префикс нарушает предикат,
                            # дальше перестановки с этим префиксом не строим.
                            continue
                        self.check_state = v
                    # префикс корректен (либо состояние SHOOT_IT_BABY, либо вердикт != OH_SHIT)
                    # Заносим индекс добавленного элемента в множество индексов перестановки
                    self.idx_set.add(i)
                    # возвращаем все перестановки с дочерним префиксом
                    yield from self._mk_permut_recursive()
                finally:
                    # Восстанавливаем состояние перестановщика
                    self.prefix.pop()
                    self.check_state = old_state
                    self.idx_set.discard(i)
    
        def __iter__(self):
            return self._mk_permut_recursive()
        
        def __call__(self):
            return iter(self)
    

    和一个包装函数

    def permute[T](elems: list[T]|tuple[T], predicate: callable = _all_is_good) -> Iterator[Tuple[T]]:
        """Возвращает генератор перестановок, удовлетворяющих предикату.
        
        Предикат получает префикс перестановки и возвращает одно из трёх значений:
        - OH_SHIT: префикс нарушает ограничение, не нужно строить остальные перестановки,
          которые начинаются с данного префикса.
        - SHOOT_IT_BABY: все перестановки, начинающиеся с  данного префикса, заведомо корректны.
          Дочерние префиксы проверять не нужно.
        - SO_FAR_SO_GOOD: префикс корректен, но дочерние префиксы тоже нужно проверять.
        """
        return _Permutator(elems, predicate=predicate)
    
    • 1
  4. Best Answer
    CrazyElf
    2025-01-21T14:47:26Z2025-01-21T14:47:26Z

    亚历山大·巴拉金(aleksandr barakin)评论中同一主题的变体,纯粹是出于itertools美观的目的,没有检查,我们假设该函数已经通过提前将数字与列表分开来以所需的方式调用,并且生成器获得生成器,以节省内存并在必要时保存提前终止:

    from itertools import permutations, cycle, chain
    
    def combinations(first, lst):
        for prefix, body in zip(cycle([first]), permutations(lst, len(lst))):
            yield chain([prefix], body)
    
    for item in combinations(1, [2, 3]):
        print(list(item))
    

    结论:

    [1, 2, 3]
    [1, 3, 2]
    

    更新:我已经做了额外的事情,它更简单:

    from itertools import permutations, chain
    
    def combinations(first, lst):
        return (chain([first], body) for body in permutations(lst, len(lst)))
    

    这实际上可以归结为斯坦尼斯拉夫·沃洛达尔斯基的回答,只是没有检查。好吧,我将把它作为一个应用示例cycle,chain以防它对某人派上用场。

    • 0

相关问题

  • 是否可以以某种方式自定义 QTabWidget?

  • telebot.anihelper.ApiException 错误

  • Python。检查一个数字是否是 3 的幂。输出 无

  • 解析多个响应

  • 交换两个数组的元素,以便它们的新内容也反转

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5