Анализ алгоритмов поиска плагиата в исходных кодах программ

АНАЛИЗ АЛГОРИТМОВ ПОИСКА ПЛАГИАТА В ИСХОДНЫХ

КОДАХ ПРОГРАММ О.А. Евтифеева*, А.Л. Красс, М.А. Лакунин*, Е.А. Лысенко*, Р.Р. Счастливцев* * (Санкт-Петербургский государственный университет) Научный руководитель - д.т.н., профессор А.А. Шалыто

Статья содержит краткий обзор существующих алгоритмов поиска плагиата в исходных кодах программ/ Рассмотрены их особенности, преимущества и недостатки, приведены результаты тестирования наиболее популярных некоммерческих детекторов плагиата, использующих эти алгоритмы.

1. Введение

Плагиат в исходных кодах программ встречается как в коммерческой разработке программного обеспечения, так и в образовании. Сейчас, когда к соблюдению авторских прав в нашей стране стали относиться более внимательно, задача выявления плагиата стала актуальной: необходимы методы и средства, позволяющие автоматизировать этот процесс.

Формально определить понятие плагиата крайне сложно. Условимся под этим термином понимать случай, когда между исходными кодами двух программ есть существенная (на уровне языка программирования) общая часть. При этом производная программа получается из оригинальной несложными преобразованиями, цель которых -скрыть факт заимствования вставкой лишних операторов, изменением порядка следования в программе независимых операторов, разбиением одной функции на две, изменением имен переменных и так далее. Такое определение в большинстве случаев пригодно, ибо серьезные изменения исходного кода, сделанные для сокрытия плагиата вручную, крайне трудоемки, если же для этого используются автоматические средства (например, обфускаторы), то по виду исходного кода это легко определяется человеком.

В начале основной части этой статьи мы рассмотрим, в какое представление переводится исходный код программ в большинстве современных алгоритмов, далее будут кратко описаны наиболее интересные современные алгоритмы поиска плагиата в исходных текстах программ. В конце этой части мы приведем результаты тестирования соответствующих детекторов плагиата, а в заключении обсудим дальнейшие перспективы исследований в данной области.

2. Алгоритмы

2.1. Классификация

Существующие в данной области алгоритмы можно классифицировать следующим образом:

• текстовые алгоритмы,

• структурные алгоритмы,

• семантические алгоритмы.

Особенностью текстовых алгоритмов является то, что они представляют исходные коды программ в виде текста, а точнее, строки над алфавитом, символ которого соответствует определенному оператору или группе операторов языка программирования. Причем аргументы операторов (которые сами могут быть операторами) игнорируются, что делает многие элементарные действия по сокрытию плагиата (например, изменения имен переменным) бесполезными. Символ такого алфавита традиционно называют то-кеном.

Текстовые алгоритмы включают в себя как наиболее эффективные современные алгоритмы поиска плагиата в исходных кодах программ, так и самые старые алгоритмы. Примером последних может служить [1], где для проверки на плагиат производится подсчет некоторых характеристик программ (например, количество операторов ветвления в программе), а затем полученные векторы характеристик сравниваются между собой. Конечно, этот метод очень легко обойти, к тому же он находит плагиат во всей программе и не может быть эффективно адаптирован для нахождения относительно небольшого фрагмента плагиата в большом исходном коде.

Практически во всех алгоритмах текстовой группы считается, что если исходные коды программ считаются похожими (т.е. один с большой вероятностью содержит плагиат другого), то и на уровне вышеописанных строковых представлений у них присутствуют существенные общие части. Причем эти общие части не обязательно должны быть непрерывны (в противном случае алгоритм было бы легко обмануть). Одним из наиболее ранних алгоритмов, использующих этот подход, описывается в работе [2], но из-за эвристики, которой пользуется данный алгоритм, его легко обойти плагиатору. В следующей части мы кратко опишем современные алгоритмы данной области.

