пример текста в ветке есть, но сейчас уже другой алгоритм на подходе.
по скорости - если алгоритм проявит себя как рабочий можно реализовать на c++ / gcc это даст скорость.---------- Добавлено 15.09.2016 в 23:48 ----------Кто просил Ципф без лемы по Morphy ?
<?php class ZipfFreqAnalise { public $str; private $N; public function __construct( ) { $M = new CMorphy( ); } public function __destruct( ) { } public function LoadAnalizedStr( $str ) { $this->str = trim( $str ); $this->str = preg_replace("|[.,-;:]+|i"," ",$this->str); } public function EtalonMatrix( ) { return array( 0 => 0.025185185185185 , 1 => 0.013333333333333 , 2 => 0.008461538461536 , 3 => 0.006666666666667 , 4 => 0.005185185185185 , 5 => 0.004444444444444 , 6 => 0.003703703703704 , 7 => 0.003703703703704 , 8 => 0.002962962962963 , 9 => 0.002962962962963 , 10 => 0.002962962962963 , 11 => 0.002222222222222 , 12 => 0.002222222222222 , 13 => 0.002222222222222 , 14 => 0.002222222222222 , 15 => 0.001481481481482 , 16 => 0.001481481481482 , 17 => 0.001481481481482 , 18 => 0.001481481481482 , 19 => 0.001481481481482 , ); } public function WordsToArray( ) { var_dump( $this->str ); $array_words = array( ); $tok = strtok($this->str, " \t\n"); while($tok) { $array_words[] = $tok; $tok = strtok(" \t\n"); } $F = array( ); foreach ( $array_words as $i => $w ) { if ( strlen( $w ) > 6 ) { $F[] = $w; } } $array_words = $F; return $array_words; } function RMS( $values, $round = 4 ) { $sum = 0; foreach ( $values as $val ) { $sum += pow( $val, 2 ); } $rms = pow( ( $sum / count( $values ) ), 0.5 ); $rms = round( $rms, $round ); return $rms; } public function PointCompare( ) { $F = array_count_values( $this->WordsToArray( ) ); $AF = array( ); $i = 0; foreach ( $F as $j => $n ) { $AF[$i] = $n; $i++; } arsort( $AF ); foreach ( $AF as $j => $n ) { $AF[$j] = $n / count($F); } $AFcompared = array_shift( array_chunk( $AF, count( $this->EtalonMatrix( ) ) ) ); $E = $this->EtalonMatrix( ); $R = array(); foreach ( $AFcompared as $i => $fq ) { $d = (abs($E[$i] - $fq) / $E[$i]); $R[] = $this->RMS( array($E[$i], $d) ); } $res = round( 1 / $this->RMS( $R ), 2 ) * 100; return $res; } }
правда без лемы погрешность ~300%, так что пример чисто ознакомительный.
Если бы ПС имели простые алгоритмы можно было бы пойти по пути упрощения, а сейчас стоит задача сделать сложное простым в использовании.
простой и быстрый - это пачка парсеров с шаблонизатором. это уже реализовано, так что если не востребованы алгоритмы генерации контента могу оставить их для приват версии, или сделать 2 на выбор.
итого задача сводится к 3м критериям:
мрфология
Ципф
уникальность
+ желательно читабельность и хотябы отдаленное наличие смысла.
чем Morphy плох?
Сниппеты по Ципфу 80-90%, возможно в этом причина их индекса.
по Ципфу - верхнюю точку берешь за 0.95 от нее отсчитываешь веса других точек, делаешь такие весовые матрицы для 10-11-12 словных итд предложений, а далее просто ложишь посчитанные веса слов на эту матрицу и смотришь расхождения, в местах склеек дергаешь шинглы.
на практике можно делать проще - берешь норм форму по всем словам, смотришь какие самые увесистые, берешь 3-4 и дрегаешь с ними парную чатсь речи. Совпадает с запросом хоть одно - предложение релевантно, а вообще можно просто нормализовать и предложение и запрос и по нему смотерть, опечатки, слэнг и прочие расхождения только нейронкой поймать можно.---------- Добавлено 15.09.2016 в 11:27 ----------
print_r ($this->Morphy->lemmatize($wprepared, phpMorphy::NORMAL));
берет лему, этой лемой проверяешь предложения в цикле, впринципе этим можно обойтись для первой версии алгоритма.
если очень приближенно то в готовом виде алго для сниппета - берем запрос, переводим в нормальную форму - ед число, им. падеж итд, лему берем и ищем ее в лематезированном тексте, далее по номеру позиций забираем предложения. две строки и пару циклов.
статистика - круто, но на обучение нужна выборка более 10к подобных блоков, это долго и неэкономно, хотя это лучший вариант конечно, но на непроммасштабе приходится изощраться с оптимизациями и всякими кастыльными решениями вроде того что привел выше.
Ципф - это статистическое распределение, можно взвесить запрос в сниппете и вытащить конструкцию с тем же весом из предложений, обычно этим занимается нейронка. У решения есть миннус - долго обучать, сложно настраивать, можно пойти более простым путем, аппроксимировать и по косвенным признакам вытащить.
можно попробовать лему вытащить из текста, что-то вроде смыслообразующего центра.---------- Добавлено 15.09.2016 в 10:12 ----------Нашел интересный пример
function detect_encoding($text, $short = 1) { /* returns: none - encoding not detected w - windows-1251 k - KOI8-R i - ISO a - DOS 866 */ setlocale(LC_CTYPE, 'ru_RU'); $x_win = array('а'=>'0.07890365448505', 'б'=>'0.013981173864895', 'в'=>'0.043050941306755', 'г'=>'0.018687707641196', 'д'=>'0.027685492801772', 'е'=>'0.089285714285714', 'ж'=>'0.0094130675526024', 'з'=>'0.01578073089701', 'и'=>'0.071151716500554', 'й'=>'0.013427464008859', 'к'=>'0.038898117386489', 'л'=>'0.044435215946844', 'м'=>'0.032392026578073', 'н'=>'0.072120708748616', 'о'=>'0.11600221483942', 'п'=>'0.024363233665559', 'р'=>'0.040420819490587', 'с'=>'0.054817275747508', 'т'=>'0.063538205980066', 'у'=>'0.024363233665559', 'ф'=>'0.0016611295681063', 'х'=>'0.0080287929125138', 'ц'=>'0.0038759689922481', 'ч'=>'0.017303433001107', 'ш'=>'0.008859357696567', 'щ'=>'0.0024916943521595', 'ъ'=>'0.00027685492801772', 'ы'=>'0.018410852713178', 'ь'=>'0.017995570321152', 'э'=>'0.002906976744186', 'ю'=>'0.0065060908084164', 'я'=>'0.018964562569214'); $x_koi = array('б'=>'0.07890365448505', 'в'=>'0.013981173864895', 'Ч'=>'0.043050941306755', 'з'=>'0.018687707641196', 'д'=>'0.027685492801772', 'е'=>'0.089285714285714', 'Ц'=>'0.0094130675526024', 'Ъ'=>'0.01578073089701', 'й'=>'0.071151716500554', 'к'=>'0.013427464008859', 'л'=>'0.038898117386489', 'м'=>'0.044435215946844', 'н'=>'0.032392026578073', 'о'=>'0.072120708748616', 'п'=>'0.11600221483942', 'Р'=>'0.024363233665559', 'Т'=>'0.040420819490587', 'У'=>'0.054817275747508', 'Ф'=>'0.063538205980066', 'Х'=>'0.024363233665559', 'ж'=>'0.0016611295681063', 'и'=>'0.0080287929125138', 'г'=>'0.0038759689922481', 'Ю'=>'0.017303433001107', 'Ы'=>'0.008859357696567', 'Э'=>'0.0024916943521595', 'Я'=>'0.00027685492801772', 'Щ'=>'0.018410852713178', 'Ш'=>'0.017995570321152', 'Ь'=>'0.002906976744186', 'а'=>'0.0065060908084164', 'С'=>'0.018964562569214'); $x_iso = array('Р'=>'0.07890365448505', 'С'=>'0.013981173864895', 'Т'=>'0.043050941306755', 'У'=>'0.018687707641196', 'Ф'=>'0.027685492801772', 'Х'=>'0.089285714285714', 'Ц'=>'0.0094130675526024', 'Ч'=>'0.01578073089701', 'Ш'=>'0.071151716500554', 'Щ'=>'0.013427464008859', 'Ъ'=>'0.038898117386489', 'Ы'=>'0.044435215946844', 'Ь'=>'0.032392026578073', 'Э'=>'0.072120708748616', 'Ю'=>'0.11600221483942', 'Я'=>'0.024363233665559', 'а'=>'0.040420819490587', 'б'=>'0.054817275747508', 'в'=>'0.063538205980066', 'г'=>'0.024363233665559', 'д'=>'0.0016611295681063', 'е'=>'0.0080287929125138', 'ж'=>'0.0038759689922481', 'з'=>'0.017303433001107', 'и'=>'0.008859357696567', 'й'=>'0.0024916943521595', 'к'=>'0.00027685492801772', 'л'=>'0.018410852713178', 'м'=>'0.017995570321152', 'н'=>'0.002906976744186', 'о'=>'0.0065060908084164', 'п'=>'0.018964562569214'); $x_dos = array(' '=>'0.07890365448505', 'с'=>'0.013981173864895', 'т'=>'0.043050941306755', 'у'=>'0.018687707641196', 'ф'=>'0.027685492801772', 'х'=>'0.089285714285714', 'ц'=>'0.0094130675526024', 'ч'=>'0.01578073089701', 'ш'=>'0.071151716500554', 'щ'=>'0.013427464008859', 'ъ'=>'0.038898117386489', 'ы'=>'0.044435215946844', 'ь'=>'0.032392026578073', '_'=>'0.072120708748616', 'ю'=>'0.11600221483942', 'я'=>'0.024363233665559', 'а'=>'0.040420819490587', 'б'=>'0.054817275747508', 'в'=>'0.063538205980066', 'г'=>'0.024363233665559', 'д'=>'0.0016611295681063', 'е'=>'0.0080287929125138', 'ж'=>'0.0038759689922481', 'з'=>'0.017303433001107', 'и'=>'0.008859357696567', 'й'=>'0.0024916943521595', 'к'=>'0.00027685492801772', 'л'=>'0.018410852713178', 'м'=>'0.017995570321152', 'н'=>'0.002906976744186', 'о'=>'0.0065060908084164', 'п'=>'0.018964562569214'); if ($short) $text = substr($text, 0, 200); $len = strlen($text); for ($i = 0;$i < $len;$i++) { $let = strtolower($text[$i]); $t[$let]++; } if (is_array($t)) foreach($t as $k => $v) { $t_win += $v * $x_win[$k]; $t_koi += $v * $x_koi[$k]; $t_iso += $v * $x_iso[$k]; $t_dos += $v * $x_dos[$k]; } $r = 'none'; $tmp = max($t_win, $t_koi, $t_iso, $t_dos); if ($t_win == $tmp) $r = 'w'; if ($t_koi == $tmp) $r = 'k'; if ($t_iso == $tmp) $r = 'i'; if ($t_dos == $tmp) $r = 'a'; return $r; } ?>
Это нейронка, в массиве весовые коэффициенты, упрощенная конечно, но принцип тот же.
Проще сгенерировать свой сниппет, перейдя по ссылке из выдачи, но без ненужных элементов, я об этом. А из этих кусочков уже клеить текст, плюс немного можно морфозаменой перемешать для уникализации.
не тот объем, второй момент - кодировку не нейро правят. думаю важен результат а сниппеты или нет - вопрос вторичный. оставлю сниппеты значит, это уже что-то вроде классики в подобного рода софтах))