Gruppen Matching

Status
Nicht offen für weitere Antworten.
P

parasara

Gast
Hallo,

ich möchte gerne ein Programm schreiben, dass mir die beste Zuordnung von Zahlenpaaren ausgibt.
Ich habe zwei Personengruppen; jede Person hat zwei Werte: Alter und IQ. Nun soll aus jeder Gruppe der ähnlichste Partner aus der anderen Gruppe gefunden werden.
Die grundsätzliche Formel dafür ist:
(AlterA-AlterB)^2+(IQa-IQb)^2
Mein Ziel ist, das Minimum davon für alle Personen zu finden. Natürlich darf jeder Person nur eine andere zugeordnet werden.
Mein erster Versuch in Java:


Code:
public class Matching{
  public static void main(String [] args){

  int /*contr_age [], contr_iq [],*/ contr_length;
  int /*mci_age [], mci_iq [],*/ mci_length;
  contr_length= 4;
  mci_length= 4;
  int minimum_cm;
  int minimal_pair= 100;
  int paira[]=new int[30];
  int pairb[]=new int[30];
  int pairi[]=new int[30];
  int pairj[]=new int[30];
  int contr_age[]= { 75,74,76,74};
  int contr_iq[] = { 110,115,112,100};
  int mci_age[]= { 73,75,74,77};
  int mci_iq[] = { 105,113,108,106};
  int i,j;

  for(i=0; i<=contr_length-1; i++){
          for(j=0;j<=mci_length-1;j++){
          minimum_cm=(contr_age[i] - mci_age[j])*(contr_age[i] - mci_age[j])+(contr_iq[i]-mci_iq[j])*(contr_iq[i]-mci_iq[j]);
          if (minimum_cm <= minimal_pair)
             {
               minimal_pair = minimum_cm;
             //minimal_pair =
             paira[i]=contr_age[i];
             pairb[i]=mci_age[j];
             pairi[i]=contr_iq[i];
             pairj[i]=mci_iq[j];
             }
             
          }
          }
             for (i=0;i<=40;i++){

          IO.println("minimal pair: "+ paira[i] +" "+pairb[i] +" "+ pairi[i] +" "+ pairj[i]);
          }
}
}


Ich benutze ein vorgefertigtes Paket, daher das IO.println...
Eigentlich gehören mehr Alter und IQ-Werte in die Arrays, aber zum probieren fand ich es so leichter.
Außerdem wollte ich auch die Länge berechnen lassen durch mci_age.length() anstatt 4 vorzugeben, aber das geht irgendwie nicht...
Insgesamt ist der Code auch nicht so professionell, ich weiß. Aber er soll mir nur die Berechnung richtig durchführen...
Könntet Ihr euch das bitte mal ansehen, und mir Tipps geben, wie ich das bekomme was ich möchte?

Ich freue mich auf eure Antworten!

Viele Grüße
parasara
 

Marco13

Top Contributor
Nicht
mci_age.length()
sondern
mci_age.length

Und zum eigentlichen Problem: Je nachdem, welche Ansprüche du das hast ... wenn man da einfach einen "Greedy-Algorithmus" drauf losläßt, könnte das evtl. schon reichen: Man such für jede Person p den Partner q, der am besten passt, und nimmt dann p und q aus dem Menge der Personen raus. Allerdings liefert das nicht (immer) die optimale Lösung: Was du suchst ist (FALLS ich das richtig sehe) präzise(r) formuliert ein "Minimales Gewichtetes Matching" - und das ist nicht ganz trivial. Kannst mal auf http://en.wikipedia.org/wiki/Matching schauen, und vielleicht mit sowas wie http://www.google.com/search?q=grap...illa-search&start=0&start=0&ie=utf-8&oe=utf-8 nach möglichen Ansätzen suchen...
 

Marco13