Структурные алгоритмы, что следует из названия, используют саму структуру программы, обычно это или граф потока управления, или абстрактное синтаксическое дерево. Вследствие такого представления алгоритм сводит на нет многие возможные действия плагиатора. Но все алгоритмы этого класса являются крайне трудоемкими, поэтому не используются на практике. Единственным исключением может служить алгоритм из статьи [3]. Он использует представление программы в виде абстрактного синтаксического дерева и имеет, по утверждению авторов, квадратичную сложность, хотя проверить это не представилось возможным, так как авторы скрывают некоторые детали алгоритма. Но даже этого недостаточно на практике для наиболее распространенного случая поиска плагиата, когда у нас есть исходный код программы (а возможно - несколько) и большая база оригиналов, с которыми мы хотим его сравнить.

Семантические алгоритмы во многом похожи на структурные и текстовые. Например, в статье [4] используется представление исходного кода программы в виде графа с вершинами двух типов. Одни строятся из последовательности операторов, которым назначается определенная семантика (например, математическое выражение, цикл), другие задают отношение, в котором состоят соседние с ней вершины (например, вхождение). Между двумя вершинами первого типа обычно стоит вершина второго.

2.2. Алгоритм, основанный на выравнивании строк

Пусть у нас есть два исходных текста программ, представим их в виде строк токе-нов ^ и I соответственно (возможно, различной длины). Теперь мы можем воспользоваться методом локального выравнивания строк, разработанным для определения схожести ДНК [5]. Выравнивание двух строк получается с помощью вставки в них пробелов таким образом, чтобы их длины стали одинаковыми. Заметим, что существует большое количество различных выравниваний двух строк. Будем называть строки, полученные после выравнивания 8 и I, соответственно, 8' и I'. Рассмотрим и f^, стоимость их совпадения т, если только одни из символов пропуск, то - g, стоимость несовпадения ё, где т - положительно, ё и g - неположительны. Цена выравнивания - это сумма индивидуальных стоимостей всех пар 8 \ и t\, наибольшее значение этой целевой функции для всех г и] (г <= ] <= |У|=^'|) на строках и t'[i..j] - величина выравнивания.

Оптимальное выравнивание между двумя строками - максимальное значение целевой функции среди всех выравниваний. Это значение может быть вычислено с помощью динамического программирования.

Применение этого алгоритма в нашем случае выглядит так: получаем токенизиро-ванное представление s и t двух программ, делим вторую строку t на подстроки (секции), каждая из которых представляет модуль (функцию или процедуру) исходной программы. Для каждой секции и s получаем значение оптимального локального выравнивания, комбинируем результаты. Это позволяет алгоритму корректно обрабатывать перестановки модулей исходной программы. С деталями алгоритма можно ознакомиться в статье [5].

2.3. Жадное строковое замощение

Рассмотрим эвристический алгоритм получения жадного строкового замощения (The Greedy String Tiling, см. [6]). Он получает на вход две строки символов над определенным алфавитом (у нас это множество допустимых токенов), а на выходе дает набор их общих непересекающихся подстрок, близкий к оптимальному.

Алгоритм использует две эвристики:

считается, что более длинные последовательные совпадения лучше, чем набор меньших и непоследовательных, даже если сумма длин последних больше.

алгоритм игнорирует совпадения, длины которых меньше определенного порога.

Обе эвристики способствуют потере оптимального замощения, но результаты работы алгоритма обычно достаточно близки к нему для обнаружения плагиата. К тому же вторая эвристика способствует фильтрации шумов, т.е. небольших участков кода, которые случайно совпали в проверяемых исходных кодах и не являются плагиатом.

Асимптотика худшего случая для этого алгоритма O(n3), но на практике она значительно ниже O(n2), где n - длина строки, в которую переводят программу, используя токенизированное представление. Этот алгоритм в настоящее время успешно используется в детекторе плагиата JPlag [7].

2.4. Колмогоровская сложность в задаче нахождения плагиата

В работе [8] используется расстояние между последовательностями, основанное на теории информации (an information based sequence distance):

d (x,,) = i - ,

К

