【问题标题】:Find possible bijection between characters and digits查找字符和数字之间可能的双射
【发布时间】:2012-11-21 08:21:42
【问题描述】:

假设您在列表 L 中有一个字符串 S 和一个数字序列,使得 len(S) = len(L)。

如果您可以找到字符串的字符与序列中的数字之间的双射,以使每个字符匹配一个且仅一个数字,那么检查的最简洁方法是什么。

例如,“aabbcc”应与 115522 匹配,但不能与 123456 或 111111 匹配。

我有一个包含两个字典和循环的复杂设置,但我想知道是否有一种干净的方法可以做到这一点,也许可以使用 Python 库中的一些函数。

【问题讨论】:

  • 如果 a="abcabc" 和 b="123127" 预期的输出是什么?对或错
  • False,因为 'c' 映射到 3 和 7(或者相反,3 和 7 都映射到 'c')。在双射中,每个元素在另一个集合中只有一个匹配元素。

标签: python list bijection


【解决方案1】:

我会为此使用一组:

In [9]: set("aabbcc")
Out[9]: set(['a', 'c', 'b'])

In [10]: set(zip("aabbcc", [1, 1, 5, 5, 2, 2]))
Out[10]: set([('a', 1), ('c', 2), ('b', 5)])

当且仅当映射是满射时,第二组的长度才等于第一组。 (如果不是,您将有两个字母映射到第二组中的相同数字,反之亦然)

这是实现这个想法的代码

def is_bijection(seq1, seq2):
    distinct1 = set(seq1)
    distinct2 = set(seq2)
    distinctMappings = set(zip(seq1, seq2))
    return len(distinct1) == len(distinct2) == len(distinctMappings)

如果一个序列比另一个序列短,这也将返回 true,但已经建立了有效的映射。如果序列的长度必须相同,则应添加一个检查。

【讨论】:

  • 嗯,我不相信这行得通?使用 [1,1,1,1,1,1] 你最终得到 (a,1),(b,1),(c,1) ,它有 3 个项目,就像另一组一样。所以这给了你一个满射,而不是一个完整的双射。
  • 是的。我最初只是提供了这个想法。编辑版本中的代码检查了这两个集合。
  • 快速题外话,a == b == c 被认为是不好的做法吗?
  • 了解 Python 社区,它实际上可能被认为比我所做的更 Pythonic。
  • @EhsanKia 几乎没有。简洁明了。
【解决方案2】:

有一种更优雅的方法可以做到这一点(使用排序和itertools.groupby),但我现在很想知道这一点。但这应该仍然有效:

In [172]: S = "aabbcc"

In [173]: L = [1, 1, 5, 5, 2, 2]

In [174]: mapping = collections.defaultdict(list)

In [175]: reverseMapping = collections.defaultdict(list)

In [176]: for digit, char in zip(L, S):
    mapping[digit].append(char)
    reverseMapping[char].append(digit)
   .....:     

In [177]: all(len(set(v))==1 for v in mapping.values()) and all(len(set(v))==1 for v in reverseMapping.values())
Out[177]: True

In [181]: S = "aabbcc"

In [182]: L = [1, 2, 3, 4, 5, 6]

In [183]: mapping = collections.defaultdict(list)

In [184]: reverseMapping = collections.defaultdict(list)

In [185]: for digit, char in zip(L, S):                                                                         
    mapping[digit].append(char)
    reverseMapping[char].append(digit)
   .....:     

In [186]: all(len(set(v))==1 for v in mapping.values()) and all(len(set(v))==1 for v in reverseMapping.values())
Out[186]: False

希望对你有帮助

【讨论】:

    【解决方案3】:

    这尊重顺序:

    >>> s = "aabbcc"
    >>> n = 115522
    >>> l1 = dict(zip(s, str(n))).items()
    >>> l2 = zip(s, str(n))
    >>> l1
    [('a', '1'), ('c', '2'), ('b', '5')]
    >>> l2
    [('a', '1'), ('a', '1'), ('b', '5'), ('b', '5'), ('c', '2'), ('c', '2')]
    >>> not bool([i for i in l2 if i not in l1])
    True
    >>> n = 115225
    >>> l1 = dict(zip(s, str(n))).items()
    >>> l2 = zip(s, str(n))
    >>> not bool([i for i in l2 if i not in l1])
    False
    

    【讨论】:

      【解决方案4】:

      由于您通常只谈论集合之间的双射,因此我假设与其他答案不同,数字的 顺序 不必与字母的顺序匹配。如果是这样,有一个简短而优雅的解决方案,但它需要 collections.Counter 类,该类是在 python 2.7 中引入的。对于那些坚持使用旧版本的人,有一个 backport for 2.5+

      from collections import Counter
      
      def bijection_exists_between(a, b):
          return sorted(Counter(a).values()) == sorted(Counter(b).values())
      

      测试:

      >>> bijection_exists_between("aabbcc", "123123")
      True
      >>> bijection_exists_between("aabbcc", "123124")
      False
      

      您的示例在边缘情况下相当轻松,因为阅读问题的另一种方式允许数字的数量和字母的数量不相等(即,您寻找从唯一字符集到唯一的数字,例如"aabbcc" 会双射到"123333"。)。如果这是您的意思,请改用此版本:

      def bijection_exists_between(a, b):
          return len(set(a)) == len(set(b))
      

      【讨论】:

      • 也许我不清楚,位双射是一种双向映射。在您的最后一个示例中,“a”映射到 1 和 2,而 3 映射到“b”和“c”,因此它不仅不是双射的,甚至不是满射或单射的。
      • @EhsanKia 您以一种奇怪的方式使用术语 bijection。双射是一种双向映射,是的,但它只存在于sets 之间。字符串不是集合,因为它可能包含重复值。因此,要回答您的问题,需要对其进行解释,并且我提出了两种完全有效的此类解释。我的最后一个例子是双射,因为存在从"aabbcc" ({a, b, c}) 中的字符集到123333 ({1, 2, 3}) 中的数字集的双射.
      【解决方案5】:
      import itertools
      
      a = 'aabbcc'
      b = 112233
      
      z = sorted(zip(str(a), str(b)))
      x = all(
          gx == g0
          for k, g in itertools.groupby(z, key=lambda x: x[0])
          for gx in g for g0 in g
      )
      print x
      

      或:

      import itertools
      
      a = 'aabbcc'
      b = 112233
      
      z = zip(str(a), str(b))
      x = all(
          (z1[0] == z2[0]) == (z1[1] == z2[1]) for z1 in z for z2 in z
      )
      print x
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-31
        • 2019-08-13
        • 2015-03-22
        • 2021-10-20
        相关资源
        最近更新 更多