Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

1

01.12.2015, 13:56

PriorityQueue alternativen

Hallo, ich implementiere gerade den A*-Algorithmus in Java.
Ich habe eine GraphNode-Klasse für die Knoten und für die zusätzlichen Werte (wie im Wiki-Artikel hier beschrieben), verwende ich eine Wrapper-Klasse OpenListEntry, das noch g und h, sowie einen f-Getter beinhaltet.

Die OpenList ist eine PriorityQueue. Nun ist das Problem, dass ich beim Bearbeiten der adjazenten Knoten evtl. auf bereits gefundene Knoten stoße.
In OpenListEntry habe ich bereits einen equals()-Override, der Gleichheit durch Gleichheit der gewrappten Knoten definiert, somit kann ich über PriorityQueue.contains() herausfinden ob der Knoten schon hinzugefügt wurde. Jedoch besteht hier keine Möglichkeit den Eintrag zu bekommen, da es eine Queue ist. Ich muss aber herausfinden welcher F-Wert bisher in der Queue stand.

Da es so etwas wie eine PriorityList nicht gibt, habe ich mir 2 Möglichkeiten überlegt.

1. Eine normale List, die in jedem Durchlauf neu sortiert wird (ineffizient?, da ja immer nur minimal verändert evtl. Insertion Sort?)
2. Ich speichere parallel zur PriorityQueue eine Hashmap in der jedem Node das F als Value gemappt wird, so kann ich die Werte schnell finden und verändern.

Was meint ihr dazu?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »birdfreeyahoo« (01.12.2015, 14:01)


birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

2

01.12.2015, 14:16

Ich dachte halt man kann das InsertionSort mit dem Einfügen verbinden, da die Liste eh eins nach dem anderen aufgebaut wird, kann ich doch einen neuen Knoten beim hinzufügen direkt richtig hinschieben.

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

3

01.12.2015, 14:28

Alternativ könnte man dem Knoten einfach ein Flag geben, das sagt, ob er schon besucht wurde... ;)

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

4

01.12.2015, 14:29

Warum nicht in den Knoten selbst abspeichern, ob sie schon gefunden wurden?

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

5

01.12.2015, 14:30

Das herauszufinden ist ja wie gesagt nicht schwer, doch herauszufinden mit welcher f-Distanz, das stellt sich als Problem heraus.
Ich könnte die Daten einfach direkt in die Knoten einbauen, aber mir ist dabei nicht wohl, weil ich die Knoten unabhängig vom Algorithmus gerne habe.
Oder macht man das oft so?

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »birdfreeyahoo« (01.12.2015, 14:39)


David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

6

01.12.2015, 15:05

Da hast du schon Recht. Es ist schöner, wenn die Informationen getrennt vorliegen. Du arbeitest mit Java, richtig? Es scheint eine Limitierung zu sein, dass man zwar abfragen kann, ob ein Element vorhanden ist, man aber nicht an das Element herankommt (in C++ gibt es find, das einen Iterator liefert). Du könntest deine eigene Priority-Queue implementieren, die dies ermöglicht.

Da fällt mir ein Hack ein: Du weißt ja, dass wenn contains() true liefert, zuletzt deine überladene equals()-Methode aufgerufen wurde und diese ebenfalls true geliefert hat. Dort kannst du dann einfach den this-Zeiger irgendwo speichern, und somit hast du am Ende das gefundene Objekt ... ;) Nicht schön, sollte aber funktionieren. Aber aufpassen bei Multithreading!

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

7

01.12.2015, 15:10

Oh...weiß nicht ist ein Uni-Projekt :D Trotzdem eine sau gute Idee.

Unabhängig von der Weise, wie ich da jetzt rankomme, kann vielleicht jemand kurz über den Code schauen zur Bestätigung? Das ist meine letzte Version wo ich noch eine extra Map für die f-Werte habe:

C#-Quelltext

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
/**
 * Implements A* Algotithm for pathfinding with manhattan metric
 */
public class AStar {