где K(x) - колмогоровская сложность последовательности x. Она показывает, сколько информации содержит последовательность x. По определению, К (x) - длина самой короткой программы, которая на пустой ввод печатает строку x. Аналогично, К(x | у) -это длина самой короткой программы, которая на ввод строки y печатает строку x. Можно отметить, что если у не содержит никакой полезной информации для понимания строения x, то К(x | у) = К(x). Тогда выражение К(x) - К(x | у) можно трактовать как, сколько у «знает» о x. Более подробно про колмогоровскую сложность можно посмотреть в [9].

Основное отличие приведенной метрики от других, используемых в задачах определения плагиата, состоит в ее универсальности: две программы, близкие относительно любой другой метрики, будут близкими и относительно данной [10]. Теоретически детектор, основанный на такой метрике, невозможно обмануть.

Чем ближе функция расстояния d

d(x, у) « 1 --

Comp

где Comp(.) (Comp(.\.)) - длина сжатой (условно) строки, полученной из исходной с помощью какого-либо алгоритма сжатия. В работе [8] специально был разработан алгоритм сжатия, который удовлетворяет следующим специфическим требованиям.

• От традиционных алгоритмов сжатия очень часто требуют возможность выполнения в реальном времени или с линейной асимптотикой. Для нас же главное - качество сжатия, а скорость менее важна. Наш алгоритм может работать за сверхлинейное время, но должен достигать наилучшей степени сжатия для используемого типа данных.

• Для получения d(xy) нужно вычислить Comp(x), условное сжатие x по y (Comp(x\y)) и Comp(xy). Обычно программа, которая является плагиатом другой, содержит длинные, приблизительно совпадающие с фрагментами оригинальной программы блоки. Они возникают, когда злоумышленник копирует часть кода оригинальной программы и делает в нем простые изменения. Требуемый алгоритм сжатия должен эффективно справляться с такими вещами, поэтому применяется алгоритм семейства LZ (Lempel-Ziv, см. [11]), использующий найденные неточные совпадения.

Рассмотрим непосредственно процесс сравнения двух программ. Сначала производится их токенизация, далее запускается алгоритм TokenCompress, основанный на алгоритме сжатия LZ (см. [11]). Первым делом он находит длиннейшую неточно повторяющуюся подстроку (approximately duplicated substring), заканчивающуюся в текущем символе; кодирует ее указателем на предыдущее размещение и сохраняет информацию о внесенных поправках. Реализация отличается от классического LZ-сжатия некоторыми особенностями, связанными со спецификой задачи: классический алгоритм семейства LZ с ограниченным буфером может пропустить некоторые длинные повторяющиеся подстроки из-за ограничения на размер словаря (или скользящего окна) во время кодирования. Это не страшно для кодирования обычных текстов, где нужно, прежде всего, экономить память и уменьшать время исполнения, ведь потери качества сжатия будут крайне малы. Но в силу специфики задачи и выбранного алгоритма ее решения, использующего неточные совпадения, потеря даже небольшого количества длинных повторяющихся подстрок неприемлема.

В оригинальной статье [8] не приводится алгоритм подсчета условного сжатия x по y (Comp(x\y)), но, скорее всего, предполагается, что в начале алгоритма инициализируется словарь (буфер) всеми подстроками строки y, а дальше алгоритм остается без изменений.

Классические алгоритмы семейства LZ не поддерживают неточных совпадений. Данный же алгоритм ищет неточно повторяющиеся подстроки и кодирует их несовпадения. TokenCompress использует пороговую функцию, чтобы определить, выгоднее ли кодировать несовпадения или лучше воспользоваться альтернативами (кодировать части отдельно или вообще не производить кодирования некоторых частей). Соответственно, некоторые неточные повторы могут быть объединены в один с небольшим количеством ошибок (несовпадений). Неточно совпавшие пары подстрок (т.е. неточные повторы) записываются в файл и позже могут быть просмотрены человеком. На основе работы этого алгоритма считается величина d(xy), которая затем используется для определения, насколько вероятно то, что одна из этих программ является плагиатом другой.

