MESSAGE
DATE | 2017-02-24 |
FROM | Christopher League
|
SUBJECT | Re: [Learn] decision making tree for a euler walk
|
From learn-bounces-at-nylxs.com Fri Feb 24 22:13:30 2017 Return-Path: X-Original-To: archive-at-mrbrklyn.com Delivered-To: archive-at-mrbrklyn.com Received: from www.mrbrklyn.com (www.mrbrklyn.com [96.57.23.82]) by mrbrklyn.com (Postfix) with ESMTP id ACCD7161317; Fri, 24 Feb 2017 22:13:29 -0500 (EST) X-Original-To: learn-at-nylxs.com Delivered-To: learn-at-nylxs.com Received: from contrapunctus.net (contrapunctus.net [174.136.110.10]) by mrbrklyn.com (Postfix) with ESMTP id 9EE4E161315 for ; Fri, 24 Feb 2017 22:13:25 -0500 (EST) Received: from localhost (pool-98-113-34-169.nycmny.fios.verizon.net [98.113.34.169]) by contrapunctus.net (Postfix) with ESMTPSA id 2DE8921EAE; Fri, 24 Feb 2017 22:13:24 -0500 (EST) From: Christopher League To: Ruben Safir , "learn\-at-nylxs.com" In-Reply-To: <319ecec7-f60a-a905-c6a4-9749c89b9404-at-mrbrklyn.com> References: <319ecec7-f60a-a905-c6a4-9749c89b9404-at-mrbrklyn.com> User-Agent: Notmuch/0.22 (http://notmuchmail.org) Emacs/25.1.1 (x86_64-unknown-linux-gnu) Date: Fri, 24 Feb 2017 22:13:22 -0500 Message-ID: <87zihbx8rx.fsf-at-contrapunctus.net> MIME-Version: 1.0 Subject: Re: [Learn] decision making tree for a euler walk X-BeenThere: learn-at-nylxs.com X-Mailman-Version: 2.1.17 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Type: multipart/mixed; boundary="===============0065342184==" Errors-To: learn-bounces-at-nylxs.com Sender: "Learn"
--===============0065342184== Content-Type: multipart/alternative; boundary="=-=-="
--=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable
Here's my Euler calculation, in my weird FP-influenced C++ style (although it definitely uses mutable state). IMO it's usually a mistake to make bunches of classes with elaborately interconnected pointers -- if the data structure can be represented in terms of maps and sets, then just use maps and sets. Object-oriented programming is mostly a bankrupt paradigm.
The referenced `assertions.hh` and `debug-log.hh` are available here:
Enjoy.
CL
~~~~ {.cpp} #include #include #include #include #include #include "assertions.hh" #define LOG_LEVEL LOG_TRACE #include "debug-log.hh" using namespace std;
// Represent an undirected graph using hash maps and sets. The vertex // type can be anything that supports equality and hashing. template struct graph { void add_edge(vertex v1, vertex v2); void remove_edge(vertex v1, vertex v2); int degree(vertex v); vector euler_path(); ostream& show(ostream&); private: typedef unordered_multiset neighbors; typedef unordered_map> adjancency_map; adjancency_map adjacency_map; void add_directed(vertex v1, vertex v2); void remove_directed(vertex v1, vertex v2); };
// Print the graph's adjacency list template ostream& graph::show(ostream& out) { for(typename adjancency_map::iterator i =3D adjacency_map.begin(); i !=3D adjacency_map.end(); i++) { out << i->first << ':'; for(typename neighbors::iterator j =3D i->second.begin(); j !=3D i->second.end(); j++) { out << ' ' << *j; } out << '\n'; } return out; }
template ostream& operator << (ostream& out, graph& g) { return g.show(out); }
// Add an undirected edge. template void graph::add_edge(vertex v1, vertex v2) { add_directed(v1, v2); add_directed(v2, v1); }
// Add directed edge (private). template void graph::add_directed(vertex v1, vertex v2) { typename adjancency_map::iterator i =3D adjacency_map.find(v1); if( adjacency_map.end() =3D=3D i) { adjacency_map[v1] =3D neighbors{v2}; } else { i->second.insert(v2); } }
// Remove undirected edge. template void graph::remove_edge(vertex v1, vertex v2) { remove_directed(v1, v2); remove_directed(v2, v1); }
// Remove directed edge (private). A little tricky because just doing // .erase(v2) removes *all* the matching vertices from the multiset. // Using find then .erase in the iterator solves that. template void graph::remove_directed(vertex v1, vertex v2) { typename neighbors::iterator i =3D adjacency_map.at(v1).find(v2); adjacency_map.at(v1).erase(i); }
// Return the degree of given vertex. template int graph::degree(vertex v) { return adjacency_map.at(v).size(); }
// Calculate and return an euler path. Algorithm basically taken from // here: http://www.graph-magics.com/articles/euler.php // // NOTE: this is destructive -- it removes edges from the graph. So if // you need the graph afterwards, make a copy. template vector graph::euler_path() { // If all vertices have even degree, choose any of them. typename adjancency_map::iterator i =3D adjacency_map.begin(); vertex curr =3D i->first; // If there are exactly 2 vertices having an odd degree: choose one // of them. This will be the current vertex. unsigned num_odd =3D 0; for( ; i !=3D adjacency_map.end(); i++) { if(degree(i->first) % 2 =3D=3D 1) { num_odd++; curr =3D i->first; } } log(LOG_INFO, "There were " << num_odd << " odd-degree vertices."); vector path; stack stack; if(num_odd !=3D 2 && num_odd !=3D 0) { log(LOG_ERROR, "Sorry, no Euler path exists."); return path; } log(LOG_DEBUG, "Starting at " << curr << '\n' << *this); // Repeat until the current vertex has no more neighbors and the // stack is empty. while(degree(curr) > 0 || stack.size() > 0) { // If current vertex has no neighbors, if(degree(curr) =3D=3D 0) { log(LOG_DEBUG, "* Adding " << curr << " to path."); // Add it to path path.push_back(curr); // Remove the last vertex from the stack and set it as the // current one. curr =3D stack.top(); stack.pop(); log(LOG_DEBUG, " New curr is " << curr); } else { // Add the vertex to the stack log(LOG_DEBUG, "* Pushing " << curr << " to stack."); stack.push(curr); // Take any of its neighbors, remove the edge between selected // neighbor and that vertex, and set that neighbor as the // current vertex typename neighbors::iterator n =3D adjacency_map.at(curr).begin(); log(LOG_DEBUG, " Removing " << curr << " <-> " << *n); remove_edge(curr, *n); curr =3D *n; log(LOG_TRACE, *this); } } path.push_back(curr); return path; }
// Grr why isn't this just in the standard library I have to write it // every damn time. template ostream& operator << (ostream& out, const vector& vec) { for(unsigned i =3D 0; i < vec.size(); i++) { if(i > 0) { out << ", "; } out << vec.at(i); } return out; }
graph rubensburg_demo() { // Set up graph graph g; g.add_edge('A','B'); g.add_edge('A','B'); g.add_edge('A','E'); g.add_edge('A','F'); g.add_edge('A','G'); g.add_edge('B','E'); g.add_edge('B','F'); g.add_edge('C','D'); g.add_edge('E','C'); g.add_edge('E','G');
assert_eq(5, g.degree('A')); assert_eq(2, g.degree('G')); assert_eq(1, g.degree('D'));
return g; }
graph even_degree_demo() { graph g; g.add_edge(13, 16); g.add_edge(13, 18); g.add_edge(16, 18); assert_eq(2, g.degree(13)); assert_eq(2, g.degree(16)); assert_eq(2, g.degree(18)); return g; }
// This is the actual K=C3=B6nigsberg graph, for which no Euler path // exists. graph impossible_demo() { graph g; g.add_edge("austin", "baltimore"); g.add_edge("austin", "baltimore"); g.add_edge("austin", "dallas"); g.add_edge("baltimore", "chicago"); g.add_edge("baltimore", "chicago"); g.add_edge("baltimore", "dallas"); g.add_edge("chicago", "dallas"); return g; }
int main() { cout << "=3D=3D=3D=3D=3D=3D=3D Even-degree test\n"; graph g0 =3D even_degree_demo(); cout << g0.euler_path() << '\n';
cout << "=3D=3D=3D=3D=3D=3D=3D Odd-degree test\n"; graph g1 =3D rubensburg_demo(); cout << g1.euler_path() << '\n';
cout << "=3D=3D=3D=3D=3D=3D=3D Impossible test\n"; graph g2 =3D impossible_demo(); cout << g2.euler_path() << '\n';
return 0; } ~~~~
--=-=-= Content-Type: text/html; charset=utf-8 Content-Transfer-Encoding: quoted-printable
1.0, user-scalable=3Dyes">
Here=E2=80=99s my Euler calculation, in my weird FP-influenced C++ style= (although it definitely uses mutable state). IMO it=E2=80=99s usually a mi= stake to make bunches of classes with elaborately interconnected pointers = =E2=80=93 if the data structure can be represented in terms of maps and set= s, then just use maps and sets. Object-oriented programming is mostly a ban= krupt paradigm.
The referenced assertions.hh and debug-log.hh = are available here: master/include" class=3D"uri">https://gitlab.liu.edu/cs691s17/public/tree/m= aster/include
Enjoy.
CL
ceCode cpp">#include <iostr= eam> #include <unordered_map>=
#include <unordered_set>=
#include <vector> #include <stack> #include "assertions.hh&q= uot; #define LOG_LEVEL LOG_TRACE #include "debug-log.hh&qu= ot; using namespace std;
// Represent an undirected graph using hash maps and set= s. The vertex // type can be anything that supports equality and hashi= ng. template<typename ve= rtex> struct graph { void add_edge(vertex v1, vertex v2); void remove_edge(vertex v1, vertex v2); int degree(vertex v); vector<vertex> euler_path(); ostream& show(ostream&); private: typedef unordered_multiset<vertex> neighb= ors; typedef unordered_map<vertex, unordered_mult= iset<vertex>> adjancency_map; adjancency_map adjacency_map; void add_directed(vertex v1, vertex v2); void remove_directed(vertex v1, vertex v2); };
// Print the graph's adjacency list template<typename ve= rtex> ostream& graph<vertex>::show(ostream& out) { for(typename adjancen= cy_map::iterator i =3D adjacency_map.begin(); i !=3D adjacency_map.end(); i++) { out << i->first << ':'n>; for(typename neig= hbors::iterator j =3D i->second.begin(); j !=3D i->second.end(); j++) { out << ' ' << *j; } out << '\nan>'; } return out; }
template<typename ve= rtex> ostream& operator << (ostream& out,= graph<vertex>& g) { return g.show(out); }
// Add an undirected edge. template<typename ve= rtex> void graph<vertex>::add_edge(vertex v1, ver= tex v2) { add_directed(v1, v2); add_directed(v2, v1); }
// Add directed edge (private). template<typename ve= rtex> void graph<vertex>::add_directed(vertex v1,= vertex v2) { typename adjancency_map::iterator i =3D adjacen= cy_map.find(v1); if( adjacency_map.end() =3D=3D i) { adjacency_map[v1] =3D neighbors{v2}; } else { i->second.insert(v2); } }
// Remove undirected edge. template<typename ve= rtex> void graph<vertex>::remove_edge(vertex v1, = vertex v2) { remove_directed(v1, v2); remove_directed(v2, v1); }
// Remove directed edge (private). A little tricky becau= se just doing // .erase(v2) removes *all* the matching vertices from t= he multiset. // Using find then .erase in the iterator solves that.= span> template<typename ve= rtex> void graph<vertex>::remove_directed(vertex = v1, vertex v2) { typename neighbors::iterator i =3D adjacency_ma= p.at(v1).find(v2); adjacency_map.at(v1).erase(i); }
// Return the degree of given vertex. template<typename ve= rtex> int graph<vertex>::degree(vertex v) { return adjacency_map.at(v).size(); }
// Calculate and return an euler path. Algorithm basical= ly taken from // here: http://www.graph-magics.com/articles/euler.php<= /span> // // NOTE: this is destructive -- it removes edges from th= e graph. So if // you need the graph afterwards, make a copy. template<typename ve= rtex> vector<vertex> graph<vertex>::euler_path() { // If all vertices have even degree, choose any of the= m. typename adjancency_map::iterator i =3D adjacen= cy_map.begin(); vertex curr =3D i->first; // If there are exactly 2 vertices having an odd degre= e: choose one // of them. This will be the current vertex. unsigned num_odd =3D 0>; for( ; i !=3D adjacency_map.end(); i++) { if(degree(i->first) % 2= =3D=3D 1) { num_odd++; curr =3D i->first; } } log(LOG_INFO, "There were " << = num_odd << " odd-degree vertices.">); vector<vertex> path; stack<vertex> stack; if(num_odd !=3D 2 &am= p;& num_odd !=3D 0) { log(LOG_ERROR, "Sorry, no Euler path exists.&qu= ot;); return path; } log(LOG_DEBUG, "Starting at " <<= ; curr << '\n= ' << *this); // Repeat until the current vertex has no more neighbo= rs and the // stack is empty. while(degree(curr) > 0pan> || stack.size() > 0) { // If current vertex has no neighbors, if(degree(curr) =3D=3D 0= span>) { log(LOG_DEBUG, "* Adding " <&l= t; curr << " to path."); // Add it to path path.push_back(curr); // Remove the last vertex from the stack and set i= t as the // current one. curr =3D stack.top(); stack.pop(); log(LOG_DEBUG, " New curr is " &= lt;< curr); } else { // Add the vertex to the stack log(LOG_DEBUG, "* Pushing " <&= lt; curr << " to stack."); stack.push(curr); // Take any of its neighbors, remove the edge betw= een selected // neighbor and that vertex, and set that neighbor= as the // current vertex typename neighbors::iterator n =3D adjacenc= y_map.at(curr).begin(); log(LOG_DEBUG, " Removing " <= < curr << " <-> " <&l= t; *n); remove_edge(curr, *n); curr =3D *n; log(LOG_TRACE, *this); } } path.push_back(curr); return path; }
// Grr why isn't this just in the standard library I= have to write it // every damn time. template<typename T&= gt; ostream& operator << (ostream& out,= const vector<T>& vec) { for(unsigned i =3D pan class=3D"dv">0; i < vec.size(); i++) { if(i > 0) { out << ", "; } out << vec.at(i); } return out; }
graph<char> rubensburg_demo() { // Set up graph graph<char> g; g.add_edge('A','= B'); g.add_edge('A','= B'); g.add_edge('A','= E'); g.add_edge('A','= F'); g.add_edge('A','= G'); g.add_edge('B','= E'); g.add_edge('B','= F'); g.add_edge('C','= D'); g.add_edge('E','= C'); g.add_edge('E','= G');
assert_eq(5, g.degree('A= ')); assert_eq(2, g.degree('G= ')); assert_eq(1, g.degree('D= '));
return g; }
graph<int> even_degree_demo() { graph<int> g; g.add_edge(13, 16); g.add_edge(13, 18); g.add_edge(16, 18); assert_eq(2, g.degree(13an>)); assert_eq(2, g.degree(16an>)); assert_eq(2, g.degree(18an>)); return g; }
// This is the actual K=C3=B6nigsberg graph, for which n= o Euler path // exists. graph<string> impossible_demo() { graph<string> g; g.add_edge("austin", t">"baltimore"); g.add_edge("austin", t">"baltimore"); g.add_edge("austin", t">"dallas"); g.add_edge("baltimore", =3D"st">"chicago"); g.add_edge("baltimore", =3D"st">"chicago"); g.add_edge("baltimore", =3D"st">"dallas"); g.add_edge("chicago", st">"dallas"); return g; }
int main() { cout << "=3D=3D=3D=3D=3D=3D=3D Even-degree = test\n"; graph<int> g0 =3D even_degree_demo(); cout << g0.euler_path() << 'an class=3D"sc">\n';
cout << "=3D=3D=3D=3D=3D=3D=3D Odd-degree t= est\n"; graph<char> g1 =3D rubensburg_demo(); cout << g1.euler_path() << 'an class=3D"sc">\n';
cout << "=3D=3D=3D=3D=3D=3D=3D Impossible t= est\n"; graph<string> g2 =3D impossible_demo(); cout << g2.euler_path() << 'an class=3D"sc">\n';
return 0; }
--=-=-=--
--===============0065342184== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline
_______________________________________________ Learn mailing list Learn-at-nylxs.com http://lists.mrbrklyn.com/mailman/listinfo/learn
--===============0065342184==--
|
|