Przetestujmy skuteczność algorytmu – część pierwsza!

blog.lantkowiak.pl 7 lat temu

Dzisiaj zajmiemy się przetestowaniem naszego algorytmu do wykrywania plagiatów w praktyce. Miejmy nadzieję, iż to chociaż trochę zadziała

Zestaw testowy

Musimy najpierw zacząć od przygotowania danych, na których będziemy testować naszą aplikacje.

Dzisiaj przygotujemy specjalnie spreparowane kody źródłowe i zobaczymy jak z nimi poradzi sobie nasza aplikacja.

Zacznijmy od stworzenia naszej wyjściowej klasy, która będzie zawierała kilka pól oraz metod.

public class ManyMethods { private int a; private int b; private double c; private double d; private String e; private String f; public ManyMethods() { a = 4; b = 1; c = -1.43; d = 9.12; e = "test"; f = "asdasd"; } public void firstMethod() { a = a + b; } public void secondMethod() { a = a* a* a* a* a *a / 2*a; b = (int) (b + a -8 + 9 * c); } public void thridMethod() { c *= 123.432; e += "Asddsa"; } public void fourthMethod() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("raz"); stringBuilder.append(f); stringBuilder.append("dwa"); f = stringBuilder.toString(); } public void fifthMethod() { e = e + "asd " + a + b+ c+ d+ e+ f; } public static void main(String[] args) { ManyMethods manyMethods = new ManyMethods(); manyMethods.firstMethod(); manyMethods.secondMethod(); manyMethods.thridMethod(); manyMethods.fourthMethod(); manyMethods.fifthMethod(); } }

Będzie to nasz wyjściowy kod, od którego będziemy robić wszystkie modyfikacje.

Wykonajmy następujące modyfikacje kodu:

  1. Zmieńmy nazwy metod i pól public class ManyMethodsDifferentNames { private int firstIntegerVariable; private int secondIntegerVariable; private double firstDoubleVariable; private double secondDoubleVariable; private String firstStringVariable; private String secondStringVariable; public ManyMethodsDifferentNames() { firstIntegerVariable = 4; secondIntegerVariable = 1; firstDoubleVariable = -1.43; secondDoubleVariable = 9.12; firstStringVariable = "test"; secondStringVariable = "asdasd"; } public void doSomeActionOne() { firstIntegerVariable = firstIntegerVariable + secondIntegerVariable; } public void doSomeActionTwo() { firstIntegerVariable = firstIntegerVariable* firstIntegerVariable* firstIntegerVariable* firstIntegerVariable* firstIntegerVariable *firstIntegerVariable / 2*firstIntegerVariable; secondIntegerVariable = (int) (secondIntegerVariable + firstIntegerVariable -8 + 9 * firstDoubleVariable); } public void doSomeActionThird() { firstDoubleVariable *= 123.432; firstStringVariable += "Asddsa"; } public void doSomeActionFour() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("raz"); stringBuilder.append(secondStringVariable); stringBuilder.append("dwa"); secondStringVariable = stringBuilder.toString(); } public void doSomeActionFive() { firstStringVariable = firstStringVariable + "asd " + firstIntegerVariable + secondIntegerVariable+ firstDoubleVariable+ secondDoubleVariable+ firstStringVariable+ secondStringVariable; } public static void main(String[] args) { ManyMethodsDifferentNames manyMethods = new ManyMethodsDifferentNames(); manyMethods.doSomeActionOne(); manyMethods.doSomeActionTwo(); manyMethods.doSomeActionThird(); manyMethods.doSomeActionFour(); manyMethods.doSomeActionFive(); } }
  2. Przenieśmy operacje z metod do metody main, a same metody usuńmy public class ManyMethodsIncluded { private static int a; private static int b; private static double c; private static double d; private static String e; private static String f; public static void main(String[] args) { a = 4; b = 1; c = -1.43; d = 9.12; e = "test"; f = "asdasd"; a = a + b; a = a* a* a* a* a *a / 2*a; b = (int) (b + a -8 + 9 * c); c *= 123.432; e += "Asddsa"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("raz"); stringBuilder.append(f); stringBuilder.append("dwa"); f = stringBuilder.toString(); e = e + "asd " + a + b+ c+ d+ e+ f; } }
  3. Dodajmy kilka nieużywanych metod public class ManyMethodsUnnecessaryStaff { private int a; private int b; private double c; private double d; private String e; private String f; public void stupidMethodOne() { f = "asd sa dsa" + " dsad sad sa" + f + "dsa dsa "; e += "dsadsa dsa dsa + dsad sa " + " dsad sadsa"; } public ManyMethodsUnnecessaryStaff() { a = 4; b = 1; c = -1.43; d = 9.12; e = "test"; f = "asdasd"; } public void stupidMethodTwo() { a = Math.abs(-123213213*3*5); b += 3* b + a + c; } public void firstMethod() { a = a + b; } public void stupidMethodThree() { c = 123.543534/42.543*423.123+65.2; d = Math.sqrt(c + d); } public void secondMethod() { a = a* a* a* a* a *a / 2*a; b = (int) (b + a -8 + 9 * c); } public void stupidMethodFour() { e = "olelele"; try { Thread.sleep(200); } catch(Exception e) { f = "hahah"; } } public void thridMethod() { c *= 123.432; e += "Asddsa"; } public void stupidMethodFive() { c = 123.543534/42.543*423.123+65.2; d = Math.sqrt(c + d); } public void fourthMethod() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("raz"); stringBuilder.append(f); stringBuilder.append("dwa"); f = stringBuilder.toString(); } public void stupidMethodSix() { e = "olelele"; try { Thread.sleep(200); } catch(Exception e) { f = "hahah"; } } public void fifthMethod() { e = e + "asd " + a + b+ c+ d+ e+ f; } public static void main(String[] args) { ManyMethodsUnnecessaryStaff manyMethods = new ManyMethodsUnnecessaryStaff(); manyMethods.firstMethod(); manyMethods.secondMethod(); manyMethods.thridMethod(); manyMethods.fourthMethod(); manyMethods.fifthMethod(); } }
  4. Czwartym kodem do porównania, będzie kopia oryginalnego kodu