Top Contributor
Nachtrag: Wenn du schon zwei getrennte Gruppen hast (Männlein und Weiblein, vermutlich :wink: ) dann kannst du das ganze glaub ich auf einem Graphen lösen, der (englisch: ) "bipartite" ist - dadurch wird's AFAIK einfacher... (Asymptotisch - nicht in bezug auf die Implementierung an sich :wink: )
 

0x7F800000

Top Contributor
Hm, zwei Gruppen mit IQ, wobei es auch noch auf das Alter ankommt... Hört sich tatsächlich nach irgendeiner Singlebörse an, die jedoch irgendwie recht "nazimäßig" den partner suchen soll^^

Falls es das ist, dann finde ich einen solchen Algorithmus zum einen extrem uneffizient (um die aufgabe wirklich 100% exakt und bombensicher zu lösen, braucht man wohl O(n!) was sehr übel ist, oder man muss sich schon irgendwelche sehr abgefahrene KomplexitätsReduktionAnsätze ausdenken, dann kannst du schonmal eine Doktorarbeit drüberschreiben) und zum anderen wäre ein solcher Algorithmus in dem fall unbrauchbar bis unnötig.

Besser wäre es dann, auf die 2D-IQ-Alter ebene ein Quadtree drüberzulegen, und alle Personen in die Passenden Leafs des Baums einzuordnen, und je nach dem, wie exakt die voraussetzungen erfüllt werden sollen, muss man nur noch die unmittelbare umgebung absuchen und die passendsten treffer liefern, was nach dem aufbau des baumes praktisch in konstanter Zeit geht. Vor allem kann man da schnell neue Personen hinzufügen, ohne alles neuberechnen zu müssen.

Aber wie gesagt, das wäre zwar etwas effizienter und würde wenigstens ein paar auswahlmöglichkeiten bieten, aber das braucht eh keiner, weil die Euklidische Metrik für sowas nun mal völlig ungeeignet ist. Soll ja auch leute geben, die einen jungeren oder älteren partner suchen, und nicht unbedingt fordern, dass der partner ebenfalls einen dreifachen professortitel und IQ von 34192 hat^^
 
P

parasara

Gast
Hallo!
Vielen Dank schonmal für eure Antworten! Die Länge funktioniert nun... hatte zwischenzeitlich vergessen, dass auch die Reihenfolge wichtig ist.
Die anderen Vorschläge versuche ich nun mal umzusetzen.
Und: Nein, es handelt sich dabei nicht um eine Partnerbörse oder ähnliches. Es geht um die Auswertung eines Experimentes. :)
Viele Grüße
parasara
 

Marco13