2.5. Метод отпечатков

2.5.1. Основная идея

В этом алгоритме мы представляем токенизированную программу в виде набора отпечатков (меток, fingerprints), так чтобы эти наборы для похожих программ пересека-

лись. Этот метод позволяет организовать эффективную проверку по базе данных. Метод отпечатков можно представить в виде четырех шагов.

1. Последовательно хэшируем1 подстроки токенизированной программы P длины k (фиксированный параметр).

2. Выделяем некоторое подмножество их хэш-значений, хорошо характеризующее P. Проделываем те же шаги для токенизированных программ Tj,T2...Tn и помещаем их выбранные хэш-значения в хэш-таблицу.

3. С помощью хэш-таблицы (базы) получаем набор участков строки P, подозрительных на плагиат.

4. Анализируем полученные на предыдущем шаге данные и делаем выводы.

Очевидно, что содержательная часть этого алгоритма - выбор k и шага 2. Число k ограничивает длину наименьшей подстроки, с которой может работать данный алгоритм. Если в двух программах есть общая подстрока длиной, например, два символа, то не обязательно, что мы нашли плагиат. Скорее всего, это совпадение - просто случайность и обусловлено малой мощностью нашего алфавита. Т.е. значение k может способствовать игнорированию «шума», но при больших значениях k мы можем проигнорировать настоящий случай плагиата, поэтому это число нужно выбирать с осторожностью.

Пусть наша хэш-функция - это h, а h1, h2... hP|-k+1 - последовательность значений

хэш-функций, полученных на шаге 1. Рассмотрим некоторые возможные реализации шага 2.

• Наивный подход заключается в выборе каждого /-ого из n хэш-значений, но он неустойчив к модификациям кода.

• Мы можем назначать метками p минимальных хеш-значений (см. [13]), их количество для всех документов будет постоянно. С помощью этого метода нельзя найти частичные копии, но он хорошо работает на файлах примерно одного размера, находит похожие файлы, может применяться для классификации документов.

• Манбер [14] предложил выбирать в качестве меток только те хеш-значения, для которых h = 0 modp, так останется только n/p меток (объем идентификационного набора для разных файлов будет отличаться, сами метки будут зависеть от содержимого файла). Однако в этом случае расстояние между последовательно выбранными хеш-значениями не ограничено и может быть велико. В этом случае совпадения, оказавшиеся между метками, не будут учтены.

• Метод просеивания (winnowing) [15] не имеет этого недостатка. Алгоритм гарантирует, что если в двух файлах есть хотя бы одна достаточно длинная общая подстрока, то как минимум одна метка в их наборах совпадет.

2.5.2. Метод просеивания

Чтобы быть эффективнее других реализаций метода отпечатков, рассматриваемый алгоритм должен гарантировать следующее.

• Если у двух токенизированных программ есть общая подстрока длиной как минимум t, то она будет найдена. Отметим, что если мы находим любой общий фрагмент, длина которого не меньше шумового порога (k), и он полностью лежит в другом общем фрагменте, то мы можем обнаружить этот больший фрагмент, используя данные о позиции, найденных совпадений за линейное время.

• Общие подстроки короче шумового порога игнорируются.

1 Обычно используют хэш-функцию из алгоритма Карпа-Рабина поиска подстроки в строке [12].

Последний пункт гарантируется величиной параметра к. Теперь разберемся с первым пунктом. Очевидно, что он будет соблюдаться, если из каждых ^ - к +1) хэш-значений от идущих непосредственно последовательно подстрок будет выбрана хотя бы одна в качестве метки. Будем продвигать окно размера ч = ^ - к +1) вдоль последовательности к^^к^ на каждом шаге перемещаем окно на одну позицию вправо. Назначаем меткой минимальное к в окне; если таких элементов несколько, то назначается

j

самый правый из них.

Рассмотрим пример. Строке аЪгакаёаЪга соответствует последовательность хеш-значений:

12, 35, 78, 3, 26, 48, 55, 12, 35

