Python 查找字符和数字之间可能的双射

Python 查找字符和数字之间可能的双射,python,list,bijection,Python,List,Bijection,假设列表L中有一个字符串s和一个数字序列,使得len(s)=len(L) 如果可以在字符串的字符与序列中的数字之间找到一个双射,使每个字符与一个且仅一个数字匹配,那么最干净的检查方法是什么 例如,“aabbcc”应与115522匹配,而不是123456或111111 我有一个包含两个dict和loop的复杂设置,但我想知道是否有一种干净的方法可以做到这一点,也许可以使用Python库中的一些函数。我会为此使用一个集合: In [9]: set("aabbcc") Out[9]: set(['a'

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

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

例如,“aabbcc”应与115522匹配,而不是123456或111111


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

我会为此使用一个集合:

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。如果序列的长度必须相同,则应添加一个检查

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
或:


有一种更优雅的方法可以做到这一点(通过排序和
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

希望这有助于

尊重订单:

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

因为通常只讨论集合之间的双射,所以我假设,与其他答案不同,数字的顺序不需要与字母的顺序匹配。如果是这样的话,有一个简短而优雅的解决方案,但它需要类,该类是在Python2.7中引入的。对于那些坚持使用旧版本的人,有一个

测试:

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

嗯,我不相信这样行吗?与[1,1,1,1,1,1]一样,你最终得到(a,1),(b,1),(c,1),它有3个项目,就像另一组一样。这是一个满射,不是一个完整的双射。我最初只是提供了这个想法。编辑版本中的代码检查了这两个集合。快速离题问题,
a==b==c
被认为是不好的做法吗?了解Python社区,它实际上可能被认为比我所做的更像Python。@EhsanKia几乎不是这样。它是明确和简洁的。如果a=“abcabc”和b=“123127”预期输出是什么?True或FALSE,因为“c”同时映射到3和7(或者相反,3和7都映射到“c”)。在双射中,每个元素在另一个集合中都有一个且只有一个匹配元素。也许我不清楚,双射是一个双向映射。在上一个示例中,“a”同时映射到1和2,其中3同时映射到“b”和“c”,因此它不仅不是双射的,甚至不是满射或内射的。@EhsanKia您使用双射这个术语的方式很奇怪。是的,双射是一种双向映射,但它只存在于两者之间。字符串不是集合,因为它可能包含重复的值。所以为了回答你的问题,需要对它进行解释,我已经给出了两种完全有效的解释。我的最后一个例子是双射,即存在从
“aabbcc”
({a,b,c})中的字符集到
123333
({1,2,3})中的数字集的双射。
>>> bijection_exists_between("aabbcc", "123123")
True
>>> bijection_exists_between("aabbcc", "123124")
False
def bijection_exists_between(a, b):
    return len(set(a)) == len(set(b))