我有以下PHP函数来计算文本之间的关系:
function check($terms_in_article1, $terms_in_article2) {
$length1 = count($terms_in_article1); // number of words
$length2 = count($terms_in_article2); // number of words
$all_terms = array_merge($terms_in_article1, $terms_in_article2);
$all_terms = array_unique($all_terms);
foreach ($all_terms as $all_termsa) {
$term_vector1[$all_termsa] = 0;
$term_vector2[$all_termsa] = 0;
}
foreach ($terms_in_article1 as $terms_in_article1a) {
$term_vector1[$terms_in_article1a]++;
}
foreach ($terms_in_article2 as $terms_in_article2a) {
$term_vector2[$terms_in_article2a]++;
}
$score = 0;
foreach ($all_terms as $all_termsa) {
$score += $term_vector1[$all_termsa]*$term_vector2[$all_termsa];
}
$score = $score/($length1*$length2);
$score *= 500; // for better readability
return $score;
}
变量$terms_in_articleX
必须是包含文本中出现的所有单个单词的数组。
假设我有一个包含20,000个文本的数据库,这个函数需要很长时间来运行所有连接。
如何加快这一过程?我应该将所有文本添加到一个巨大的矩阵中,而不是总是只比较两个文本吗?如果您有一些代码方法,最好是在PHP中,这将是很好的。
我希望你能帮助我。提前谢谢!
答案 0 :(得分:4)
您可以在添加文本时拆分文本。简单示例:preg_match_all(/\w+/, $text, $matches);
当然真正的分裂不是那么简单......但可能,只需更正模式:)
创建表id(int primary autoincrement),value(varchar unique)和link-table,如下所示:word_id(int),text_id(int),word_count(int)。然后在拆分文本后用新值填充表格。
最后,您可以使用所需的任何数据,快速使用数据库中的索引整数(ID)进行操作。
更新: 以下是表格和查询:
CREATE TABLE terms (
id int(11) NOT NULL auto_increment, value char(255) NOT NULL,
PRIMARY KEY (`id`), UNIQUE KEY `value` (`value`)
);
CREATE TABLE `terms_in_articles` (
term int(11) NOT NULL,
article int(11) NOT NULL,
cnt int(11) NOT NULL default '1',
UNIQUE KEY `term` (`term`,`article`)
);
/* Returns all unique terms in both articles (your $all_terms) */
SELECT t.id, t.value
FROM terms t, terms_in_articles a
WHERE a.term = t.id AND a.article IN (1, 2);
/* Returns your $term_vector1, $term_vector2 */
SELECT article, term, cnt
FROM terms_in_articles
WHERE article IN (1, 2) ORDER BY article;
/* Returns article and total count of term entries in it ($length1, $length2) */
SELECT article, SUM(cnt) AS total
FROM terms_in_articles
WHERE article IN (1, 2) GROUP BY article;
/* Returns your $score wich you may divide by ($length1 / $length2) from previous query */
SELECT SUM(tmp.term_score) * 500 AS total_score FROM
(
SELECT (a1.cnt * a2.cnt) AS term_score
FROM terms_in_articles a1, terms_in_articles a2
WHERE a1.article = 1 AND a2.article = 2 AND a1.term = a2.term
GROUP BY a2.term, a1.term
) AS tmp;
嗯,现在,我希望,这会有所帮助吗?最后两个查询足以执行您的任务。其他查询以防万一。当然,你可以计算更多的统计数据,如“最受欢迎的术语”等......
答案 1 :(得分:1)
编辑:试图更明确:
首先,将每个术语编码为一个 整数。你可以使用字典 关联数组,如下所示:
$count = 0;
foreach ($doc as $term) {
$val = $dict[$term];
if (!defined($val)) {
$dict[$term] = $count++;
}
$doc_as_int[$val] ++;
}
这样,您可以替换字符串 用整数计算 计算。例如,你可以 代表“云”这个词 数字5,然后使用索引5 数组的存储计数 单词“云”。请注意我们只 在这里使用关联数组搜索, 不需要CRC等。
20*200 + 20*10*9
的比较次数 - 大约6000次比较而不是19900次。答案 2 :(得分:1)
这是原始功能的略微优化版本。它产生完全相同的结果。 (我在维基百科的两篇文章中运行它,有10000多个术语,每个都运行20次:
check():
test A score: 4.55712524522
test B score: 5.08138042619
--Time: 1.0707
check2():
test A score: 4.55712524522
test B score: 5.08138042619
--Time: 0.2624
以下是代码:
function check2($terms_in_article1, $terms_in_article2) {
$length1 = count($terms_in_article1); // number of words
$length2 = count($terms_in_article2); // number of words
$score_table = array();
foreach($terms_in_article1 as $term){
if(!isset($score_table[$term])) $score_table[$term] = 0;
$score_table[$term] += 1;
}
$score_table2 = array();
foreach($terms_in_article2 as $term){
if(isset($score_table[$term])){
if(!isset($score_table2[$term])) $score_table2[$term] = 0;
$score_table2[$term] += 1;
}
}
$score =0;
foreach($score_table2 as $key => $entry){
$score += $score_table[$key] * $entry;
}
$score = $score / ($length1*$length2);
$score *= 500;
return $score;
}
(顺便说一句。不包括将所有单词拆分成数组所需的时间。)
答案 3 :(得分:0)
如果您可以使用简单的文本而不是数组进行比较,并且如果我理解您的目标所在,您可以使用levenshtein php函数(通常用于给谷歌类似的'你有没有意思是......?'在php搜索引擎中的功能)。
它以与您使用相反的方式工作:返回两个字符串之间的差异。
示例:
<?php
function check($a, $b) {
return levenshtein($a, $b);
}
$a = 'this is just a test';
$b = 'this is not test';
$c = 'this is just a test';
echo check($a, $b) . '<br />';
//return 5
echo check($a, $c) . '<br />';
//return 0, the strings are identical
?>
但我不确切知道这是否会提高执行速度..但也许是的,你可以取出许多foreach循环和array_merge函数。
修改强>
一个简单的速度测试(是一个30秒的错误脚本,它不是100%准确的呃):
function check($terms_in_article1, $terms_in_article2) {
$length1 = count($terms_in_article1); // number of words
$length2 = count($terms_in_article2); // number of words
$all_terms = array_merge($terms_in_article1, $terms_in_article2);
$all_terms = array_unique($all_terms);
foreach ($all_terms as $all_termsa) {
$term_vector1[$all_termsa] = 0;
$term_vector2[$all_termsa] = 0;
}
foreach ($terms_in_article1 as $terms_in_article1a) {
$term_vector1[$terms_in_article1a]++;
}
foreach ($terms_in_article2 as $terms_in_article2a) {
$term_vector2[$terms_in_article2a]++;
}
$score = 0;
foreach ($all_terms as $all_termsa) {
$score += $term_vector1[$all_termsa]*$term_vector2[$all_termsa];
}
$score = $score/($length1*$length2);
$score *= 500; // for better readability
return $score;
}
$a = array('this', 'is', 'just', 'a', 'test');
$b = array('this', 'is', 'not', 'test');
$timenow = microtime();
list($m_i, $t_i) = explode(' ', $timenow);
for($i = 0; $i != 10000; $i++){
check($a, $b);
}
$last = microtime();
list($m_f, $t_f) = explode(' ', $last);
$fine = $m_f+$t_f;
$inizio = $m_i+$t_i;
$quindi = $fine - $inizio;
$quindi = substr($quindi, 0, 7);
echo 'end in ' . $quindi . ' seconds';
打印:以 0.36765 秒结束
第二次测试:
<?php
function check($a, $b) {
return levenshtein($a, $b);
}
$a = 'this is just a test';
$b = 'this is not test';
$timenow = microtime();
list($m_i, $t_i) = explode(' ', $timenow);
for($i = 0; $i != 10000; $i++){
check($a, $b);
}
$last = microtime();
list($m_f, $t_f) = explode(' ', $last);
$fine = $m_f+$t_f;
$inizio = $m_i+$t_i;
$quindi = $fine - $inizio;
$quindi = substr($quindi, 0, 7);
echo 'end in ' . $quindi . ' seconds';
?>
打印:以 0.05023 秒结束
所以,是的,似乎更快。 尝试使用许多数组项目(以及levenshtein的许多单词)会很高兴
<强> 2°修改:
使用相似的文本,速度似乎等于levenshtein方法:
<?php
function check($a, $b) {
return similar_text($a, $b);
}
$a = 'this is just a test ';
$b = 'this is not test';
$timenow = microtime();
list($m_i, $t_i) = explode(' ', $timenow);
for($i = 0; $i != 10000; $i++){
check($a, $b);
}
$last = microtime();
list($m_f, $t_f) = explode(' ', $last);
$fine = $m_f+$t_f;
$inizio = $m_i+$t_i;
$quindi = $fine - $inizio;
$quindi = substr($quindi, 0, 7);
echo 'end in ' . $quindi . ' seconds';
?>
打印:以 0.05988 秒结束
但它可能需要超过255个char:
还要注意这个的复杂性 算法是O(N ** 3),其中N是 最长字符串的长度。
并且,甚至可以以百分比形式返回相似的值:
function check($a, $b) {
similar_text($a, $b, $p);
return $p;
}
又一个编辑
如何创建数据库函数,直接在sql查询中进行比较,而不是检索所有数据并循环它们?
如果您正在运行Mysql,请查看this one(手工制作的levenshtein函数,仍然是255个char限制) 否则,如果您使用的是Postgresql,this other one(许多应该重新评估的函数)
答案 4 :(得分:0)
另一种方法是潜在语义分析,它利用大量数据来查找文档之间的相似之处。
它的工作方式是通过获取文本的共同矩阵并将其与语料库进行比较,实质上为您提供文档在“语义空间”中的抽象位置。这将加快您的文本比较,因为您可以在LSA语义空间中使用欧几里德距离比较文档。这是非常有趣的语义索引。因此,添加新文章不会花费更长的时间。
我不能给出这种方法的具体用例,只是在学校里学到了它,但知识搜索似乎是算法的开源实现。
(对不起,这是我的第一篇文章,因此无法发布链接,只需查看)