【问题标题】:How to check if permutations have equal parity?如何检查排列是否具有相等的奇偶性?
【发布时间】:2010-12-02 22:47:55
【问题描述】:

我正在寻找一种方法来检查 2 个排列(由列表表示)是否相同的parity。请注意,如果它们是 evenodd 奇偶校验,我不感兴趣,只是相等。

我是 Python 新手,下面给出了我的幼稚解决方案作为答复。我期待着 Python 大师向我展示一些很酷的技巧,以便在更简洁、更优雅的 Python 代码中实现同样的目标。

【问题讨论】:

  • 排列是不相交的循环列表吗?转置对列表? (s, σ(s)) 对的列表(其中 σ 是要表示的排列)?
  • Outis:我猜两者都可以。

标签: python list permutation parity


【解决方案1】:
def equalparity(p,q):
    return sum([p[q[i]] > p[q[j]] for i in range(len(p)) for j in range(i)]) % 2 == 0

【讨论】:

    【解决方案2】:

    字典的解决方案有问题。 这是调试版:

    def arePermsEqualParity(perm0, perm1):
        """Check if 2 permutations are of equal parity.
    
        Assume that both permutation lists are of equal length
        and have the same elements. No need to check for these
        conditions.
        """
        perm1 = list(perm1) ## copy this into a list so we don't mutate the original
        perm1_map = dict((v, i) for i,v in enumerate(perm1))
        transCount = 0
        for loc, p0 in enumerate(perm0):
            p1 = perm1[loc]
            if p0 != p1:
                sloc = perm1_map[p0]                       # Find position in perm1
                perm1[loc], perm1[sloc] = p0, p1           # Swap in perm1
                perm1_map[p0], perm1_map[p1] = loc, sloc   # Swap the map
                transCount += 1
        # Even number of transpositions means equal parity
        return (transCount % 2) == 0
    

    唯一的区别是字典中的交换没有正确进行。

    【讨论】:

      【解决方案3】:

      如果我们结合两个排列,当每个排列具有相同的奇偶性时,结果将具有偶校验,如果它们具有不同的奇偶性,则结果将具有奇校验。因此,如果我们解决奇偶校验问题,比较两个不同的排列是很简单的。

      奇偶性可以如下确定:选择任意元素,找到排列移动到的位置,重复直到回到开始的位置。您现在找到了一个循环:排列将所有这些元素旋转一个位置。您需要比循环中的元素数量少一个交换来撤消它。现在选择另一个你还没有处理过的元素并重复,直到你看到每个元素。请注意,您总共需要每个元素一次交换减去每个循环一次交换。

      时间复杂度在排列的大小上是 O(N)。请注意,虽然我们在循环中有一个循环,但内部循环只能对排列中的任何元素迭代一次。

      def parity(permutation):
          permutation = list(permutation)
          length = len(permutation)
          elements_seen = [False] * length
          cycles = 0
          for index, already_seen in enumerate(elements_seen):
              if already_seen:
                  continue
              cycles += 1
              current = index
              while not elements_seen[current]:
                  elements_seen[current] = True
                  current = permutation[current]
          return (length-cycles) % 2 == 0
      
      def arePermsEqualParity(perm0, perm1):
          perm0 = list(perm0)
          return parity([perm0[i] for i in perm1])
      

      另外,只是为了好玩,这里有一个效率低得多但时间短得多的奇偶校验函数实现,它基于 Wikipedia 中的定义(对于偶数返回 True,对于奇数返回 False):

      def parity(p):
          return sum(
              1 for (x,px) in enumerate(p)
                for (y,py) in enumerate(p)
                if x<y and px>py
              )%2==0
      

      【讨论】:

      • [p0[p1[i]] for i in xrange(len(p0))] -> [p0[i] for i in p1]?
      • 嘿嘿,你点出来就明白了!我会编辑那些。
      • 非常感谢您指出这种替代方法!对于 perm0 保持不变并与许多 perm1 列表进行比较的情况,它变得更加优化(和有用)。
      【解决方案4】:

      这里稍微重构Weeble's answer

      def arePermsEqualParity(perm0, perm1):
          """Whether permutations are of equal parity."""
          return parity(combine(perm0, perm1))
      
      def combine(perm0, perm1):
          """Combine two permutations into one."""
          return map(perm0.__getitem__, perm1)
      
      def parity(permutation):
          """Return even parity for the `permutation`."""
          return (len(permutation) - ncycles(permutation)) % 2 == 0
      
      def ncycles(permutation):
          """Return number of cycles in the `permutation`."""
          ncycles = 0
          seen = [False] * len(permutation)
          for i, already_seen in enumerate(seen):
              if not already_seen:
                  ncycles += 1
                  # mark indices that belongs to the cycle
                  j = i 
                  while not seen[j]: 
                      seen[j] = True
                      j = permutation[j]
          return ncycles
      

      【讨论】:

        【解决方案5】:

        这是我对您的代码的调整

        • 使用 list() 复制 perm1 - 这意味着您可以将元组等传递到函数中,使其更通用
        • 在 for 循环中使用 enumerate() 代替 len(..) 和数组查找以获得更简洁的代码
        • 制作一个 perm1_map 并使其保持最新,以停止对 perm1 的昂贵 O(N) 搜索和昂贵的列表切片
        • 直接返回布尔值而不是 if ... return True else return False

        这里是

        def arePermsEqualParity(perm0, perm1):
            """Check if 2 permutations are of equal parity.
        
            Assume that both permutation lists are of equal length
            and have the same elements. No need to check for these
            conditions.
            """
            perm1 = list(perm1) ## copy this into a list so we don't mutate the original
            perm1_map = dict((v, i) for i,v in enumerate(perm1))
            transCount = 0
            for loc, p0 in enumerate(perm0):
                p1 = perm1[loc]
                if p0 != p1:
                    sloc = perm1_map[p0]                       # Find position in perm1
                    perm1[loc], perm1[sloc] = p0, p1           # Swap in perm1
                    perm1_map[p0], perm1_map[p1] = sloc, loc   # Swap the map
                    transCount += 1
            # Even number of transpositions means equal parity
            return (transCount % 2) == 0
        

        【讨论】:

          【解决方案6】:

          我的直觉告诉我,仅计算两个排列之间的差异就会比从一个排列到另一个排列所需的交换次数多一个。反过来,这将为您提供平价。

          这意味着您根本不需要在代码中进行交换。

          例如:

          ABCD, BDCA.
          

          存在三个差异,因此需要两次交换才能将一个置换为另一个,因此您有偶数奇偶性。

          另一个:

          ABCD, CDBA.
          

          有四个差异,因此三个交换,因此奇校验。

          【讨论】:

          • BADC - 4 个差异 - 只需要 2 个交换。
          • 反例:ABCD、BADC。有四个不同之处,但只有两个互换。 (交换 AB,交换 CD。)我相信奇偶校验取决于周期数。
          • 糟糕,看来我有点晚了!
          • 这就是直觉很糟糕的原因。让理性占上风!
          【解决方案7】:

          上一个答案的一个小变种 - 复制 perm1,并保存数组查找。

          def arePermsEqualParity(perm0, perm1):
              """Check if 2 permutations are of equal parity.
          
              Assume that both permutation lists are of equal length
              and have the same elements. No need to check for these
              conditions.
              """
              perm1 = perm1[:] ## copy this list so we don't mutate the original
          
              transCount = 0
              for loc in range(len(perm0) - 1):                         # Do (len - 1) transpositions
                  p0 = perm0[loc]
                  p1 = perm1[loc]
                  if p0 != p1:
                      sloc = perm1[loc:].index(p0)+loc          # Find position in perm1
                      perm1[loc], perm1[sloc] = p0, p1          # Swap in perm1
                      transCount += 1
          
              # Even number of transpositions means equal parity
              if (transCount % 2) == 0:
                  return True
              else:
                  return False
          

          【讨论】:

            【解决方案8】:

            我的幼稚解决方案:

            def arePermsEqualParity(perm0, perm1):
                """Check if 2 permutations are of equal parity.
            
                Assume that both permutation lists are of equal length
                and have the same elements. No need to check for these
                conditions.
                """
            
                transCount = 0
                for loc in range(len(perm0) - 1):                         # Do (len - 1) transpositions
                    if perm0[loc] != perm1[loc]:
                        sloc = perm1.index(perm0[loc])                    # Find position in perm1
                        perm1[loc], perm1[sloc] = perm1[sloc], perm1[loc] # Swap in perm1
                        transCount += 1
            
                # Even number of transpositions means equal parity
                if (transCount % 2) == 0:
                    return True
                else:
                    return False
            

            【讨论】:

            • 我能看到的唯一改进是搜索 perm1.index(perm0[loc]) 实际上只需要在 loc 之后检查 perm1 - 不知道如何在 python 中有效地表达。
            • 我想我们可能想要复制 perm1 以便操作不会改变参数 perm1?
            • Douglas:你对制作 perm1 的副本是正确的。我什至不知道列表是通过引用函数传递的(因此可以在其中修改)!
            猜你喜欢
            • 1970-01-01
            • 2014-03-04
            • 1970-01-01
            • 2020-07-01
            • 1970-01-01
            • 1970-01-01
            • 2011-11-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多