代码之家  ›  专栏  ›  技术社区  ›  Pentium10

优化jaro-winkler算法

  •  13
  • Pentium10  · 技术社区  · 14 年前

    我有这个jaro winkler算法的代码 this 网站。我需要跑15万次来找出差距。这需要很长时间,因为我在Android移动设备上运行。

    它能更优化吗?

    public class Jaro {
        /**
         * gets the similarity of the two strings using Jaro distance.
         *
         * @param string1 the first input string
         * @param string2 the second input string
         * @return a value between 0-1 of the similarity
         */
        public float getSimilarity(final String string1, final String string2) {
    
            //get half the length of the string rounded up - (this is the distance used for acceptable transpositions)
            final int halflen = ((Math.min(string1.length(), string2.length())) / 2) + ((Math.min(string1.length(), string2.length())) % 2);
    
            //get common characters
            final StringBuffer common1 = getCommonCharacters(string1, string2, halflen);
            final StringBuffer common2 = getCommonCharacters(string2, string1, halflen);
    
            //check for zero in common
            if (common1.length() == 0 || common2.length() == 0) {
                return 0.0f;
            }
    
            //check for same length common strings returning 0.0f is not the same
            if (common1.length() != common2.length()) {
                return 0.0f;
            }
    
            //get the number of transpositions
            int transpositions = 0;
            int n=common1.length();
            for (int i = 0; i < n; i++) {
                if (common1.charAt(i) != common2.charAt(i))
                    transpositions++;
            }
            transpositions /= 2.0f;
    
            //calculate jaro metric
            return (common1.length() / ((float) string1.length()) +
                    common2.length() / ((float) string2.length()) +
                    (common1.length() - transpositions) / ((float) common1.length())) / 3.0f;
        }
    
        /**
         * returns a string buffer of characters from string1 within string2 if they are of a given
         * distance seperation from the position in string1.
         *
         * @param string1
         * @param string2
         * @param distanceSep
         * @return a string buffer of characters from string1 within string2 if they are of a given
         *         distance seperation from the position in string1
         */
        private static StringBuffer getCommonCharacters(final String string1, final String string2, final int distanceSep) {
            //create a return buffer of characters
            final StringBuffer returnCommons = new StringBuffer();
            //create a copy of string2 for processing
            final StringBuffer copy = new StringBuffer(string2);
            //iterate over string1
            int n=string1.length();
            int m=string2.length();
            for (int i = 0; i < n; i++) {
                final char ch = string1.charAt(i);
                //set boolean for quick loop exit if found
                boolean foundIt = false;
                //compare char with range of characters to either side
    
                for (int j = Math.max(0, i - distanceSep); !foundIt && j < Math.min(i + distanceSep, m - 1); j++) {
                    //check if found
                    if (copy.charAt(j) == ch) {
                        foundIt = true;
                        //append character found
                        returnCommons.append(ch);
                        //alter copied string2 for processing
                        copy.setCharAt(j, (char)0);
                    }
                }
            }
            return returnCommons;
        }
    }
    

    我提到,在整个过程中,我只是制作脚本的一个实例,所以只有一次

    jaro= new Jaro();
    

    如果您要测试并需要示例,这样就不会破坏脚本,您将找到它 here ,在另一个线程中进行python优化

    6 回复  |  直到 8 年前
        1
  •  7
  •   bmargulies    14 年前

    是的,但你不会喜欢的。全部替换 new 带有char数组的Ed StringBuffers,在构造函数中分配,以后不再分配,使用整数索引跟踪其中的内容。

    This pending Commons-Lang patch 会给你一些味道。

        2
  •  4
  •   mvantol1    14 年前

    我知道这个问题可能已经解决了一段时间,但我想对算法本身进行评论。当比较一个字符串和它本身时,答案是1/string_off。当比较稍微不同的值时,这些值也会变低。

    解决方法是在getcommoncharacters方法的内部for语句中将“m-1”调整为“m”。然后代码就像一个符咒一样工作:)

    http://en.wikipedia.org/wiki/Jaro%E2%80%93Winkler_distance 还有一些例子。

        3
  •  0
  •   Rubys    14 年前
    1. 尝试避免getcommoncharacters循环中的两个嵌套循环。
      关于如何在一张地图中存储所有字符的代码(Java有几个),其中键是字符,值是位置,这样你仍然可以计算距离,它们是共同的。我不太明白算法,但我认为这是可行的。
    2. 除了那个和bmargulies的答案,我真的没有看到比bits等东西更进一步的优化。如果这真的很关键,考虑用c重写这部分吗?
        4
  •  0
  •   Ivan    13 年前

    我不太了解Android以及它如何与数据库一起工作。WP7具有(将具有:)SQL CE。下一步通常是处理数据。添加字符串长度并限制比较。在两列上添加索引,然后按长度和值排序。长度索引也应该排序。我让它在一个旧的服务器上运行,有15万个医疗术语给我建议,在0.5秒内进行拼写检查,用户几乎看不到它,特别是在单独的线程上运行时。

    我想写一篇很长时间的博客(比如2年),因为有必要。但我最后还是写了几句,并提供了一些建议。请在这里查看:

    ISolvable.blogspot.com

    虽然它是针对微软平台的,但总体原则还是一样的。

        5
  •  0
  •   larsga    13 年前

    是的,这可以快很多。首先,您根本不需要StringBuffers。另一方面,您不需要一个单独的循环来计算换位。

    你可以找到 my implementation here 而且应该快得多。它获得了Apache2.0许可。

        6
  •  0
  •   dvidben    8 年前

    相反,使用getcommoncharacters方法返回公共字符,使用两个数组来保持匹配,类似于这里的C版本 https://github.com/miguelvps/c/blob/master/jarowinkler.c

    /*Calculate matching characters*/
    for (i = 0; i < al; i++) {
        for (j = max(i - range, 0), l = min(i + range + 1, sl); j < l; j++) {
            if (a[i] == s[j] && !sflags[j]) {
                sflags[j] = 1;
                aflags[i] = 1;
                m++;
                break;
            }
        }
    }
    

    另一个优化是为每个字符串预先计算位掩码。 使用它,检查第一个字符串上的当前字符在第二个字符串上是否存在。这可以使用有效的位操作来完成。

    这将跳过计算最大/最小值和缺少字符的循环。