在文本中寻找相似之处,更具体地说是文本的答案?-.PHP


Finding similarities in text, and more specifically answers to the text? - PHP

我有一个独特的情况,从某种意义上说,我所要求的是为了我自己的方便,而不是我的应用程序的最终用户。

我正在尝试创建一个测试人们智商分数的应用程序(我知道它们无关紧要,对任何人都没有多大用处),没有什么太严重的,只是我的一个项目,让我在作业之间忙碌。

我正在用 PHP 在 WAMP 本地编写它。我发现互联网上有很多可用的智商问题和答案,我可以将其用于我的项目。我还注意到有很多相同的问题,但它们的措辞略有不同。

是否有任何第三方PHP库可以用来阻止我在应用程序中包含"两个"相同的问题?

一些"相同"但在编程上的问题示例被认为是不同的;

The average of 20 numbers is zero. Of them, at the most, how many may be greater than zero?
The average of 20 numbers is zero. Of them how many may be greater than zero?
The average of 20 numbers is zero. Of them how many may be greater than zero, at the most?

显然,你可以看到PHP本身使用运算符无法做到这一点,而我试图区分问题中的相似之处远远超过我的编程技能。

我调查过抄袭软件,但没有找到任何开源PHP项目。

有更简单的解决方案吗?

谢谢

**编辑**

我的一个想法是在插入问题之前在每个空格中使用explode,然后在生成的数组中将其与也应用了相同功能的其他问题进行匹配。匹配越多,问题就越平等?

我是PHP的新手,这听起来可行吗?

正如acfrancis已经回答的那样:它并不比使用内置的levenshtein函数简单得多。

但是,要回答您的最后一个问题:是的,按照您建议的方式进行操作是可行的,并且不太困难。

法典

function checkQuestions($para1, $para2){
    $arr1 = array_unique(array_filter(explode(' ', preg_replace('/[^a-zA-Z0-9]/', ' ', strtolower($para1)))));
    $arr2 = array_unique(array_filter(explode(' ', preg_replace('/[^a-zA-Z0-9]/', ' ', strtolower($para2)))));
    $intersect = array_intersect($arr1, $arr2);

    $p1     = count($arr1);            //Number of words in para1
    $p2     = count($arr2);            //Number of words in para2
    $in     = count($intersect);       //Number of words in intersect
    $lowest = ($p1 < $p2) ? $p1 : $p2; //Which is smaller p1 or p2?

    return array(
        'Average'  => number_format((100 / (($p1+$p2) / 2)) * $in, 2), //Percentage the same compared to average length of questions
        'Smallest' => number_format((100 / $lowest) * $in, 2)          //Percentage the same compared to shortest question
        );
}

解释

  1. 我们定义了一个接受两个参数的函数(参数是我们正在比较的问题)。
  2. 我们过滤输入并转换为数组
    • 将输入设置为小写strtolower
    • 过滤掉非字母数字字符preg_replace
  3. 我们在空格上分解过滤后的字符串
  4. 我们过滤创建的数组
    • 删除空白array_filter
    • 删除重复项array_unique
  5. 对第二个问题重复2-4
  6. 在两个数组中查找匹配的单词并移动到新的数组$intersect
  7. 计算三个数组中每个数组中的单词数$p1$p2$in
  8. 计算百分比相似性和回报

然后,您需要设置一个阈值,说明问题在被视为相同之前必须有多相似,例如 80% .

:注:

  • 该函数返回一个包含两个值的数组。第一个将长度与两个输入问题的平均值进行比较,第二个仅与最短问题进行比较。您可以修改它返回单个值。
  • 我用number_format作为百分比...但你可能会回来int

例子

例 1

$question1 = 'The average of 20 numbers is zero. Of them, at the most, how many may be greater than zero?';
$question2 = 'The average of 20 numbers is zero. Of them how many may be greater than zero?';
if(checkQuestions($question1, $question2)['Average'] >= 80){
    echo "Questions are the same...";
}
else{
    echo "Questions are not the same...";
}
//Output: Questions are the same...

例 2

$para1 = 'The average of 20 numbers is zero. Of them, at the most, how many may be greater than zero?';
$para2 = 'The average of 20 numbers is zero. Of them how many may be greater than zero?';
$para3 = 'The average of 20 numbers is zero. Of them how many may be greater than zero, at the most?';
var_dump(checkQuestions($para1, $para2));
var_dump(checkQuestions($para1, $para3));
var_dump(checkQuestions($para2, $para3));
/**
Output:
array(2) {
  ["Average"]=>
  string(5) "93.33"
  ["Smallest"]=>
  string(6) "100.00"
}
array(2) {
  ["Average"]=>
  string(6) "100.00"
  ["Smallest"]=>
  string(6) "100.00"
}
array(2) {
  ["Average"]=>
  string(5) "93.33"
  ["Smallest"]=>
  string(6) "100.00"
}
*/

尝试使用莱文斯坦距离算法:

http://php.net/manual/en/function.levenshtein.php

我已经使用它(在 C# 中,而不是 PHP)来解决类似的问题,并且效果很好。我发现的技巧是将列文斯坦距离除以第一句话的长度(以字符为单位)。这将为您提供将问题 1 转换为问题 2(例如)所需的更改的粗略百分比。

根据我的经验,如果你得到的任何东西低于50-60%(即小于0.5或0.6),句子是相同的。它可能看起来很高,但请注意,100% 并不是最大值。例如,要将字符串"z"转换为"abcdefghi"需要大约 10 个字符更改(即列文斯坦距离:删除z然后添加 abcdefghi )或根据上述计算更改 1,000%。通过足够大的更改,您可以将任何随机字符串转换为任何其他随机字符串。