-1

Which is the best away to create and store cycles using c/c++?

I have the structs:

struct CYCLE {
    vector<Arc> route;
    float COST;
}

struct Arc {

    int i, j;
    Arc () {};
    Arc (const Arc& obj): i(obj.i), j(obj.j) {};
    Arc(int _i, int _j) : i(_i), j(_j) {}    

};

To store the cycles that have already been created, I thought about using:

vector<CYCLE> ConjCycles;

For each cycle created, I need to verify if this cycle has not yet been added to the ConjCycles. The cycle: 1-2-2-1; is the same as the cycle: 2-2-1-2. How can I detect that cycles like those are the same? I thought about using a map to control this. However, I don't know how to set a key to the cycle, so that the two cycles described above have the same key.

rbl
  • 17
  • 6
  • Would you describe what you mean by *cycle*, and what does it have to do with [dictionary](https://stackoverflow.com/questions/tagged/dictionary)? – eerorika Mar 27 '18 at 16:08
  • Cycle here is a sequence of vertex visited by a vehicle. A cycle must start and end in the same vertex, but a vertex can be visited more than one time in a cycle. – rbl Mar 27 '18 at 16:21
  • So, for example, 1-2-3-4-1 is the same cycle as 1-3-2-4-1? If it's like this, you're essentially trying to find the "difference" between 2 cycles, right? – Marco Luzzara Mar 27 '18 at 16:35
  • They are not the same. To be the same, the cycles must have the same arcs. – rbl Mar 27 '18 at 16:37
  • 1-2-3-4-1 is the same cycle as 3-4-1-2-3. – rbl Mar 27 '18 at 16:37
  • You might ["canonicalize"](https://en.wikipedia.org/wiki/Graph_canonization) your cycles: 1. define an order. e.g. a less-than for the whole sequence of numbers. 2. remove last number (it's duplicate of first, isn't it) 3. rotate sequence of numbers until it becomes least concerning the defined order. After that, you should have a unique key for your cycles which can be compared. – Scheff's Cat Mar 27 '18 at 18:05
  • Do you have an example of how I can canonicalize the cycles? – rbl Mar 27 '18 at 19:11

1 Answers1

1

You have quite a lot of redundancy in your cycle representation, e. g. for a cycle 1-3-2-4-1:

{ (1, 3), (3, 2), (2, 4), (4, 1) }

If we consider a cycle being a cyclic graph, then you store the edges in your data structure. It would be more efficient to store the vertices instead:

struct Cycle
{
    std::vector<int> vertices;
};

The edges you get implicitly from vertices[n] and vertices[n + 1]; the last vertex is always the same as the first one, so do not store it explicitly, the last edge then will be vertices[vertices.size() - 1], vertices[0].

Be aware that this is only internal representation; you still can construct the cycle from a sequence of edges (Arcs). You'd most likely check the sequence in the constructor and possibly throw an exception, if it is invalid (there are alternatives, though, if you dislike exceptions...).

Then you need some kind of equivalence. My proposition would be:

  1. if the number of vertices is not equal, the cycles cannot be equal.
  2. it might shorten the rest of the algorithm (but that would yet have to be evaluated!), if you count the number of occurrences for each vertex id, these must match
  3. search the minimum vertex id for each cycle, from this on, compare each subsequent value, wrapping around in the vector, if the end is reached.
  4. if sequences match, your done; this does not yet cover the case that there are multiple minimum values, though; if this happens, you might just repeat the step trying the next minimum value in one cycle, staying with the same in the other. You might try to do the same in parallel with the maxima, or if you have counted them anyway (see above), use of minima/maxima the ones with less elements.

Edit: Further improvement (idea inspired by [Scheff]'s comment to the question):

Instead of re-trying each minimum found, we preferably should select some kind of absolute minimum from the relative minima found so far; a relative minimum x is smaller than a relative minimum y if the successor of x is smaller than the successor of y; if both successors are equal, look at the next successors, and so on. If you discover more than one absolute minimum (if some indirect successor gets equal to the initial minium), then you have a sequence some sub-cycle repeating itself multiple times (1-2-3-1-2-3-1-2-3). Then it does not matter, which "absolute" minimum you select...

You'd definitely skip step 2 above then, though.

Find the minimum already in the constructor and store it. Then comparison gets easy, you just start in both cycles at their respective minimum...

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • 1
    @Scheff See point 4... Still, your comment on the question hinted me to some ideas to get my algorithm more efficient... – Aconcagua Mar 27 '18 at 18:10
  • Thank you for you answer! But won't this be computationally hard? Because, in any way I wiil need to go through all the vector ConjCiclos to be sure if the new cycle already exists or not. – rbl Mar 27 '18 at 19:14
  • @LuizaReal This is why I recommended to calculate the minimum only once in the constructor (in the edit) and store its index, so you only have to iterate once over the vectors during comparison. You could additionally calculate a hash value (in constructor, too) and only compare the vertices if the hash is equal (there are some chances of collisions...). If you keep your vector sorted, you can do a binary search, which further reduces effort (`O(log(n))` instead of `O(n)`). – Aconcagua Mar 27 '18 at 22:18