Пусть к = 2 t = 4, следовательно ч = t-к +1 = 3. Выпишем последовательно содержание окон, полученных алгоритмом, жирным шрифтом выделены те хэш-значения, которые мы будем назначать метками:

Заметим, что если в двух последовательных окнах хэш-значения от одной и той же подстроки являются минимальными, то меткой назначается только одна из них (нет смысла хранить две последовательные абсолютно одинаковые метки), поэтому получим такой набор меток:

12, 3, 26, 12

Нужно отметить, насколько компактен набор идентификационных меток строки.

Показателем эффективности алгоритма может служить плотность ё - доля хэш-значений, выбранных алгоритмом в качестве меток, среди всех хэш-значений документа. Для метода просеивания

ё=-2-ч +1 .

(см. оригинальную статью [15].) Некоторые другие реализации метода отпечатков тоже могут быть изменены так, чтобы соблюдались описанные выше требования, но для них показатель ё в этом случае будет значительно выше, полученного для метода просеивания.

Как мы видим, выбор метки определяется только содержимым окна, такой алгоритм называется локальным. Любой локальный алгоритм выбора меток корректен. Действительно, если в двух файлах есть достаточно большая общая подстрока, то будут и одинаковые окна, а значит, будут назначены одинаковые метки. По ним определим, что в файлах есть совпадения. Это доказывает корректность приведенного алгоритма.

3. Результаты тестирования

Мы провели тестирование четырех алгоритмов, используя существующие их реализации: алгоритм, основанный на выравнивании строк (SIM [5]), жадное строковое замощение (JPlag [7]), алгоритм, использующий приближение колмогоровской сложности (SID [8]), метод отпечатков (MOSS [15]). Отметим, что у каждого детектора есть своя метрика схожести исходных кодов. Они достаточно близки для выбранных детекторов, поэтому мы можем их использовать, не опасаясь за чистоту результата. Будем считать, что у каждого детектора есть какое-то пороговое значение этой метрики (она у всех считается в процентах: 100 % - полное совпадение, 0 % - с точки зрения алгоритма полностью различны), при превышении которого мы можем считать, что имеем дело с плагиатом. Это позволяет нам игнорировать разницу в метрике схожести, когда уже понятно, что детектор с большой вероятностью локализовал случай плагиата и поэтому увеличивает чистоту эксперимента. Мы использовали четыре коллекции тестов, рассмотрим каждую из них подробно.

Простая коллекция. Содержит настоящий случай плагиата для небольшой программы, плагиат практически не пытались скрыть. Все детекторы обнаружили плагиат.

Коллекция с олимпиады по программированию. Она построена на основе программ, написанных во время различных олимпиад по программированию, и представляет собой подмножество программ, выложенных в открытый доступ на сайте http://neerc.ifmo.ru. Алгоритмически решения задач в большинстве случаев достаточно схожи, реализации же достаточно сильно зависят от стиля программирования конкретного участника. Все детекторы, кроме SIM, справились с задачей, ни один не посчитал плагиатом независимо написанные программы. Но стоит отметить, что для менее специфических программ, содержащих больше шаблонных решений, могли возникнуть проблемы. Детектор SIM использует алгоритм, основанный на выравнивании строк, поэтому на небольших программах он бывает неоправданно подозрителен, в 14 % случаев SIM посчитал плагиатом программы, написанные независимо.

Тестовая коллекция детектора SID. Здесь мы имеем дело с четырьмя видами искусственных изменений кода: перегруппировка переменных, различные виды рефакто-ринга кода, перегруппировка методов, случайные вставки операторов. На первых трех видах изменений кода все детекторы справились идеально. Для четвертого вида изменений мы имеем более сложную картину. Для небольшого количества вставок, 20 на 100 строк кода или 300 на 1100 строк, все детекторы находят плагиат. Если же количество вставок достигает 50 на 100 строк кода или 400 на 1100 строк, то все детекторы, кроме SID, перестают находить плагиат, что неудивительно из-за построения этих алгоритмов. Отметим, что даже SID в одном случае не смог обнаружить плагиат, что закономерно: его алгоритм просто более устойчив к данному способу сокрытия плагиата.