    /**
     * Finds the shortest path from startNode to targetNode
     * @param graph The graph the algorithm is applied on
     * @param startNode The start point
     * @param targetNode The target
     */
    public static Queue<GraphNode> shortestPath(Graph graph, GraphNode startNode, GraphNode targetNode) {
        PriorityQueue<OpenListEntry> openList = new PriorityQueue<>(graph.nodes.length, new OpenListEntryComparator());
        // For quickly accessing the f-values
        Map<GraphNode, Double> fMap = new HashMap<>();
        // Maps previous node to node
        Map<GraphNode, GraphNode> closedList = new HashMap<>();


        // Inserting the startNode
        openList.add(new OpenListEntry(startNode, 0, manhattanEstimateDistance(startNode, targetNode), null));
        fMap.put(startNode, manhattanEstimateDistance(startNode, targetNode));

        // Traversing all elements in openList
        for(int i = 0; i < graph.nodes.length; i++) {
            OpenListEntry olentry = openList.poll();

            for(GraphNode adjacent : olentry.node.getAdjacentNodes()) {
                OpenListEntry newEntry = new OpenListEntry(adjacent, olentry.g + manhattanEstimateDistance(adjacent, olentry.node), manhattanEstimateDistance(adjacent, targetNode), olentry.node);
                if(fMap.containsKey(adjacent)) {
                    if(newEntry.f() < fMap.get(adjacent)) {
                        // Removes object with same node
                        openList.remove(newEntry);
                    }
                }

                fMap.put(adjacent, newEntry.f());
                openList.add(newEntry);
            }

            // automatically removed from openList by calling poll()
            closedList.put(olentry.node, olentry.previous);


            // We surely have a path now
            if(olentry.node == targetNode) {
                break;
            }

        }

        LinkedList<GraphNode> path = new LinkedList<>();

        if(!closedList.containsKey(targetNode)) {
            return path;
        }

        GraphNode next = targetNode;
        while(next != null) {
            // The targetNode should be the last one then, so you start at the first one when polling from the queue
            path.addFirst(next);
            next = closedList.get(next);
        }

        if(path.getFirst() != startNode) {
            Log.w("A-STAR", "Unexpected behaviour in A-Star algorithm");
        }

        return path;
    }

    /**
     * Estimates the distance by manhattan distance. Also is the distance of 2 adjacent nodes.
     * @param node
     * @param target
     * @return
     */
    private static double manhattanEstimateDistance(GraphNode node, GraphNode target) {
        double xDiff = Math.abs(target.getX() - node.getX());
        double yDiff = Math.abs(target.getY() - node.getY());
        double zDiff = Math.abs(target.getZ() - node.getZ());

        return xDiff + yDiff + zDiff;
    }

    private static class OpenListEntry {
        public GraphNode node;
        // The distance that is estimated by the heuristic
        public double h;
        // The distance already gone from the startNode
        public double g;

        public GraphNode previous;

        /**
         *
         * @return The estimated total distance through that node
         */
        public double f() {
            return g+h;
        }

        public OpenListEntry(GraphNode node, double h, double g, GraphNode previous) {
            this.node = node;
            this.h = h;
            this.g = g;
        }


        @Override
        public boolean equals(Object o) {
            if(!(o instanceof OpenListEntry))
                return false;

            return node == ((OpenListEntry)o).node;
        }
    }


    private static class OpenListEntryComparator implements Comparator<OpenListEntry> {

        @Override
        public int compare(OpenListEntry lhs, OpenListEntry rhs) {
            double diff = (lhs.f() - rhs.f());

            // We can't directly return diff, because it is a double and maybe the decimals get lost by a direct cast
            return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
        }
    }
}

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

8

01.12.2015, 15:56

Mir tut es immer in den Augen weh, wenn ich überall static sehe. Ich denke dann immer, da hat jemand das mit dem objektorientierten mal so gar nicht verstanden. Warum ist AStar kein Objekt? Aber man kann es auch rein funktional programmieren. Auf mich wirkt das immer so, als wenn jemand keine Lust hat sich Gedanken zu machen oder es nicht kann. Das ist nicht persönlich gemeint. Ich kenne viele, die leider auch so programmieren und man kriegt es manchmal selbst aus erfahrenen Leuten nicht raus. Programmierst Du lieber in C? Ist nur eine Frage.
Was ist denn mit Unit Tests?

birdfreeyahoo

Alter Hase

  • »birdfreeyahoo« ist der Autor dieses Themas

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

9

01.12.2015, 17:16

Ich weiß nicht ob ein Algorithmus als Objekt so viel Sinn macht, vorallem gibt es hier keine wirklichen Eigenschaften und mMn ist das unnötig in dem Fall, weil es in der Tat eine Funktion(ssammlung) ist. Wie bei Math in Java übrigens auch.

Was soll mit den Unit-Tests denn sein?

TGGC

1x Rätselkönig

Beiträge: 1 799

Beruf: Software Entwickler

  • Private Nachricht senden

10

01.12.2015, 17:37

Echt mal, was ist denn jetzt mit Unit Tests!

Werbeanzeige