-
02.04.07 13:41 #16
Schwierig wird das ganze, wenn man lieber eine nur eine Tablle mit zusammengesetzten Primärschlüssel aus UserID und FreindID verwenden will... Bei www.lokalisten.de werden einem nur die Freunde eines Freundes angezeigt, das war es dann auch schon. Es scheint wohl nicht so einfach zu sein.
Mein neues Projekt: zandman.de - Bericht über den Aufbau einer Entwicklungsumgebung für Test-Driven-Development mit phpUnderControl und dem Aufbau einer Webapplikation mit Zend Framework Version 1.9.x
-
02.04.07 14:18 #17
Die Frage "Wer kennt wen über wen?" ist nüchtern betrachtet, nichts anderes als ein extrem einfacher Pathfinding Algorithmus. Das schöne daran ist, dass übliche Parameter wie "Wegekosten" o.ä. wegfallen und nur die Anzahl der Knoten bis zum Ziel entscheidend für die kürzeste Strecke sind. Daher eignet sich hier das rekursive SQL ("connect by") von Oracle sehr gut.
Rein aus Interesse habe ich mich mal an den wohl bekanntesten Pathfinding Algorithmus (A*) gemacht und in PL/SQL umgesetzt:
Als Grundlage dient mir ein Koordinatensystem mit fixen Orten, dargestellt durch folgende Tabelle und INSERTS
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
0 1 2 3 4 5 6 7 8 9101112 0A B 1 H 2 C 3 D 4 F I 5E 6 G 7 K 8 J N 9 M 10 L 11 O 12 CREATE TABLE places ( placeid NUMBER(10,0) CONSTRAINT NN_places_id NOT NULL, name VARCHAR2(30) CONSTRAINT NN_places_name NOT NULL, posx NUMBER(10,0) CONSTRAINT NN_places_posx NOT NULL, posy NUMBER(10,0) CONSTRAINT NN_places_posy NOT NULL ); ALTER TABLE places ADD CONSTRAINT PK_places PRIMARY KEY ( placeid ) USING INDEX; INSERT INTO places VALUES( 1, 'A', 0, 0 ); INSERT INTO places VALUES( 2, 'B', 4, 0 ); INSERT INTO places VALUES( 3, 'C', 6, 2 ); INSERT INTO places VALUES( 4, 'D', 2, 3 ); INSERT INTO places VALUES( 5, 'E', 0, 5 ); INSERT INTO places VALUES( 6, 'F', 5, 4 ); INSERT INTO places VALUES( 7, 'G', 7, 6 ); INSERT INTO places VALUES( 8, 'H', 9, 1 ); INSERT INTO places VALUES( 9, 'I', 11, 4 ); INSERT INTO places VALUES( 10, 'J', 9, 8 ); INSERT INTO places VALUES( 11, 'K', 3, 7 ); INSERT INTO places VALUES( 12, 'L', 5, 10 ); INSERT INTO places VALUES( 13, 'M', 1, 9 ); INSERT INTO places VALUES( 14, 'N', 12, 8 ); INSERT INTO places VALUES( 15, 'O', 7, 11 );
Die einzelnen Orte sind untereinander mit Strassen verbunden. Als zusätzliche Information werden die Wegekosten abgelegt. In meinem Fall wurden sie einfach über die Länge der Strecke berechnet. Ich gehe also davon aus, dass ich auf allen Strecken gleich schnell voran komme, keine Steigungen o.ä. vorhanden sind etc..
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
CREATE TABLE roads ( sourceid NUMBER(10,0) CONSTRAINT NN_roads_source NOT NULL, targetid NUMBER(10,0) CONSTRAINT NN_roads_target NOT NULL, cost NUMBER(5,0) CONSTRAINT NN_roads_cost NOT NULL ); ALTER TABLE roads ADD CONSTRAINT PK_roads PRIMARY KEY ( sourceid, targetid ) USING INDEX; CREATE UNIQUE INDEX idx_roads_inv ON roads ( targetid, sourceid ); INSERT INTO roads VALUES ( 1, 4, 10 ); INSERT INTO roads VALUES ( 9, 14, 15 ); INSERT INTO roads VALUES ( 10, 14, 10 ); INSERT INTO roads VALUES ( 14, 15, 25 ); INSERT INTO roads VALUES ( 12, 15, 5 ); INSERT INTO roads VALUES ( 13, 12, 15 ); INSERT INTO roads VALUES ( 13, 5, 15 ); INSERT INTO roads VALUES ( 5, 4, 7 ); INSERT INTO roads VALUES ( 8, 9, 12 ); INSERT INTO roads VALUES ( 8, 3, 10 ); INSERT INTO roads VALUES ( 3, 4, 15 ); INSERT INTO roads VALUES ( 3, 2, 7 ); INSERT INTO roads VALUES ( 1, 2, 15 ); INSERT INTO roads VALUES ( 7, 10, 7 ); INSERT INTO roads VALUES ( 6, 7, 7 ); INSERT INTO roads VALUES ( 7, 11, 15 ); INSERT INTO roads VALUES ( 11, 5, 13 ); INSERT INTO roads VALUES ( 8, 7, 23 );
Daraus ergibt sich dann folgende Karte:
(siehe Anhang...)
Der Algorithmus selbst benötigt 2 temporäre Tabellen zur Berechnung:
Code :1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
CREATE GLOBAL TEMPORARY TABLE freelist ( placeid NUMBER(10,0), parentid NUMBER(10,0), abscost NUMBER(10,0), calccost NUMBER(10,0), CONSTRAINT PK_FREELIST PRIMARY KEY ( placeid ) USING INDEX ); CREATE GLOBAL TEMPORARY TABLE locklist ( placeid NUMBER(10,0), parentid NUMBER(10,0), abscost NUMBER(10,0), calccost NUMBER(10,0), CONSTRAINT PK_LOCKLIST PRIMARY KEY ( placeid ) USING INDEX );
Die Funktionsweise des A* Algorithmus möchte ich hier nicht erläutern, das würde den Rahmen sprengen, darum hier einfach der Sourcecode meines Packages:
Code sql:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
CREATE OR REPLACE FUNCTION CALCCOSTS( sourceid IN places.placeid%TYPE, targetid IN places.placeid%TYPE ) RETURN NUMBER IS s places%ROWTYPE; t places%ROWTYPE; calc NUMBER; BEGIN SELECT * INTO s FROM places WHERE placeid = sourceid; SELECT * INTO t FROM places WHERE placeid = targetid; calc := 5 * ROUND( SQRT( (t.POSX - s.POSX)*(t.POSX - s.POSX) + (t.POSY - s.POSY)*(t.POSY - s.POSY) ) ); DBMS_OUTPUT.put_line( '=> Calculation distance between ' || TO_CHAR( sourceid ) || ' and ' || to_char( targetid ) || ' => ' || to_char( calc ) ); RETURN calc; END; / SHO ERRORS CREATE OR REPLACE PACKAGE A_STAR IS PROCEDURE SEARCH( sourceid IN places.placeid%TYPE, targetid IN places.placeid%TYPE ); END; / SHOW ERRORS CREATE OR REPLACE PACKAGE BODY A_STAR IS targetnode places.placeid%TYPE; ------------------------------------------------------------------------------------------------ PROCEDURE INIT_SEARCH IS BEGIN DELETE FROM LOCKLIST; DELETE FROM FREELIST; targetnode := NULL; END; ------------------------------------------------------------------------------------------------ PROCEDURE ADD_TO_LOCKLIST( n_placeid IN places.placeid%TYPE, n_parentid IN places.placeid%TYPE, n_abscost IN NUMBER, n_calccost IN NUMBER, n_rowt OUT locklist%ROWTYPE ) IS BEGIN DBMS_OUTPUT.put_line( '=> Adding node to locklist: ' || TO_CHAR( n_placeid )); INSERT INTO LOCKLIST VALUES( n_placeid, n_parentid, n_abscost, n_calccost ) RETURNING placeid, parentid, abscost, calccost INTO n_rowt; DELETE FROM FREELIST WHERE placeid = n_placeid; END; ------------------------------------------------------------------------------------------------ PROCEDURE STEP_FORWARD( rowt OUT locklist%ROWTYPE ) IS BEGIN SELECT * INTO rowt FROM FREELIST WHERE ABSCOST + CALCCOST = ( SELECT MIN( ABSCOST + CALCCOST ) FROM FREELIST ) AND ROWNUM = 1; END; ------------------------------------------------------------------------------------------------ PROCEDURE MERGE_FREELIST( SOURCE IN locklist%ROWTYPE ) IS BEGIN MERGE INTO FREELIST f USING ( -- finde alle orte, die mit der source verbunden sind und noch nicht in der -- lockliste drin stehen SELECT sourceid AS PLACEID, cost FROM roads WHERE targetid = SOURCE.placeid AND sourceid NOT IN ( SELECT PLACEID FROM locklist ) UNION SELECT targetid AS PLACEID, cost FROM roads WHERE sourceid = SOURCE.placeid AND targetid NOT IN ( SELECT PLACEID FROM locklist ) ) r ON ( f.PLACEID = r.PLACEID ) WHEN MATCHED THEN UPDATE SET f.PARENTID = SOURCE.placeid, f.ABSCOST = SOURCE.abscost + r.cost WHERE f.ABSCOST > SOURCE.abscost + r.cost WHEN NOT MATCHED THEN INSERT VALUES ( r.PLACEID, SOURCE.PLACEID, SOURCE.abscost + r.cost, CALCCOSTS( r.placeid, targetnode )); END; ------------------------------------------------------------------------------------------------ PROCEDURE SEARCH( sourceid IN places.placeid%TYPE, targetid IN places.placeid%TYPE ) IS rowt locklist%rowtype; i PLS_INTEGER := 1; BEGIN INIT_SEARCH; targetnode := targetid; ADD_TO_LOCKLIST( sourceid, sourceid, 0, 0, rowt ); WHILE rowt.placeid <> targetid LOOP MERGE_FREELIST( rowt ); STEP_FORWARD( rowt ); ADD_TO_LOCKLIST( rowt.placeid, rowt.parentid, rowt.abscost, rowt.calccost, rowt ); i := i+1; IF i >= 1000 THEN RETURN; END IF; END LOOP; END; END; / SHOW ERRORS
Der Aufruf erfolgt dann einfach mit
Wobei die beiden Parameter die ID`s des Startknoten und des Zielknoten angeben.Code :1 2
set serveroutput on exec A_STAR.SEARCH( 9, 1 );
Wer sich detailiert damit auseinadersetzen will, kann mal hier ( http://www.gamasutra.com/features/20...pinter_pfv.htm ) etwas tiefer nachlesen. Meine Implementation ist dagegen recht rudimentär.liebe Grüße
Exceptionfault (http://exceptionfault.de)
Never say: "Always"! Always say: "Never say never"! - Tom Kyte @ Ask Tom Live in Berlin 2008
-
Damit hast du wohl Recht. Sry nicht gesehen.
Ich dachte es geht jetzt nur um mysql.
Hmm, was das Beispiel angeht - Damit komme ich irgendwie nicht ganz klar. Bin selber dabei eine Lösung zu finden. Bin bereits soweit gekommen, dass ich eine Person zwischen zwei Personen bestimmen kann. Wobei die Lösung mehr oder weniger mit php umgesetzt wurde, mit array Funktionen. Würde mich freuen wenn jemand aus diesem Forum mal ein Script speziell für mysql entwickeln könnte.
-
02.04.07 15:22 #19
- Registriert seit
- Jun 2002
- Ort
- Saarbrücken (Saarland)
- Beiträge
- 9.886
- Blog-Einträge
- 29
Hallo,
Genau dieses Problem wird in dem Link in meinem ersten Post für MySQL (zum Teil über Stored procedures) gelöst: http://www.artfulsoftware.com/mysqlb...qled1ch20.htmlHmm, was das Beispiel angeht - Damit komme ich irgendwie nicht ganz klar. Bin selber dabei eine Lösung zu finden. Bin bereits soweit gekommen, dass ich eine Person zwischen zwei Personen bestimmen kann. Wobei die Lösung mehr oder weniger mit php umgesetzt wurde, mit array Funktionen. Würde mich freuen wenn jemand aus diesem Forum mal ein Script speziell für mysql entwickeln könnte.
Gruß TomJava rocks!
How to become a good Java Programmer?
Does IT in Java and .Net
The only valid measurement of code quality: WTFs / minute
Blog
Xing
Twitter
Ähnliche Themen
-
Mit <select> Bilder in einem Verzeichnis auflisten
Von Maxm123 im Forum PHPAntworten: 3Letzter Beitrag: 25.09.11, 15:23 -
[Oracle 8i] An Oracle 8i über Vb und ODBC anmelden/DB bearbeiten
Von Animal21 im Forum Relationale DatenbanksystemeAntworten: 0Letzter Beitrag: 02.06.08, 11:00 -
Bei einem Graph Diagramm, Bild einfügen
Von MasterBN im Forum PHPAntworten: 4Letzter Beitrag: 17.09.07, 14:39 -
Alle Dateien auf einem Webserver auflisten
Von danielm im Forum PHPAntworten: 5Letzter Beitrag: 21.09.05, 20:31 -
Alle Farben in einem Bild auflisten?
Von Jersey im Forum PhotoshopAntworten: 4Letzter Beitrag: 20.01.05, 15:47



1Danke

Zitieren

Login





