2

可能重复:
如何纠正此 Damerau-Levenshtein 实现中的错误?

我有以下Cython代码(改编自bpbio项目)进行Damerau-Levenenshtein 编辑距离计算:

#---------------------------------------------------------------------------
cdef extern from "stdlib.h":
  ctypedef unsigned int size_t
  size_t strlen(char *s)
  void *malloc(size_t size)
  void *calloc(size_t n, size_t size)
  void free(void *ptr)
  int strcmp(char *a, char *b)
  char * strcpy(char *a, char *b)

#---------------------------------------------------------------------------
cdef extern from "Python.h":
  object PyTuple_GET_ITEM(object, int)
  void Py_INCREF(object)

#---------------------------------------------------------------------------
cdef inline size_t imin(int a, int b, int c):
  if a < b:
    if c < a:
      return c
    return a
  if c < b:
    return c
  return b

#---------------------------------------------------------------------------
cpdef int editdistance( char *a, char *b ):
  """Given two byte strings ``a`` and ``b``, return their absolute Damerau-
  Levenshtein distance. Each deletion, insertion, substitution, and
  transposition is counted as one difference, so the edit distance between
  ``abc`` and ``ab``, ``abcx``, ``abx``, ``acb``, respectively, is ``1``."""

  #.........................................................................
  if strcmp( a, b ) == 0: return 0
  #.........................................................................
  cdef int    alen    = strlen( a )
  cdef int    blen    = strlen( b )
  cdef int    R
  cdef char   *ctmp
  cdef size_t i
  cdef size_t j
  cdef size_t achr
  cdef size_t bchr
  #.........................................................................
  if alen > blen:
    ctmp = a;
    a = b;
    b = ctmp;
    alen, blen = blen, alen
  #.........................................................................
  cdef char   *m1     = <char *>calloc(   blen + 2,    sizeof( char ) )
  cdef char   *m2     = <char *>calloc(   blen + 2,    sizeof( char ) )
  cdef char   *m3     = <char *>malloc( ( blen + 2 ) * sizeof( char ) )
  #.........................................................................
  for i from 0 <= i <= blen:
    m2[ i ] = i
  #.........................................................................
  for i from 1 <= i <= alen:
    m1[ 0 ] =    i + 1
    achr    = a[ i - 1 ]
    for j from 1 <= j <= blen:
      bchr = b[ j- 1 ]
      if achr == bchr:
        m1[ j ] = m2[ j - 1 ]
      else:
        m1[ j ] = 1 + imin( m1[ j - 1 ], m2[ j - 1 ], m2[ j ] )
      if i != 1 and j != 1 and achr == b[ j - 2 ] and bchr == a[ i - 2 ]:
        m1[ j ] = m3[ j - 1 ]
    #.......................................................................
    m1, m2 = m2, m1
    strcpy( m3, m2 )
  #.........................................................................
  R = <int>m2[ blen ]
  #.........................................................................
  # cleanup:
  free( m3 )
  free( m1 )
  free( m2 )
  #.........................................................................
  return R

该代码运行良好且快速(在我的 PC 上每秒进行 300,000...400,000 次比较)。

挑战在于使此代码也可以与 unicode 字符串一起使用。我正在运行 Python 3.1 并从数据库中检索文本,然后将其与查询文本匹配。

将这些字符串编码为bytes在将它们传递给 Cython 函数进行比较之前不是一个好主意,因为性能会受到很大影响(经过测试),并且对于包含 7 位 US ASCII 以外的字符的任何文本,结果可能是错误的。

(非常简洁的)Cython 手册确实提到了 unicode 字符串,但对手头的问题几乎没有帮助。

正如我所看到的,一个unicode字符串可以被认为是一个整数数组,每个代表一个单独的代码点,上面的代码基本上char已经在s数组上运行,所以我的猜测是我应该(1)扩展它处理 C 整数数组;(2)添加将python unicode字符串转换为C数组的代码;(3)利润!

注意: 这种方法有两个潜在问题:一个是处理 unicode 代理字符,但我想我知道如何处理这些。另一个问题是 unicode 代码点并没有真正将 1:1 映射到“字符”的概念'。我很清楚这一点,但我认为它超出了这个问题的范围。请假设一个 unicode 代码点是一个比较单位。)

所以我正在征求建议如何

  • 编写一个快速的 Cython 函数,该函数接受 python unicode 字符串并返回 Cythonunsigned int的 C 数组(4 个字节);

  • 修改显示的代码以处理这些数组并进行正确的内存分配/释放(这对我来说很陌生)。

编辑John Machin指出奇怪的类型转换char *m1等可能是为了速度和/或内存优化;这些变量仍被视为数字数组。我意识到代码没有做任何事情来防止可能的长字符串溢出;当一个数组元素超过 127 或 255(取决于所使用的 C 编译器)时,可能会出现错误结果。来自生物信息学项目的代码有点令人惊讶。

也就是说,我只对少于一百个字符的大致相同字符串的精确结果感兴趣。出于我的目的,低于 60% 相同性的结果可以安全地报告为“完全不同”(通过返回较长文本的长度),所以我想最好保留char *m1强制转换,但添加一些代码来检查溢出和在猖獗差异的情况下早期堕胎。

4

3 回答 3

3

用于ord()将字符转换为其整数代码点。它适用于任一unicodestr字符串类型的字符:

codepoints = [ord(c) for c in text]
于 2010-07-30T00:47:59.667 回答
0

警告讲师:我从来没有这样做过。以下是我尝试的粗略草图。

您将需要使用PyUnicode_AsUnicode函数和下一个函数 PyUnicode_GetSize。在您当前拥有的声明中,请char改用Py_UNICODE。大概使用窄 (UCS2) 构建,您将复制内部结构,随时转换代理对。使用宽 (UCS4) 构建,您可以直接在内部结构上进行操作。

于 2010-08-01T00:06:26.583 回答
-2

我关闭了这个问题,因为我找到了一个更好的算法......有它自己的问题。那边见。

于 2010-08-07T20:32:37.093 回答