Top Contributor
Andrey hat gesagt.:
Falls es das ist, dann finde ich einen solchen Algorithmus zum einen extrem uneffizient (um die aufgabe wirklich 100% exakt und bombensicher zu lösen, braucht man wohl O(n!) was sehr übel ist,

Welchen meinst du jetzt? Der beschriebene "brute force" hat erstmal ... öhm O(n^2) oder so, und ein miniamles gewictetes Matching in bipartiten Graphen läßt sich (wenn ich das bei Wikipedia richtig überflogen habe) auch mit O(E*V^2) implementieren... Ob das ganze dann ein "sinnvolles" Ergebnis liefert ist ja eine ganz andere Frage :lol:
 

0x7F800000

Top Contributor
Marco13 hat gesagt.:
öhm O(n^2) oder so
wieso? wenn ich stur alle disjunkte 2-Tupel aus den gleichmächtigen Mengen A und B mit |A|=|B|=n bilden will, kriege ich doch genau n! möglichkeiten. Also, das ist doch genau die Mächtigkeit der n-ten symmetrischen Gruppe, denn ich will ja im schlimmsten fall bruteforcig alle Permutationen durchprobieren, und mit dieser alters-iq-metrik bewerten. Und das liefert mit sicherheit das beste ergebnis, weil man ja jede erdenkliche Möglichkeit durchprobiert.
 

Marco13

Top Contributor
Kann sein, dass ich da was falsch verstanden hatte. Das ursprünglich Brute force war ja grob
Code:
Für jede Person p
{
    Suche besten Partner q
    entferne p und q aus der Liste
}
also immer den erstbesten, und nichts mit "alle Kombination". Letzeres hätte wohl exponentielle Laufzeit, gut möglich...
 

0x7F800000

Top Contributor
Das was du vorschlägst, hätte O(n²/2) laufzeit. Bei hinreichend vielen, zufällig und gleichmäßig verteilten punkten würde es auch was liefern, was ziemlich nah an einer optimalen Lösung ist, aber wahrscheinlich nie die beste. falls man wirklich ALLES durchprobieren will, wird es leider nicht mal beim O(exp(n)) bleiben, sondern, wie gesagt, mit O(n!) davonfliegen, was eigentlich nur rein theoretisch "funktioniert", und zwar nur, wenn man das haltbarkeitsdatum des Universums ausser acht lässt^^ :p
 

Marco13

Top Contributor
Ja, und O(n^2 / 2) == O(n^2) - allerdings war ich irrtümlich davon ausgegangen, dass O(n!) = O(2^n) ist, und bin nicht sicher, ob man das nicht in "nur" exponentieller Laufzeit lösen könnte, aber ... das will ich jetzt nicht nachvollziehen...
 
P

parasara

Gast
Leider konnte ich das alles nicht umsetzen. Bin Programmier-Laie... kann mir bitte jemand praktisch helfen?! Ich habe auch einen R-Code gefunden, der wohl macht was ich möchte. Nur kenne ich mich mit R noch weniger aus und weiß nicht wie ich das dann auf meine Daten anwenden kann...
 

0x7F800000

Top Contributor
Ach je...
Nun ja, sag doch bitte nochmal, ob du einen fast-optimalen algorithmus brauchst, der was sinnvolles liefert (~für's fach Bio), oder ob du wirklich die optimale Lösung brauchst (~für reine Mathematik)

das Problem ist, wie gesagt, vielleicht nicht so trivial, ob da jetzt jemand rein aus neugier sich hinhockt und sowas baut?
 
G

Gast

Gast
Hm, ok. Wenn du so fragst, würde mir auch was fast-optimales genügen. In R sah das auch gar nicht so schlimm aus, deshalb dachte ich, ich könnte es in Java hinbekommen. Vielleicht kann das einer von euch "übersetzen"?!
Code:
n = length(x1)
y.y1 = y1
y.y2 = y2
y.match = matrix(NA, nrow=n, ncol=2)

for (i in 1:n){
sqr.diff = (y.y1-x1[i])^2+(y.y2-x2[i])^2
index = which(sqr.diff == min(sqr.diff))
y.match[i,1] = y.y1[index[1]]
y.match[i,2] = y.y2[index[1]]
y.y1 = y.y1[-c(index[1])]
y.y2 = y.y2[-c(index[1])]
}
return(y.match)
 

Marco13

Top Contributor
Andrey hat gesagt.:
ob da jetzt jemand rein aus neugier sich hinhockt und sowas baut?

Zufälligerweise baue ich gerade etwas, wovon eine Teilmenge eine Teilmenge des Problems lösen würde. Deswegen hab' ich (rein aus Neugier :wink: ) auch mal gewebsucht, und da über http://en.wikipedia.org/wiki/Hungarian_algorithm was gefunden - nicht auf Graphen basierend, sondern auf Matrizen ... kannst ja schauen ob's für dich passt.
Code:
// Adapted code from [url]http://sites.google.com/site/garybaker/hungarian-algorithm/assignment[/url]
// for [url]http://www.java-forum.org/de/viewtopic.php?t=76171&highlight=[/url]

import java.util.*;


class Person
{
    private int age;
    private int iq;

    public Person(int age, int iq)
    {
        this.age = age;
        this.iq = iq;
    }

    public int getAge()
    {
        return age;
    }

    public int getIQ()
    {
        return iq;
    }

    public String toString()
    {
        return "Person("+age+","+iq+")";
    }
}

class Assignment
{
    public static void main(String args[])
    {
        List<Person> pa = new ArrayList<Person>();
        pa.add(new Person(75, 110));
        pa.add(new Person(74, 115));
        pa.add(new Person(76, 112));
        pa.add(new Person(74, 100));

        List<Person> pb = new ArrayList<Person>();
        pb.add(new Person(73, 105));
        pb.add(new Person(75, 113));
        pb.add(new Person(74, 108));
        pb.add(new Person(77, 106));

        solve(pa, pb);

    }

    private static void solve(List<Person> pa, List<Person> pb)
    {
        float cost[][] = new float[pa.size()][pb.size()];
        for (int i=0; i<pa.size(); i++)
        {
            for (int j=0; j<pb.size(); j++)
            {
                cost[i][j] = computeCost(pa.get(i), pb.get(j));
            }
        }
        AssignmentProblem ap = new AssignmentProblem(cost);
        int result[][] = ap.solve(new HungarianAlgorithm());
        float totalCost = 0;
        for (int i=0; i<result.length; i++)
        {
            Person a = pa.get(result[i][0]);
            Person b = pb.get(result[i][1]);
            float c = computeCost(a,b);
            System.out.println(a+" and "+b+" cost "+c);
            totalCost += c;
        }
        System.out.println("totalCost "+totalCost);

    }

    private static float computeCost(Person p0, Person p1)
    {
        int da = p0.getAge() - p1.getAge();
        int di = p0.getIQ() - p1.getIQ();
        return da * da + di * di;
    }



}







//=====================================================================================
// From [url]http://sites.google.com/site/garybaker/hungarian-algorithm/assignment[/url]



/**
 * Just in case I want to try more than one implementation of the assignment algorithm.
 * Copyright 2007 Gary Baker (GPL v3)
 * @author gbaker
 */
interface AssignmentAlgorithm {
    int[][] computeAssignments(float[][] costMatrix);
}
/**
 * an implementation of this: [url]http://en.wikipedia.org/wiki/Hungarian_algorithm[/url]
 *
 * based loosely on Guy Robinson's description of the algorithm here: [url]http://www.netlib.org/utk/lsi/pcwLSI/text/node222.html#SECTION001182000000000000000[/url]
 *
 * In short, it finds the cheapest assignment pairs given a cost matrix.
 *
 * Copyright 2007 Gary Baker (GPL v3)
 * @author gbaker
 */
class AssignmentProblem {

    private final float[][] costMatrix;

    public AssignmentProblem(float[][] aCostMatrix) {
        costMatrix = aCostMatrix;
    }

    private float[][] copyOfMatrix() {
        // make a copy of the passed array
        float[][] retval = new float[costMatrix.length][];
        for (int i = 0; i < costMatrix.length; i++) {
            retval[i] = new float[costMatrix[i].length];
            System.arraycopy(costMatrix[i], 0, retval[i], 0, costMatrix[i].length);
        }
        return retval;
    }


    public int[][] solve(AssignmentAlgorithm anAlgorithm) {

        float[][] costMatrix = copyOfMatrix();
        return anAlgorithm.computeAssignments(costMatrix);

    }
}


/**
 * An implementation of the classic hungarian algorithm for the assignment problem.
 *
 * Copyright 2007 Gary Baker (GPL v3)
 * @author gbaker
 */
class HungarianAlgorithm implements AssignmentAlgorithm {



    public int[][] computeAssignments(float[][] matrix) {


        // subtract minumum value from rows and columns to create lots of zeroes
        reduceMatrix(matrix);


        // non negative values are the index of the starred or primed zero in the row or column
        int[] starsByRow = new int[matrix.length]; Arrays.fill(starsByRow,-1);
        int[] starsByCol = new int[matrix[0].length]; Arrays.fill(starsByCol,-1);
        int[] primesByRow = new int[matrix.length]; Arrays.fill(primesByRow,-1);

        // 1s mean covered, 0s mean not covered
        int[] coveredRows = new int[matrix.length];
        int[] coveredCols = new int[matrix[0].length];

        // star any zero that has no other starred zero in the same row or column
        initStars(matrix, starsByRow, starsByCol);
        coverColumnsOfStarredZeroes(starsByCol,coveredCols);

        while (!allAreCovered(coveredCols)) {

            int[] primedZero = primeSomeUncoveredZero(matrix, primesByRow, coveredRows, coveredCols);

            while (primedZero == null) {
                // keep making more zeroes until we find something that we can prime (i.e. a zero that is uncovered)
                makeMoreZeroes(matrix,coveredRows,coveredCols);
                primedZero = primeSomeUncoveredZero(matrix, primesByRow, coveredRows, coveredCols);
            }

            // check if there is a starred zero in the primed zero's row
            int columnIndex = starsByRow[primedZero[0]];
            if (-1 == columnIndex){

                // if not, then we need to increment the zeroes and start over
                incrementSetOfStarredZeroes(primedZero, starsByRow, starsByCol, primesByRow);
                Arrays.fill(primesByRow,-1);
                Arrays.fill(coveredRows,0);
                Arrays.fill(coveredCols,0);
                coverColumnsOfStarredZeroes(starsByCol,coveredCols);
            } else {

                // cover the row of the primed zero and uncover the column of the starred zero in the same row
                coveredRows[primedZero[0]] = 1;
                coveredCols[columnIndex] = 0;
            }
        }

        // ok now we should have assigned everything
        // take the starred zeroes in each column as the correct assignments

        int[][] retval = new int[matrix.length][];
        for (int i = 0; i < starsByCol.length;  i++) {
            retval[i] = new int[]{starsByCol[i],i};
        }
        return retval;





    }

    private boolean allAreCovered(int[] coveredCols) {
        for (int covered : coveredCols) {
            if (0 == covered) return false;
        }
        return true;
    }


    /**
     * the first step of the hungarian algorithm
     * is to find the smallest element in each row
     * and subtract it's values from all elements
     * in that row
     *
     * @return the next step to perform
     */
    private void reduceMatrix(float[][] matrix) {

        for (int i = 0; i < matrix.length; i++) {

            // find the min value in the row
            float minValInRow = Float.MAX_VALUE;
            for (int j = 0; j < matrix[i].length; j++) {
                if (minValInRow > matrix[i][j]) {
                    minValInRow = matrix[i][j];
                }
            }

            // subtract it from all values in the row
            for (int j = 0; j < matrix[i].length; j++) {
                matrix[i][j] -= minValInRow;
            }
        }

        for (int i = 0; i < matrix[0].length; i++) {
            float minValInCol = Float.MAX_VALUE;
            for (int j = 0; j < matrix.length; j++) {
                if (minValInCol > matrix[j][i]) {
                    minValInCol = matrix[j][i];
                }
            }

            for (int j = 0; j < matrix.length; j++) {
                matrix[j][i] -= minValInCol;
            }

        }

    }

    /**
     * init starred zeroes
     *
     * for each column find the first zero
     * if there is no other starred zero in that row
     * then star the zero, cover the column and row and
     * go onto the next column
     *
     * @param costMatrix
     * @param starredZeroes
     * @param coveredRows
     * @param coveredCols
     * @return the next step to perform
     */
    private void initStars(float costMatrix[][], int[] starsByRow, int[] starsByCol) {


        int [] rowHasStarredZero = new int[costMatrix.length];
        int [] colHasStarredZero = new int[costMatrix[0].length];

        for (int i = 0; i < costMatrix.length; i++) {
            for (int j = 0; j < costMatrix[i].length; j++) {
                if (0 == costMatrix[i][j] && 0 == rowHasStarredZero[i] && 0 == colHasStarredZero[j]) {
                    starsByRow[i] = j;
                    starsByCol[j] = i;
                    rowHasStarredZero[i] = 1;
                    colHasStarredZero[j] = 1;
                    break; // move onto the next row
                }
            }
        }
    }


    /**
     * just marke the columns covered for any coluimn containing a starred zero
     * @param starsByCol
     * @param coveredCols
     */
    private void coverColumnsOfStarredZeroes(int[] starsByCol, int[] coveredCols) {
        for (int i = 0; i < starsByCol.length; i++) {
            coveredCols[i] = -1 == starsByCol[i] ? 0 : 1;
        }
    }


    /**
     * finds some uncovered zero and primes it
     * @param matrix
     * @param primesByRow
     * @param coveredRows
     * @param coveredCols
     * @return
     */
    private int[] primeSomeUncoveredZero(float matrix[][], int[] primesByRow,
                                       int[] coveredRows, int[] coveredCols) {


        // find an uncovered zero and prime it
        for (int i = 0; i < matrix.length; i++) {
            if (1 == coveredRows[i]) continue;
            for (int j = 0; j < matrix[i].length; j++) {
                // if it's a zero and the column is not covered
                if (0 == matrix[i][j] && 0 == coveredCols[j]) {

                    // ok this is an unstarred zero
                    // prime it
                    primesByRow[i] = j;
                    return new int[]{i,j};
                }
            }
        }
        return null;

    }

    /**
     *
     * @param unpairedZeroPrime
     * @param starsByRow
     * @param starsByCol
     * @param primesByRow
     */
    private void incrementSetOfStarredZeroes(int[] unpairedZeroPrime, int[] starsByRow, int[] starsByCol, int[] primesByRow) {

        // build the alternating zero sequence (prime, star, prime, star, etc)
        int i, j = unpairedZeroPrime[1];

        Set<int[]> zeroSequence = new LinkedHashSet<int[]>();
        zeroSequence.add(unpairedZeroPrime);
        boolean paired = false;
        do {
            i = starsByCol[j];
            paired = -1 != i && zeroSequence.add(new int[]{i,j});
            if (!paired) break;

            j = primesByRow[i];
            paired = -1 != j && zeroSequence.add(new int[]{ i, j });

        } while (paired);


        // unstar each starred zero of the sequence
        // and star each primed zero of the sequence
        for (int[] zero : zeroSequence) {
            if (starsByCol[zero[1]] == zero[0]) {
                starsByCol[zero[1]] = -1;
                starsByRow[zero[0]] = -1;
            }
            if (primesByRow[zero[0]] == zero[1]) {
                starsByRow[zero[0]] = zero[1];
                starsByCol[zero[1]] = zero[0];
            }
        }

    }


    private void makeMoreZeroes(float[][] matrix, int[] coveredRows, int[] coveredCols) {

        // find the minimum uncovered value
        float minUncoveredValue = Float.MAX_VALUE;
        for (int i = 0; i < matrix.length; i++) {
            if (0 == coveredRows[i]) {
                for (int j = 0; j < matrix[i].length; j++) {
                    if (0 == coveredCols[j] && matrix[i][j] < minUncoveredValue) {
                        minUncoveredValue = matrix[i][j];
                    }
                }
            }
        }

        // add the min value to all covered rows
        for (int i = 0; i < coveredRows.length; i++) {
            if (1 == coveredRows[i]) {
                for (int j = 0; j < matrix[i].length; j++) {
                        matrix[i][j] += minUncoveredValue;
                }
            }
        }

        // subtract the min value from all uncovered columns
        for (int i = 0; i < coveredCols.length; i++) {
            if (0 == coveredCols[i]) {
                for (int j = 0; j < matrix.length; j++) {
                    matrix[j][i] -= minUncoveredValue;
                }
            }
        }
    }




}



(Will garnicht wissen, wem ich da jetzt wieder welche Hausaufgaben gemacht habe...)
 
P

parasara

Gast
Merci. Vielleicht bringt mich das endlich weiter. Sieht auf den ersten Blick ja gut aus!
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben