2010-10-06 73 views
2

我正在写一个非常流程密集的PHP函数,它需要尽可能优化,因为它可以获得速度,因为在极端情况下可能需要60秒才能完成。这是我的情况:比较/匹配两个大阵列的最有效方法?

我想匹配一个人的数组XML列表的工作。这组人有我已经分析过的关键字,用空格分隔。作业来自大型XML文件。

这是目前的设置是这样的:

$matches = new array(); 
foreach($people as $person){ 
    foreach($jobs as $job){ 
     foreach($person['keywords'] as $keyword){ 
      $count = substr_count($job->title, $keyword); 
      if($count > 0) $matches[$job->title] = $count; 
     } 
    } 
} 

我做的关键字循环几次以不同的类别。它做我需要它做的事情,但它感觉非常草率,这个过程可能需要很长时间,这取决于人员/工作的数量。

有没有更高效或更快速的方法来做到这一点?

+4

您是否考虑过使用关系数据库(例如:MySQL)而不是“大型XML文件”?因为他们是这样做的,你知道的。 – NullUserException 2010-10-06 16:42:55

+0

你为什么一次只匹配一个人关键字?如果你打算使用substr_count函数,你为什么不传递一个更大的字符串作为第二个参数,即所有关键字的集合? – 2010-10-06 16:50:13

+0

使用xpath来搜索XML文件不是更容易吗? – 2010-10-06 17:17:59

回答

1
$matches = new array(); 
foreach($people as $person){ 
    foreach($jobs as $job){ 
     foreach($person['keywords'] as $keyword){ 
      $count = substr_count($job->title, $keyword); 
      if($count > 0) $matches[$job->title] = $count; 
     } 
    } 
} 

事实上,你的方法有点草率,但我认为这是因为你有一些特殊格式的数据,你必须解决?尽管除了只是马虎,我还是会看到一些丢失的数据,就像你处理的事情我不认为是有意的。

我看到你不是只是检查“是职位名称中的关键字”,而是“职位名称中的关键字有多少次”,然后你就是在存储这个职位。这意味着对于职位名称friendly friend of the friend company,“关键字”的朋友出现3次,因此​​。由于您在成为$people foreach循环之前声明了$matches,但这意味着您在任何时候新用户拥有该关键字时都会覆盖该值。换句话说,如果第一个人有关键字“朋友”,那么$matches["friendly friend of the friend company"]设置为3.然后,如果第二个人有关键字“友好”,则该值将被覆盖,并且$matches["friendly friend of the friend company"]现在等于1.

I认为你想要做的是计数有多少人有一个关键字是包含在职位名称。在这种情况下,您应该看到,如果它出现,而不是计算$keyword$job->title中出现的次数,并相应地做出响应。

$matches = new array(); 
foreach($people as $person){ 
    foreach($jobs as $job){ 
     foreach($person['keywords'] as $keyword){ 
      if(strpos($job->title, $keyword) !== FALSE) /* "If $keyword exists in $job->title" */ 
       $matches[$job->title]++; /* Increment "number of people who match" */ 
     } 
    } 
} 

另一种可能性是,你想知道多少关键字一个特定的人曾其匹配给定的职务。在这种情况下,你会想每个人都有一个单独的数组。这是稍做修改而完成的。

$matches = new array(); 
foreach($people as $person){ 
    $matches[$person] = new array(); 
    foreach($jobs as $job){ 
     foreach($person['keywords'] as $keyword){ 
      if(strpos($job->title, $keyword) !== FALSE) /* "If $keyword exists in $job->title" */ 
       $matches[$person][$job->title]++; /* Increment "number of keywords which match" */ 
     } 
    } 
} 

,或者,你可以返回到计数关键字现在多少次比赛,因为每个人其实这是一个有意义的值(“以及如何做这项工作匹配”)

$matches = new array(); 
foreach($people as $person){ 
    $matches[$person] = new array(); 
    foreach($jobs as $job){ 
     foreach($person['keywords'] as $keyword){ 
      if($count = substr_count($job->title, $keyword)) /* if(0) = false */ 
       $matches[$person][$job->title] += $count; /* Increase "number of keywords which match" by $count */ 
     } 
    } 
} 

本质上,在解决让循环高效的问题之前,你需要弄清楚你的循环真正要完成的是什么。把这一点说出来,然后提高效率的最好方法就是将循环的迭代次数降到最低,并尽可能多地使用内置函数,因为这些函数都是用C语言实现的(一个非解释的,因此更快 - 运行语言)。

+0

是的,为了保持简单,我忽略了一些细节,我试图完成它,并试图专注于整体结构。尽管你已经非常注意头部。 目前,关键字是数组中的索引,值是它们在该人物上出现的次数。然后,当匹配作业时,它会计算关键字在作业中出现的次数,并在不同的“匹配”数组(关键字值*匹配数)中给它一个“分数”,这最终让我排列相关性。 – Nick 2010-10-06 17:48:13

+0

在这种情况下,计算出现次数(并因此单独解析每个字符串)的需求会给您一个算法复杂性的独特情况。我肯定会建议初学者将人员和工作清单尽可能小(如果你不输出结果,不要为人们计算匹配) – stevendesu 2010-10-06 23:50:35

+0

@Nick另外,考虑给每个工作一个包含所有内容的数组词汇。计算机比较两个阵列要快得多/容易,因为如果确实存在,您就可以确切地知道单词的起始位置。基本上,你只能检查尽可能多的职位,因为有文字,而不是字母。因此,'$ job-> wordArray = array('friendly','friend','of','','friend','company');'。之后,查看'array_count_values()'和'array_key_exists()'以查找匹配的数量。 – stevendesu 2010-10-07 00:02:19

1

你可以使用的话指数在职称,使查找更高效:

$jobsByWords = array(); 
foreach ($jobs as &$job) { 
    preg_match_all('/\w+/', strtolower($jobs->title), $words); 
    foreach ($words[0] as $word) { 
     if (!isset($jobsByWords[$word])) $jobsByWords[$word] = array(); 
     $jobsByWords[$word][] = &$job; 
    } 
} 

然后你只迭代人,并检查关键字索引:

foreach ($people as $person) { 
    foreach ($person['keywords'] as $keyword) { 
     $keyword = strtolower($keyword); 
     if (isset($jobsByWords[$keyword])) { 
      foreach ($jobsByWords[$keyword] as &$job) { 
       $matches[$job->title] = true; 
      } 
     } 
    } 
}