Futási eredmények kiírása

A program futása nem öncélú. Használni szeretnénk a kiszámolt eredményeket. Több módszer is elképzelhető, az a kérdés, hogy mire vagyunk kíváncsiak. Ha egy konkrét problémát szeretnénk megoldani, akkor elsődleges lehet maga a megoldás, azaz esetünkben a partíció. Ha a módszerek egymáshoz viszonyított eredményére vagyunk kíváncsiak, akkor érdekel bennünket, hogy mely hogy teljesített, mi a célfüggvény értéke az egyes esetekben. Mivel mindegyik módszer használja a véletlen szám generátort, több teszt esetén már a leíró statisztika érdekelhet bennünket: a célfüggvényértékek átlaga, szórása. Miután a korreláció klaszterezés feladatához elméleti eredmények vannak a maximális partíció méretére, így ezt is tekinthetjük.

Lehet például az a kívánságunk, hogy minden egyes mátrixra futtassunk le négy tesztet, és az optimálisnak talált megoldáshoz tartozó célfüggvény értékeket írjuk ki:

#m HCAll2 
#f m200_30-000.tab
0  0  0  1
#f m200_30-005.tab
568  568  567  567
#f m200_30-010.tab
1205 1210 1210 1210
#f m200_30-015.tab
1850 1851 1865 1842
...
85790 ms

Hasonlóan az az igény is felmerülhet, hogy a legnagyobb csoport méretét szeretnénk kiírni a tesztek alatt:

#m HCAll2 
# m200_30-000.tab
5  5  4  4
# m200_30-005.tab
5  5  5  5
# m200_30-010.tab
7  7  7  6
...
87540 ms

Természetesen igényelhetünk leíró statisztikákat is. A pontsorozat úgy áll elő, hogy minden teszteset lefutása után kiír egy pontot. A standard output és hiba megfelelő kezelésével elérhető, hogy a megoldás adatait fájlba, a futás visszaigazolását pedig a képernyőre írja a program:

#m HCAll2 
# m200_30-000.tab
0 ....................
0  value:     0.00    0    1 maxGroup:  4.00 4 6 noGroups: 126.00 121 133
## m200_30-005.tab
5 ....................
5  value:   568.00  560  579 maxGroup:  5.00 5 7 noGroups:  76.00  68  83
## m200_30-010.tab
10 ....................
10 value:  1206.00 1197 1219 maxGroup:  6.00 6 8 noGroups:  57.00  54  66
...
86278 ms

5.7. ábra - PrintSolution osztálydiagram

PrintSolution osztálydiagram

A továbbiakban először leírjuk a közös absztrakt osztályt, majd bemutatunk két konkrét megvalósítást is.

Eredmények kiírása

Természetesen nem elég, hogy a program csak számol, kíváncsiak vagyunk a számolás végeredményére is. Sőt az sem baj, ha a programunk már számunkra emészthető formában szolgáltatja az eredményeket. Gyakran más és más igényeink vannak. Ennek megfelelően készült el ez az absztrakt osztály, mely megadja a keretet a kiíráshoz, és a későbbiekben ismertetett megvalósítások, melyek konkrét igényhez töltik fel az itt bemutatott vázat:

package hu.unideb.inf.optimization.methods;
/**
 * A módszerek eredményeit több módszerrel is kiírhatja.
 * @author ASZALÓS László
 */
public abstract class PrintSolution {

Gyakran több adaton is lefuttatjuk a tesztjeinket, és rendszerint nem is egyszer, mert a módszerek sztochasztikus volta miatt más és más eredményeket kaphatunk. Épp ezért több lehetőségünk is van, hogy a felhasználót információval ellássuk. Első ilyen alkalom a kezdés előtt. Itt még eredményekről nem lehet beszámolni, viszont az archívum számára megadhatjuk az alkalmazott módszer elnevezését. A felhasznált paraméterek kiírása a módszer inicializálásánál már megtörténhetett:

/**
 * Mi a teendő a tesztek előtt?
 * @param method az alkalmazott módszer elnevezése
 */
public abstract String printBefore(String method);

Ha újabb adattal kezdünk dolgozni, akkor megadhatjuk az adat azonosítóját, ami rendszerint az azt tartalmazó fájl neve:

/**
 * Mi a teendő ha újabb fájlt kezdünk?
 * @param filename adatfájl neve
 */
public abstract String printBetween(String filename);

Ha az eredmények feldolgozását nem kívánjuk más programokra bízni, megoldható, hogy egyszerűbb leíró statisztikákat implementáljunk, és az így kiszámított eredményeket akár az előbbi rutinnal kiírjuk, ha azok adat-specifikusak, vagy a soron következővel, ha átfogó statisztikákat gyártunk:

/**
 * Mi a teendő e számolások után?
 */
public abstract String printAfter();

Az eredemény kiírásához a végeredményt és a teszt sorszámát adjuk át. Ennek megfelelően ki lehet írni a végeredmény valamely jellemzőjét, vagy statisztikák készítésekor jelezni azt, hogy hol tartunk a sorszám segítségével:

/**
 * A <code>d</code> megoldást kiírja valahogy.
 * @param x végeredmény
 * @param i teszt sorszáma
 */
public abstract String printData(State x, int i);
}

Célfüggvény értéke

A legegyszerűbb kiírás csak a célfüggvény értékét adja meg:

package hu.unideb.inf.optimization.clustering;
import hu.unideb.inf.optimization.methods.PrintSolution;
import hu.unideb.inf.optimization.methods.State;
/**
 * Célfüggvény értéke
 * @author ASZALÓS László
 */
public class PrintValue extends PrintSolution {

Mivel a számítások előtt és után nincs teendőnk, a program üres sztringet ad vissza:

/** Összetartozó megoldások után sort emel.
 */
@Override
public String printAfter() {
    return "";
}

/** Összetartozó megoldások előtt sort emel.
 */
@Override
public String printBefore(String method) {
    return "";
}

Új fájl esetén pedig csak annak nevét írjuk ki:

/** Újabb mátrix esetén kiírja a fájl nevét.
 */
@Override
public String printBetween(String filename) {
    System.err.println("\n");
    return "\n#f " + filename;
}

Sokadik tesztnél már nem izgalmasak a képernyőn futó vagy éppen cammogó számok. Ekkor inkább a teszt előrehaladására vagyunk kíváncsiak. Épp ezért a standard hiba csatornára ezt írjuk ki, míg a standard outputra (amit egy fájlba irányítunk) pedig a célfüggvény értékét. Igaz, hogy ebben az esetben nem lett volna szükség a második segédfüggvényre, de ha a legnagyobb klaszter mérete, vagy az átlagos klaszterméret, klaszterek száma érdekelne, akkor azt ilyen módon lehet megoldani:

@Override
public String printData(State x, int i){
    System.err.println(i%10);
    if (x instanceof Cluster){
        return printDataC((Cluster)x);
    }
    return "";
}/**
 * Kiírja <code>value</code> értékét.
 * @param x optimális megoldás
 */
private String printDataC(Cluster x) {
    return x.getValue() + "\t";
}}

Leíró statisztika

Lássunk egy jóval bonyolultabb esetet!

package hu.unideb.inf.optimization.clustering;
import static hu.unideb.inf.optimization.clustering.Constants.MAXSAMPLE;
import hu.unideb.inf.optimization.methods.PrintSolution;
import hu.unideb.inf.optimization.methods.State;
import java.util.Locale;
/**
 * Elkészíti a megoldások leíró statisztikáit.
 * @author ASZALÓS László
 */
public class PrintStatistic extends PrintSolution {

A könnyebb érthetőség kedvéért a program a kiszámított eredményeket egy tömbben tárolja, melynek mérete konstansként adott. Később a tömbbeli értékék alapján számítjuk ki a statisztikákat. Annak érdekében, hogy tudjuk, hova kell a soron következő eredményt elhelyezni, használnunk kell ezt a helyet tároló változót:

private int index;

Két értéket fogunk figyelemmel követni a legnagyobb csoport méretét és a célfüggvény értékét:

private int maxGroup[] = new int[MAXSAMPLE];
private int value[] = new int[MAXSAMPLE];

A különböző mátrixokat egyben kezeljük, ezért az összes teszt előtt inicializáljuk az indexet:

/** Újabb teszt előtt nullázza a számlálókat */
public String printBefore(String method) {
    index = 0;
    return "";
}

A fájlok között nincs teendőnk:

/** Mivel az összes tesztet egyben kezeli, nincs teendő mátrixváltáskor */
public String printBetween(String filename) {
    return "";
}

Viszont miután megkaptuk az összes adatot, elkezdhetjük a statisztikák kiszámítását. Miután a kiírt adatokat esetleg más programmal fel szeretnénk dolgozni, angol írásmóddal írjuk ki a számokat, hogy a tizedesvessző nehogy megzavarja a tizedespontra váró programokat:

/** Tesztek után alapvető statisztikákat készít:
 * átlag, szórás, min, max
 */
public String printAfter() {
    int sumValue = 0;
    int sumMaxGroup = 0;
    int min = value[0], max = value[0];
    double atlagValue, szorasnegyzet = 0.0, szoras;
    double atlagMaxGroup;
    for (int j = 0; j < index; j++) {
        sumValue += value[j];
        if (min > value[j]) { min = value[j]; }
        if (max < value[j]) { max = value[j]; }
        sumMaxGroup += maxGroup[j];
    }
    atlagValue = sumValue / index;
    for (int j = 0; j < index; j++) {
        szorasnegyzet += Math.pow(value[j] - atlagValue, 2);
    }
    szorasnegyzet /= index;
    szoras = Math.sqrt(szorasnegyzet);
    atlagMaxGroup = sumMaxGroup / index;

    return String.format(Locale.ENGLISH, ""
            + "\navg=%.2f\tdev=%.3f\tmin=%d\tmax=%d\nmax group avg=%.2f\t",
            atlagValue, szoras, min, max, atlagMaxGroup);
}

Itt most nem alkottunk egy újabb rutint, de hasonlóképpen megvizsgáltuk, hogy Cluster-rel dolgozunk-e vagy sem. Most ehhez pluszban azt is megvizsgáljuk, hogy az index-szel nem lépjük-e túl a határt:

/**
 * Tárolja az értékeket.
 * @param x optimális megoldás
 * @param i teszt sorszáma
 */
public String printData(State x, int i) {
    if (x instanceof Cluster){
        if (index < Constants.MAXSAMPLE-1) {
            value[index] = ((Cluster)x).getValue();
            maxGroup[index] = ((Cluster)x).getG().maxGroup();
            index++;
        }
        return "";
    }
    return "";
}
}