Testujemy!

Stwórzmy prostą metodę, która wczyta nasze kody źródłowe oraz wywoła naszą aplikacje i wyliczy prawdopodobieństwo.

fun main(args: Array<String>) { val root = "C:\\kody_do_testow\\"; val oryg = FileInputStream(root + "oryg\\ManyMethods.java").bufferedReader().use { it -> it.readText() } val code1 = FileInputStream(root + "diff_names\\ManyMethodsDifferentNames.java").bufferedReader().use { it -> it.readText() } val code2 = FileInputStream(root + "included\\ManyMethodsIncluded.java").bufferedReader().use { it -> it.readText() } val code3 = FileInputStream(root + "unn_staff\\ManyMethodsUnnecessaryStaff.java").bufferedReader().use { it -> it.readText() } val code4 = FileInputStream(root + "oryg\\ManyMethods.java").bufferedReader().use { it -> it.readText() } var calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code1) println(String.format("code1 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py)) calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code2) println(String.format("code2 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py)) calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code3) println(String.format("code3 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py)) calculate = PlagDetectorAlgorithm().calculate(LexerType.JAVA_8, oryg, code4) println(String.format("code4 d=%.2f, px=%.2f, py=%.2f", calculate.d, calculate.px, calculate.py)) }

Omówmy wyniki!

Pora na krótką analizę wyników.

  1. Zmienione nazwy metod i pól
    Program zwrócił następujący wynik: d=0.97, px=0.98, py=0.98

    Tutaj możemy zauważyć, iż program poradził sobie całkiem nieźle. Wskaźniki są bardzo bliskie 1, więc możemy z dużą dozą prawdopodobieństwa stwierdzić, iż porównywane programy zawierają ten sam kod.

  2. Kod z metod przeniesiony do głównej metody
    Zwrócony wynik: d=0.57, px=0.69, py=0.76

    Tutaj niestety algorytm poradził sobie dużo gorzej. Wystarczyła drobna zmiana w kodzie i stopień wykrywalności jest o wiele mniejszy. Może to byś spowodowane tym, iż testowany kod jest krótki i choćby takie małe zmiany znacząco wpływają na wyniki. Warto na pewno się temu przyjrzeć w przyszłości.

  3. Kod z nieużywanymi metodami
    Wyliczone podobieństwo: d=0.63, px=0.92, py=0.67

    Na tym przykładzie możemy zobaczyć w praktyce wykorzystanie wskaźników px oraz py. O ile główny wskaźnik wynosi tylko 0.63 i nie musi wydawać się podejrzany, o tyle wartość wskaźnika px (0.92) mocno wskazuje, iż jeden z porównywanych kodów zawiera dużą część drugiego kodu, oraz iż mógł zajść plagiat.

  4. Porównanie dwóch identycznych kodów źródłowych
    Wynik: d=0.97, px=0.98, py=0.98

    Tutaj podobnie jak w pierwszym przypadku algorytm poradził sobie bardzo dobrze i wyliczył wskaźniki bliskie 1.

Podsumowanie

Dzisiaj wykonaliśmy pierwsze testy naszego algorytmu. Do testów użyliśmy kilka spreparowanych kodów źródłowych. Pierwsze wyniki wskazują, iż algorytm jakieś prawdopodobieństwo wylicza

W następnym wpisie odnośnie PlagDetectora przeprowadzimy testy na kodach źródłowych z28za czasów studenckich

Idź do oryginalnego materiału