如何检查排列是否具有相同的奇偶性?

发布于 2024-08-06 23:42:02 字数 295 浏览 7 评论 0原文

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

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

I am looking for a way to check if 2 permutations (represented by lists) are of the same parity. Note that I am not interested if they are even or odd parity, just the equality.

I am new to Python and my naive solution is given below as a reply. I am looking forward to Python gurus showing me some cool tricks to achieve the same in lesser, more elegant Python code.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

别靠近我心 2024-08-13 23:42:03

我的天真的解决方案:

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

My naive solution:

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
凉城凉梦凉人心 2024-08-13 23:42:03

以下是稍微重构的 Weeble 的答案

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

Here's slightly refactored 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
长安忆 2024-08-13 23:42:03

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

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

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

The solution with the dictionary is bugged.
This is the debug version:

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

The only differences is that the swap in the dictionary was not made correctly.

不即不离 2024-08-13 23:42:03

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

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

例如:

ABCD, BDCA.

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

另:

ABCD, CDBA.

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

My intuition tells me that, just counting the differences between the two permutations will give you one more than the number of swaps need to get from one to the other. This in turn will give you the parity.

This means that you don't need to do the swaps in your code at all.

For example:

ABCD, BDCA.

There are three differences, hence two swaps are needed to permute one into the other, hence you have even parity.

Another:

ABCD, CDBA.

There are four differences, hence three swaps, hence odd parity.

指尖凝香 2024-08-13 23:42:03
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
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
末が日狂欢 2024-08-13 23:42:02

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

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

就排列的大小而言,时间复杂度为 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])

另外,只是为了好玩,这里有一个基于维基百科定义的奇偶校验函数的效率低得多但更短的实现(对于偶数返回 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

If we combine both permutations, the result will have even parity when each permutation has the same parity, and odd parity if they have different parity. So if we solve the parity problem it's trivial to compare two different permutations.

Parity can be determined as follows: pick an arbitrary element, find the position that the permutation moves this to, repeat until you get back to the one you started with. You have now found a cycle: the permutation rotates all these elements round by one position. You need one swap less than the number of elements in the cycle to undo it. Now pick another element you haven't dealt with yet and repeat until you've seen every element. Observe that in total you needed one swap per element minus one swap per cycle.

Time complexity is O(N) in the size of the permutation. Note that although we have a loop within a loop, the inner loop can only ever iterate once for any element in the permutation.

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])

Also, just for fun, here's a much less efficient but much shorter implementation of the parity function based on the definition in Wikipedia (returning True for even and False for odd):

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
去了角落 2024-08-13 23:42:02

这是我对代码的调整

  • 使用 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

Here is my tweak of your code

  • Use list() to copy perm1 - this means you can pass tuples, etc into the function, making it more generic
  • Use enumerate() in the for loop instead of len(..) and array lookups for neater code
  • Make a perm1_map and keep it up to date to stop an expensive O(N) search on perm1, and an expensive list slice
  • return the boolean directly rather than the if ... return True else return False

Here is it

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
北方的巷 2024-08-13 23:42:02

上一个答案的一个小变体 - 复制 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

A minor variant of the previous answer - copy perm1, and save array lookups.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文