4. Анализ результатов тестирования

Все детекторы показали приемлемый результат на тестах, приближенных к реальным случаям плагиата, где не использовались средства автоматизированного сокрытия плагиата (или ими пытались сгенерировать код, по которому человек не сможет быстро определить, было ли применено автоматизированное средство сокрытия плагиата). Все же в сложных случаях можно утверждать, что будет лидировать SID с минимальным преимуществом. При использовании автоматизированных средств сокрытия плагиата, которые применяют в достаточно большом количестве случайные вставки, SID будет вне конкуренции. А так как случайные вставки прекрасно имитируют любые изменения, которые затрагивают одну и изредка несколько соседних строк, то можно быть уверенным, что алгоритм, на котором основан SID, справится с любыми случаями плагиата, где нет значительного изменения структуры кода.

Но все описанные нами алгоритмы, кроме метода отпечатков, не могут эффективно работать с большой базой, так как требуют проведения сравнений образца с каждым элементом базы. А метод отпечатков проводит сравнение образца со всей базой целиком, причем скорость на практике почти линейно зависит от количества элементов базы, относительно которых с определенной вероятностью образец содержит плагиат.

Существует работа [16], в которой предлагается использовать кластеризацию элементов базы, объединяя в кластер очень похожие исходные коды, а потом выбрать (или построить) у каждого кластера своего представителя и проводить сравнение только с ним. Это приведет к ускорению проведения поиска плагиата относительно базы для любого алгоритма, но, на наш взгляд, такой подход не даст на практике достигнуть скорости работы с базой метода отпечатков, а также может способствовать некоторому ухудшению качества поиска при неправильном выборе алгоритма.

Можно обобщить вышеописанную идею и проводить кластеризацию алгоритмом с большей полнотой (отношение количества найденных случаев плагиата ко всем слу-

чаям плагиата), но при этом, возможно, неприемлемо низкой точностью (доля настоящих случаев плагиата в найденных алгоритмом) для самостоятельного использования, а внутри кластера применить алгоритм с большей точностью, причем, возможно, даже несколько алгоритмов, но неустойчивых к разным способам сокрытия плагиата. Наиболее интересно, что для уточнения результатов внутри кластера мы можем применить алгоритм с достаточно высокой трудоемкостью, т.е. вышеупомянутые структурные или семантические методы.

5. Заключение

В настоящий момент существует достаточно много текстовых алгоритмов поиска плагиата, наиболее практичные и распространенные из них мы описали в этой статье, они используют разные эвристики, но основные черты этих алгоритмов неизменны (см. п. 2.1). На основе полученных результатов тестирования мы можем предположить, что существующие алгоритмы почти достигли предела для этого подхода по полноте поиска, остается только увеличивать точность, но для этого можно использовать сторонние алгоритмы или, например, вышеописанный подход, использующий кластеризацию.

Для метода отпечатков и подобных ему в работе с базой мы можем предложить определенным образом задавать вес каждому фрагменту исходного кода, имеющего отображение в хэш-таблице, например, в зависимости от частоты его встречаемости в базе. Ведь чем чаще фрагмент появляется в базе, тем больше вероятность того, что это шаблонное решение, которое в большинстве случаев считать плагиатом не следует. С помощью машинного обучения мы можем корректировать веса фрагментов в зависимости от того, считает ли пользователь текущий фрагмент плагиатом. Эти модификации должны существенно повысить точность поиска.

Насколько нам известно, подобные системы еще не реализованы на практике, и, тем более, их эффективность не была исследована. Единственный текстовый алгоритм, при улучшении которого будет получен существенный результат - это алгоритм, использующий приближение колмогоровской сложности [8], так как он более устойчив к случайным вставкам, чем другие алгоритмы и не имеет относительно них других слабых сторон.

