python - 如何加快 Levenshtein 距离计算

标签 python c performance optimization levenshtein-distance

我正在尝试运行模拟来测试平均值 Levenshtein distance随机之间 二进制字符串。

我的程序是用 python 编写的,但我正在使用这个 C extension .相关且花费大部分时间的函数计算两个字符串之间的 Levenshtein 距离,就是这个。

lev_edit_distance(size_t len1, const lev_byte *string1,
                  size_t len2, const lev_byte *string2,
                  int xcost)
{
  size_t i;
  size_t *row;  /* we only need to keep one row of costs */
  size_t *end;
  size_t half;

  /* strip common prefix */
  while (len1 > 0 && len2 > 0 && *string1 == *string2) {
    len1--;
    len2--;
    string1++;
    string2++;
  }

  /* strip common suffix */
  while (len1 > 0 && len2 > 0 && string1[len1-1] == string2[len2-1]) {
    len1--;
    len2--;
  }

  /* catch trivial cases */
  if (len1 == 0)
    return len2;
  if (len2 == 0)
    return len1;

  /* make the inner cycle (i.e. string2) the longer one */
  if (len1 > len2) {
    size_t nx = len1;
    const lev_byte *sx = string1;
    len1 = len2;
    len2 = nx;
    string1 = string2;
    string2 = sx;
  }
  /* check len1 == 1 separately */
  if (len1 == 1) {
    if (xcost)
      return len2 + 1 - 2*(memchr(string2, *string1, len2) != NULL);
    else
      return len2 - (memchr(string2, *string1, len2) != NULL);
  }
  len1++;
  len2++;
  half = len1 >> 1;
  /* initalize first row */
  row = (size_t*)malloc(len2*sizeof(size_t));
  if (!row)
    return (size_t)(-1);
  end = row + len2 - 1;
  for (i = 0; i < len2 - (xcost ? 0 : half); i++)
    row[i] = i;

  /* go through the matrix and compute the costs.  yes, this is an extremely
   * obfuscated version, but also extremely memory-conservative and relatively
   * fast.  */
  if (xcost) {
    for (i = 1; i < len1; i++) {
      size_t *p = row + 1;
      const lev_byte char1 = string1[i - 1];
      const lev_byte *char2p = string2;
      size_t D = i;
      size_t x = i;
      while (p <= end) {
        if (char1 == *(char2p++))
          x = --D;
        else
          x++;
        D = *p;
        D++;
        if (x > D)
          x = D;
        *(p++) = x;
      }
    }
  }
  else {
    /* in this case we don't have to scan two corner triangles (of size len1/2)
     * in the matrix because no best path can go throught them. note this
     * breaks when len1 == len2 == 2 so the memchr() special case above is
     * necessary */
    row[0] = len1 - half - 1;
    for (i = 1; i < len1; i++) {
      size_t *p;
      const lev_byte char1 = string1[i - 1];
      const lev_byte *char2p;
      size_t D, x;
      /* skip the upper triangle */
      if (i >= len1 - half) {
        size_t offset = i - (len1 - half);
        size_t c3;

        char2p = string2 + offset;
        p = row + offset;
        c3 = *(p++) + (char1 != *(char2p++));
        x = *p;
        x++;
        D = x;
        if (x > c3)
          x = c3;
        *(p++) = x;
      }
      else {
        p = row + 1;
        char2p = string2;
        D = x = i;
      }
      /* skip the lower triangle */
      if (i <= half + 1)
        end = row + len2 + i - half - 2;
      /* main */
      while (p <= end) {
        size_t c3 = --D + (char1 != *(char2p++));
        x++;
        if (x > c3)
          x = c3;
        D = *p;
        D++;
        if (x > D)
          x = D;
        *(p++) = x;
      }
      /* lower triangle sentinel */
      if (i <= half) {
        size_t c3 = --D + (char1 != *char2p);
        x++;
        if (x > c3)
          x = c3;
        *p = x;
      }
    }
  }

  i = *end;
  free(row);
  return i;
}

这可以加速吗?

我将在 AMD FX(tm)-8350 八核处理器上的 32 位 ubuntu 中运行代码。

这是调用它的 python 代码。

from Levenshtein import distance
import random
for i in xrange(16):
    sum = 0
    for j in xrange(1000):
        str1 = bin(random.getrandbits(2**i))[2:].zfill(2**i)
        str2 = bin(random.getrandbits(2**i))[2:].zfill(2**i)
        sum += distance(str1,str2)
    print i,sum/(1000*2**i)

最佳答案

也许您可以并行运行此程序。在开始时生成一个巨大的随机数列表,然后在您的循环中,一次生成线程(8 个线程)以每个处理列表的一个 block 并将其最终结果添加到 sum 变量。或者一次生成 8 个列表并一次执行 8 个。

openmp 建议的问题是“由于大量数据依赖,该算法的并行化很差”- 维基百科

from threading import Thread

sum = 0

def calc_distance(offset) :
    sum += distance(randoms[offset][0], randoms[offset][1]) #use whatever addressing scheme is best

threads = []
for i in xrange(8) :
    t = new Thread(target=calc_distance, args=(i))
    t.start()
    threads.append(t)

后来....

for t in threads :
     t.join()

如果 levenshtein 距离内核可用(或可编码),我认为此方法稍后也可以很好地移植到 opencl。

这只是凭内存快速发帖,因此可能存在一些问题需要解决。

关于python - 如何加快 Levenshtein 距离计算,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16278874/

相关文章:

python - 使用python将具有嵌套循环结构的文件解析为列表结构

c - 如何根据 C 编程语言标准初始化结构体

python - Matplotlib:更改文本中的字体大小 "midway"

python - 如何使用 Python 创建 Windows 服务

使用 POSIX 标准将结构复制到 C 中的共享内存

c - 一个只有 fork() 这行的程序不会自己崩溃吗?

algorithm - 是否有可能使 `foldrRanges` 与 `foldrRange2D` 一样快?

Android——对象创建/内存分配与性能

android - 如何在 android 中使用自定义 View 减少复杂布局的布局加载时间?

python - 是否可以在 Python 中子类化 Lock() 对象?如果没有,还有其他调试死锁的方法吗?