【问题标题】:calculating how much two strings are similar?计算两个字符串有多少相似?
【发布时间】:2016-11-22 11:29:57
【问题描述】:

我有一个函数可以计算两个给定字符串的方差。有没有更快的方法(或算法)来做这样的事情?

请记住,我的琴弦的每个字母都装有 DNA,这意味着它们是 A 或 T 或 C 或 G 之一:

unsigned __int8 dis(char* FirstString, char* SecondString)
{
    unsigned __int8 distanceIndex = 0;
    for (unsigned __int8 i = 0; i < l; i++)
    {
        if (FirstString[i] != SecondString[i])
            distanceIndex++;
    }
    return distanceIndex;
}

【问题讨论】:

  • Levenshtein Distance en.wikipedia.org/wiki/Levenshtein_distance 怎么样?
  • 您可能想像拼写检查器一样使用Levenshtein distance
  • Levenshtein 是否提供更好的时间复杂度?任何人都可以像我在上面那样实现这样的方法吗?
  • 如果每个元素只有 4 个可能的值,则可以使用更紧凑的序列表示。您只能使用 2 位而不是 8 位。然后您可以计算汉明距离。 en.wikipedia.org/wiki/Hamming_distance
  • 根据编译器和/或目标平台,我可以设想在循环中使用显式 8 位 int 可能比使用自然大小的 int 慢.

标签: c++ algorithm processing-efficiency cpu-speed


【解决方案1】:

虽然我仍然怀疑字符串比较是否真的是你项目的the bottleneck,但我还是忍不住接受了挑战……

你所有的序列都是13 chars long。 DNA序列只包含字母ATCG,可以在2位内编码。您可以将每个 DNA 序列存储在一个 32 位的值内,让计算机进行并行比较:

  • XOR 组合这些值以获得位差
  • 移位和或组合与归一化的子集(奇数位,偶数位)到 将位差异转化为核碱基差异
  • 计算设置的位以获得DNA序列距离

根据计算机架构,可能有位计数功能 在 CPU 中实现。更多详情有问题的答案:How to count the number of set bits in a 32-bit integer?

这里是核心功能:

int distV(const unsigned va, const unsigned vb)
{
    const unsigned x = va ^ vb;
    const unsigned bn = ((x & 0xaaaaaaaa) >> 1 ) | (x & 0x55555555);
    return __builtin_popcount(bn);
}

请参阅使用长度为 16 的序列的 full GCC-4.3.2 demo。我为比较本身(不包括编码)测量了 4 倍的性能增量。

【讨论】:

  • 压缩核碱基字符串thanx是一个非常好的主意,但是您能解释一下如何并行进行比较吗?
  • 是位操作。你看过演示吗?我将添加核心功能以与我的答案进行比较。
  • 那太好了:)
  • @ShaheenZahedi 已添加,请参阅distV 函数:它并行比较 32 位(即给定编码中的 16 个核碱基)。
  • 哦,抱歉,我明白了,这确实是最快的,但是编码需要花费大量时间,值得我重新实现所有项目以使用编码字符串吗?
【解决方案2】:

这是一个 O(n) 算法。

比较两个字符串之间是否相等(或在这种情况下为距离)的最有效算法是 O(n)。

【讨论】:

  • Levenshtein 和汉明距离怎么样?这个算法比那些更好吗?
  • Levenshtein 复杂度为 O(n + d^2),其中 d 是距离。汉明距离也是 O(n)。
  • 该评论是在答案已经发布后添加的,但是当 N 仅为 13 时谈论 O(N) 并没有多大意义,因此算法复杂性,即使总体上是正确的,恕我直言,不回答这个问题。
  • 我不知道为什么当 n=13 时 OP 甚至担心复杂性。但上面的答案是general
  • @AlessandroTeruzzi 在这种情况下(低 n)确实有一些优化的地方,具体取决于架构的操作数宽度,请参阅 my answer
【解决方案3】:

你可以不用if

unsigned __int8 dis(char* FirstString, char* SecondString)
{
    unsigned __int8 distanceIndex = 0;
    for (unsigned __int8 i = 0; i < l; i++)
       {
            distanceIndex += FirstString[i] != SecondString[i];
       }
    return distanceIndex;
}

但我怀疑这是否重要

【讨论】:

  • 不正确,distanceIndex每次都会递增
  • @ShaheenZahedi 我刚试了一下,对我来说效果很好(如果有问题,我使用普通的unsigned int
  • 它只是给了我错误的答案,只是一些奇怪的数字,我不知道你的方法有什么问题:(
  • @ShaheenZahedi 你可以看到我的实时代码here
【解决方案4】:

您可以通过避免通过索引完成的随机访问来稍微加快速度,您实际上只需要对字符串进行顺序访问。

我不确定编译器是否可以为您优化。

【讨论】:

  • 我的编译器,可以优化一下,这两个没有区别
猜你喜欢
  • 1970-01-01
  • 2019-11-03
  • 2013-11-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-28
相关资源
最近更新 更多