Большой потенциал мы видим в усовершенствовании алгоритма из статьи [3] и создании подобного ему, но сохраняющего семантику.

Мы планируем реализовать и провести тестирование комбинированных методов (см. выше). Нам особенно интересно использование в качестве кластеризующего алгоритма атрибутно-подсчитывающих [1] методов, обогащенных алгоритмом из статьи [16], а в качестве действующего внутри кластера - одного из структурных или семантических алгоритмов, например, [3] или [4]. Мы ожидаем существенное улучшение результатов при таком подходе.

Сейчас только метод отпечатков [15] и алгоритм, основанный на сравнении абстрактных синтаксических деревьев, из работы [3] позволяют организовать базу исходных кодов программ, относительно которой можно искать плагиат в любом другом исходном коде за приемлемое время, а не проверять на плагиат образец с каждым элементом базы. Это происходит из-за того, что эти алгоритмы используют хэш-таблицы. Но в работе [3] утверждается, что нужна особая нетрадиционная «хэш-функция», примеры которой не сообщаются, поэтому можно выразить сомнения о ее эффективном применении на практике.

6. Благодарности

Авторы благодарят Юрия Лифшица за общее руководство. Исследования были

поддержаны корпорацией Intel и грантом РФФИ № 06-01-00584-a.

Литература

1. Faidhi J.A.W., Robinson S.K. An Empirical Approach for Detecting Program Similarity within a University Programming Environment. // Computers and Education. 1987. 11(1). P. 11-19.

2. Heckel P. A Technique for Isolating Differences Between Files. // Communications of the ACM 21(4). April 1978. P. 264-268.

3. Baxter I., Yahin A., Moura L., Anna M.S., Bier L. Clone Detection Using Abstract Syntax Trees. // Proceedings of ICSM. IEEE. 1998.

4. Mishne G., M. de Rijke. Source Code Retrieval using Conceptual Similarity // Proceedings RIAO. Vaucluse. 2004. P. 539-555.

5. Huang X., Hardison R.C., Miller W. A space-efficient algorithm for local similarities. // Computer Applications in the Biosciences 6. 1990. P. 373-381.

6. Wise M.J. String similarity via greedy string tiling and running Karp-Rabin matching. // Dept. of CS, University of Sydney. December 1993.

7. Prechelt L., Malpohl G., Philippsen M. JPlag: Finding plagiarisms among a set of programs. // Technical Report No. 1/00, University of Karlsruhe, Department of Informatics. March 2000.

8. Chen X., Francia B., Li M., McKinnon B., Seker A. Shared Information and Program Plagiarism Detection. // IEEE Trans. Information Theory. July 2004. P. 1545-1550.

9. Li M., Vitanyi P. An introduction to Kolmogorov complexity and its applications. 2nd Ed., Springer, New York. 1997.

10. Li M., Chen X., Li X., Ma B., Vitanyi P. The similarity metric. // Proceedings of the 14th Annual ACM-SIAM Symposium on Discrete Algorithms. 2003. P. 863-872.

11. Ziv J., Lempel A. A universal algorithm for sequential data compression // IEEE Transactions on Information Theory 23. 1977. P. 337-343.

12. Karp R.M., Rabin M.O. Efficient randomized pattern-matching algorithms // IBM J. of Research and Development. 1987. 31(2). P. 249-260.

13. Heintze N. Scalable document fingerprinting. // In 1996 USENIX Workshop on Electronic Commerce, 1996.

14. Manber U. Finding similar files in a large file system. // Proceedings of the USENIX Winter 1994 Technical Conference. San Francisco. 1994. P. 1-10.

15. Aiken A., Schleimer S., Wikerson D. Winnowing: local algorithms for document fingerprinting. // Proceedings of ACMSIGMOD Int. Conference on Management of Data. San Diego. 2003. P. 76-85. ACM Press. New York, USA. 2003.

16. Moussiades L.M., Vakali A. PDetect: A Clustering Approach for Detecting Plagiarism in Source Code Datasets. // The Computer Journal Advance Access. June 24, 2005.