两个列表中的匹配元素数量

时间:2014-05-23 13:36:05

标签: python python-2.7

我有很多组2个字符串。我正在尝试确定这两个字符串中匹配元素的数量。规则是如果字符串共享一个共同的字母,这是一个点,顺序确实很重要,但第一个字符串中的每个字母只能匹配第二个字符串中的一个字母。因此,在字符串'aaaab''acccc'中,仅授予1点,因为在第二个字符串中只有一个'a'匹配。以下是一些例子:

aaabb  bbaaa  5
aabbb  bbbaa  5
aaabb  aabbb  4
aaabb  ccaaa  3
aaaaa  bbbbb  0
ababa  babab  4
aabcc  babaf  3
abcde  abfgh  2
bacde  abdgh  3

希望能够了解它是如何运作的。

这是我能够提出的最有效的代码,但它非常令人费解。我希望有人能想到更好的东西。

def Score(guess, solution):
    guess = list(guess)
    solution = list(solution)
    c = 0
    for g in guess:
        if g in solution and g != "_":
            c += 1
            solution[solution.index(g)] = "_"
    return c

当然,这不是最好的方法,但我无法解决任何问题。我尝试使用Counter并执行guess&solution创建一个算法,但这个算法很有效,但最终变慢了。有人有什么想法吗?

6 个答案:

答案 0 :(得分:2)

您可以使用NumPy以矢量化形式完成!

import numpy as np

counts1 = np.bincount(np.array('aaadez', 'c').view(np.uint8), minlength=128)
counts2 = np.bincount(np.array('eeeedddddaa', 'c').view(np.uint8), minlength=128)
np.min((counts1, counts2), axis=0).sum()

counts1看起来像这样:

array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
       0, 0, 0, 0, 0, 3, 0, 0, 1, 1, 0...])

这是一个由ASCII码索引的数组。非零元素位于位置97,100和101,它们是ASCII' a' d' d'和' e。然后我们做成对的min(),然后求和得到得分(在这个例子中,4)。

关于这个解决方案的一些好处是你可以将它应用到你想要的任意数量的字符串而不会降低效率,甚至非常长的字符串也会非常快,因为Python本身没有循环 - 仅在编译的NumPy中代码。

在编辑之前,我有一个使用Pandas和SciPy的类似但更慢和更复杂的解决方案。这是:

import scipy.stats
import numpy as np
import pandas

x1 = scipy.stats.itemfreq(np.array('aaade', 'c').view(np.uint8))
x2 = scipy.stats.itemfreq(np.array('bbacadde', 'c').view(np.uint8))
merged = pandas.merge(pandas.DataFrame(x1), pandas.DataFrame(x2), on=0)
np.sum(np.min(merged.values[:,1:], axis=1))

这给了4.0。前两行将字符串转换为整数数组,并运行itemfreq()来计算每个字符出现的次数。在此示例中,x1为:

arrray([[  97.,    3.],
        [ 100.,    1.],
        [ 101.,    1.]])

然后我们在第0列加入两个表,删除另一个不存在的任何字符:

     0  1_x  1_y
0   97    3    2
1  100    1    2
2  101    1    1

然后我们只做一个min和sum来得到最终得分(在这种情况下为2 + 1 + 1)。

答案 1 :(得分:1)

只需使用remove()的{​​{1}}方法而不是使用list进行查找,即可获得~10%*的加速速度。

此外,您无需将index()复制到guess

list

*至少这是我在机器上测量的那个

答案 2 :(得分:0)

这是;

list_a = list("aabbb")
list_b = list("bbbaa")

list_c = set(list_b)

counter = 0

for i in list_c:
    if i in list_b:
        counter = list_a.count(i)
        print "counter : %s  element : %s" %(counter,i )

我只想展示如何计算常用元素,您可以将代码更改为对计数器结果进行求和。

答案 3 :(得分:0)

这是使用Counter的一个非常简单的解决方案:

def proc(vals):
    for s1, s2 in vals:
        c1, c2 = Counter(s1), Counter(s2)
        same = set(s1) & set(s2)
        print s1, s2, sum(min(c1[c], c2[c]) for c in same)

vals看起来像

vals = [('aaaaa', 'bbbbb'), ...]

答案 4 :(得分:0)

试试这个:

a, b = 'aaabb', 'ccaaa'

dict_a, dict_b = {}, {}

for key in list(a):
    dict_a[key] = dict_a.setdefault(key, 0) + 1

for key in list(b):
    dict_b[key] = dict_b.setdefault(key, 0) + 1

count = 0
for key, a_val in dict_a.items():
    try:
        b_val = dict_b[key]
        count += min(b_val, a_val)
    except KeyError:
        None

print count

答案 5 :(得分:0)

与@sloth相同的概念,但使用try代替if

def Score(guess, solution):
    solution = list(solution)
    c = 0
    for g in guess:
        try:
            solution.remove(g)
            c += 1
        except ValueError:
            pass

    return c