Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/226.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 优化近似重复值搜索_Php_Optimization_String Matching - Fatal编程技术网

Php 优化近似重复值搜索

Php 优化近似重复值搜索,php,optimization,string-matching,Php,Optimization,String Matching,我试图在一组字段中找到几乎重复的值,以便管理员能够清理它们 我有两个标准可以匹配 一个字符串完全包含在另一个字符串中,且至少为其长度的1/4 这些字符串的编辑距离小于两个字符串总长度的5% 伪PHP代码: foreach($values as $value){ $matches = array(); foreach($values as $match){ if( ( $value['length'] < $match['length'] &&am

我试图在一组字段中找到几乎重复的值,以便管理员能够清理它们

我有两个标准可以匹配

  • 一个字符串完全包含在另一个字符串中,且至少为其长度的1/4
  • 这些字符串的编辑距离小于两个字符串总长度的5%
  • 伪PHP代码:

    foreach($values as $value){
    $matches = array();
    foreach($values as $match){
      if(
        (
          $value['length'] < $match['length']
          &&
          $value['length'] * 4 > $match['length']
          &&
          stripos($match['value'], $value['value']) !== false
        )
        ||
        (
          $match['length'] < $value['length']
          &&
          $match['length'] * 4 > $value['length']
          &&
          stripos($value['value'], $match['value']) !== false
        )
        ||
        (
          abs($value['length'] - $match['length']) * 20 < ($value['length'] + $match['length'])
          &&
          0 < ($match['changes'] = levenshtein($value['value'], $match['value']))
          &&
          $match['changes'] * 20 <= ($value['length'] + $match['length'])
          )
        ){
          $matches[] = &$match;
        }
    }
    // output matches for current outer loop value
    }
    
    我是否可以做其他事情来减少检查标准的时间,更重要的是,是否有任何方法可以减少所需标准检查的数量(例如,通过预处理输入值),因为存在如此低的选择性


    编辑:实现的解决方案

    // $values is ordered from shortest to longest string length
    $values_count = count($values); // saves a ton of time, especially on linux
    for($vid = 0; $vid < $values_count; $vid++){
    for($mid = $vid+1; $mid < $values_count; $mid++){ // only check against longer strings
      if(
        (
          $value['length'] * 4 > $match['length']
          &&
          stripos($match['value'], $value['value']) !== false
        )
        ||
        (
          ($match['length'] - $value['length']) * 20 < ($value['length'] + $match['length'])
          &&
          0 < ($changes = levenshtein($value['value'], $match['value']))
          &&
          $changes * 20 <= ($value['length'] + $match['length'])
          )
        ){
          // store match in both directions
          $matches[$vid][$mid] = true;
          $matches[$mid][$vid] = true;
        }
    
    }
    }
    // Sort outer array of matches alphabetically with uksort()
    foreach($matches as $vid => $mids){
      // sort inner array of matches by usage count with uksort()
      // output matches
    }
    
    /$值按从最短到最长的字符串长度排序
    $values_count=count($values);//节省大量时间,特别是在linux上
    对于($vid=0;$vid<$values\u count;$vid++){
    对于($mid=$vid+1;$mid<$values\u count;$mid++){//仅检查较长的字符串
    如果(
    (
    $value['length']*4>$match['length']
    &&
    stripos($match['value'],$value['value'])!==false
    )
    ||
    (
    ($match['length']-$value['length'])*20<($value['length']+$match['length']))
    &&
    0<($changes=levenshtein($value['value'],$match['value']))
    &&
    $changes*20$mids){
    //使用uksort()按使用计数对内部匹配数组进行排序
    //输出匹配
    }
    
    您可以先按长度(O(N))对字符串进行排序,然后只检查较小的字符串是否为子字符串或较大的字符串,并且只检查差异不太大的字符串对中的levenshtein

    您已经执行了这些检查,但现在对所有N x N对都执行了检查,而按长度预选“第一”将帮助您减少要首先检查的对。避免N x N循环,即使它只包含将失败的测试

    对于子字符串匹配,您可以通过为所有较小的项创建索引来进一步改进,并在解析较大的项时相应地更新索引。索引应该可以形成一个在字母上分支的树结构,其中每个单词(字符串)形成从根到叶的路径。通过这种方式,您可以找到索引中的任何单词是否与要匹配的字符串进行比较。对于匹配字符串中的每个字符,请尝试继续树索引中的任何指针,并在索引处创建新指针。如果指针无法继续指向索引中的后续字符,则将其删除。如果有任何指针到达叶子注释时,您已找到子字符串匹配。
    我认为,实现这一点并不困难,但也不是微不足道的。

    通过收紧内部循环,您可以立即获得100%的改进。您的结果中是否存在重复匹配


    对于预处理步骤,我会经过并计算字符频率(假设您的字符集很小,如a-z0-9,鉴于您使用的是stripos,我认为这是可能的)。然后,与比较序列(昂贵)相比,比较频率(便宜)。这会给您带来误报,您可以接受,也可以插入当前必须删除的测试。

    值数组中的所有项都是唯一的。还有一个比较可以跳过将项与自身进行比较。我曾考虑按长度排序,但担心输出排序的额外开销,如它需要使用
    usort()
    。但是,由于结果集很小,与减少循环迭代所节省的时间相比,这是微不足道的。按长度排序在我的windows开发机器上几乎将时间缩短了一半,这是可以预期的。奇怪的是,在生产Linux系统上,时间最初实际上增加了2到3倍。从一个
    foreach
    循环到
    for
    ,我每次都计算值数组的长度;计算一次并将其存储在一个变量中,而在windows上则将时间减少了一半,在linux上则减少到原来
    foreach
    循环时间的5%。嗯,这很奇怪。在windows和linux上运行完全相同的代码?
    // $values is ordered from shortest to longest string length
    $values_count = count($values); // saves a ton of time, especially on linux
    for($vid = 0; $vid < $values_count; $vid++){
    for($mid = $vid+1; $mid < $values_count; $mid++){ // only check against longer strings
      if(
        (
          $value['length'] * 4 > $match['length']
          &&
          stripos($match['value'], $value['value']) !== false
        )
        ||
        (
          ($match['length'] - $value['length']) * 20 < ($value['length'] + $match['length'])
          &&
          0 < ($changes = levenshtein($value['value'], $match['value']))
          &&
          $changes * 20 <= ($value['length'] + $match['length'])
          )
        ){
          // store match in both directions
          $matches[$vid][$mid] = true;
          $matches[$mid][$vid] = true;
        }
    
    }
    }
    // Sort outer array of matches alphabetically with uksort()
    foreach($matches as $vid => $mids){
      // sort inner array of matches by usage count with uksort()
      // output matches
    }