diffsym.cpp
// $Id$
/* COMIENZO DE DESCRIPCION
__USE_WIKI__
Dada una lista de conjuntos de enteros
#list< set<int> > l# escribir una funci\'on
#void diffsym(list< set<int> > &L, set<int> &ad)#
que retorna en #ad# el conjunto de los elementos que
pertenecen a uno y s\'olo uno de los conjuntos de #L#.
Por ejemplo, si #L = ({1,2,3},{2,4,5},{4,6})# entonces
#ad# debe ser #{1,3,5,6}#. Notar que si el n\'umero
de conjuntos en #l# es 2 y los llamamos #A# y #B#,
entonces debe retornar #ad = (A-B) union (B-A)#.
[Tomado en el 3er parcial 23/6/2005].
keywords: conjunto
FIN DE DESCRIPCION */
#include <time.h>
#include <sys/time.h>
#include <cassert>
#include <cmath>
#include <set>
#include <list>
#include <algorithm>
#include "./util.h"
using namespace std;
void print(const set<int> &s,
const char *t=NULL) {
if (t) printf("%s",t);
set<int>::iterator q = s.begin();
while (q!=s.end()) printf("%d ",*q++);
printf("\n");
}
// -----------------------------------------------------------------
// Obtiene el tiempo en segundos.
// warning: `gettimeofday' is a GNU extension.
double gettod() {
struct timeval tv;
gettimeofday (&tv,0);
return tv.tv_sec + 1e-6 * tv.tv_usec;
}
// Un algoritmo posible es recorrer todos los elementos
// #x# que pertenecen a alg\'un conjunto de #l# y contar
// en cuantos elementos de #l# est\'a (debe estar en al
// menos uno). El elemento es insertado en #ad# s\'olo
// si el conteo da exactamente 1.
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
void diffsym(list< set<int> > &l,set<int> &ad) {
list< set<int> >::iterator q = l.begin();
while (q!=l.end()) {
set<int> &s = *q;
set<int>::iterator r = s.begin();
while (r!=s.end()) {
int count = 0;
list< set<int> >::iterator w = l.begin();
while (w!=l.end()) {
if (w->find(*r) != w->end()) count++;
if (count>=2) break;
w++;
}
if (count==1) ad.insert(*r);
r++;
}
q++;
}
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Otro algoritmo, basado en funciones binarias
// Notar que en el caso de tres conjuntos si:
// S=diffsym(A,B) y
// U= A union B,
// entonces:
// diffsym(A,B,C) = (S-C) \cup (C-U).
// Esto vale en general para cualquier n\'umero de conjuntos, de manera
// que podemos utilizar el siguiente lazo
// L = lista de conjuntos, S=U=<conjunto-vacio>
// FOR Q en la lista de conjuntos de L
// S = (S-Q) union (Q-U)
// U = U union Q
// Al terminar el lazo, S es la diferencia sim\'etrica buscada.
void diffsym2(list< set<int> > &l,
set<int> &all_diff) {
all_diff.clear();
set<int> all_union,s1,s2;
list< set<int> >::iterator
q = l.begin();
while (q!=l.end()) {
s2.clear();
set_difference(all_diff.begin(),all_diff.end(),
q->begin(),q->end(),
inserter(s2,s2.begin()));
all_diff = s2;
set_difference(q->begin(),q->end(),
all_union.begin(),all_union.end(),
inserter(all_diff,all_diff.begin()));
s1.clear();
set_union(q->begin(),q->end(),
all_union.begin(),all_union.end(),
inserter(s1,s1.begin()));
all_union = s1;
q++;
}
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Otra version: Hacemos all_diff = U - UI
// donde U = Union de todos los conjuntos
// donde UI = Union de las intersecciones de todos los
// pares de conjuntos.
void diffsym3(list< set<int> > &l,
set<int> &all_diff) {
set<int> U,UI,tmp1,tmp2;
// Hace U = union de todos los conjuntos
list< set<int> >::iterator q = l.begin(),r;
while (q!=l.end()) {
tmp2.clear();
set_union(U.begin(),U.end(),
q->begin(),q->end(),
inserter(tmp2,tmp2.begin()));
swap(U,tmp2);
q++;
}
// Hace I = Union de todas las intersecciones de todos los
// pares de conjuntos
q = l.begin();
while (q != l.end()) {
// Poniendo r = siguiente de q
// se gana un poco en eficiencia.
// Se puede tambien verificar
r = q;
r++;
tmp1.clear();
while (r!=l.end()) {
set_intersection(q->begin(),q->end(),
r->begin(),r->end(),
inserter(tmp1,tmp1.begin()));
r++;
tmp2.clear();
set_union(UI.begin(),UI.end(),
tmp1.begin(),tmp1.end(),
inserter(tmp2,tmp2.end()));
swap(tmp2,UI);
}
q++;
}
// Hace all_diff = U - UI
all_diff.clear();
set_difference(U.begin(),U.end(),
UI.begin(),UI.end(),
inserter(all_diff,all_diff.begin()));
}
typedef list< set<int> > lset_t;
typedef lset_t::iterator lset_iter;
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
void diffsym4(lset_t &l, set<int> &all_diff, set<int> &all_union) {
all_diff.clear();
all_union.clear();
if (l.size()==0) {
return;
} if (l.size()==1) {
lset_iter q = l.begin();
swap(all_diff,*q);
l.erase(q);
all_union = all_diff;
return;
}
lset_t lodd, leven;
lset_iter q = l.begin();
int indx = 0;
set<int> empty_set,
all_diff_odd, all_union_odd,
all_diff_even, all_union_even,
tmp1,tmp2;
while (q != l.end()) {
lset_t *lp = (indx % 2? &lodd : &leven);
lset_iter r = lp->insert(lp->end(),empty_set);
// printf("indx %d, *r: ",indx); print(*q);
swap(*r,*q);
q = l.erase(q);
indx++;
}
diffsym4(lodd,all_diff_odd,all_union_odd);
diffsym4(leven,all_diff_even,all_union_even);
#if 0
printf("antes del merge--------\n");
printf("all_diff_odd: "); print(all_diff_odd);
printf("all_union_odd: "); print(all_union_odd);
printf("all_diff_even: "); print(all_diff_even);
printf("all_union_even: "); print(all_union_even);
#endif
set_difference(all_diff_odd.begin(),all_diff_odd.end(),
all_union_even.begin(),all_union_even.end(),
inserter(tmp1,tmp1.begin()));
swap(all_diff_odd,tmp1);
tmp1.clear();
set_difference(all_diff_even.begin(),all_diff_even.end(),
all_union_odd.begin(),all_union_odd.end(),
inserter(tmp1,tmp1.begin()));
swap(all_diff_even,tmp1);
tmp1.clear();
all_diff.clear();
set_union(all_diff_even.begin(),all_diff_even.end(),
all_diff_odd.begin(),all_diff_odd.end(),
inserter(all_diff,all_diff.end()));
all_union.clear();
set_union(all_union_even.begin(),all_union_even.end(),
all_union_odd.begin(),all_union_odd.end(),
inserter(all_union,all_union.end()));
#if 0
printf("despues del merge--------\n");
printf("all_diff: "); print(all_diff);
printf("all_union: "); print(all_union);
#endif
while (1) {
lset_t *lp = (indx % 2? &lodd : &leven);
if (lp->empty()) return;
lset_iter r = lp->begin();
q = l.insert(l.end(),empty_set);
swap(*r,*q);
lp->erase(r);
}
}
void diffsym4(lset_t &l, set<int> &all_diff) {
set<int> s;
diffsym4(l,all_diff,s);
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Este es similar al `diffsym4' pero se basa en lo
// siguiente. Si dividimos la lista de conjuntos l en
// dos partes l1 y l2, entonces podemos calcular
// por la diferencia simetrica de l1 all_diff_1
// y la union de todos sus conjuntos all_union_1.
// Lo mismo para l2. Ahora para hacer el `merge' (fusion) de
// los dos queda:
//
// all_diff = (all_diff_1 - all_union_2) \cup (all_diff_2 - all_union_1)
// all_union = all_union_1 \cup all_union_2
//
// Para calcular all_diff_1 aplicamos recursion. La recursion corta
// cuando la lista es vacia (en cuyo caso all_diff = all_union = empty-set)
// o tiene un solo conjunto en cuyo caso all_diff = all_union = s.
//
// La idea es que este algoritmo puede ser mas eficiente
// porque va agrupando los conjuntos en forma balanceada.
void diffsym4b(lset_t &l, set<int> &all_diff,
set<int> &all_union,
lset_iter q1, lset_iter q2, int n) {
all_diff.clear();
all_union.clear();
if (n == 0) {
return;
} if (n==1) {
all_diff = *q1;
all_union = *q1;
return;
}
lset_t l1, l2;
int n1 = n/2, n2 = n-n1;
lset_iter qh = q1;
for (int j=0; j<n1; j++) qh++;
set<int> empty_set,
all_diff_1, all_union_1,
all_diff_2, all_union_2,
tmp1,tmp2;
diffsym4b(l,all_diff_1,all_union_1,q1,qh,n1);
diffsym4b(l,all_diff_2,all_union_2,qh,q2,n2);
#if 0
printf("antes del merge--------\n");
printf("all_diff_1: "); print(all_diff_1);
printf("all_union_1: "); print(all_union_1);
printf("all_diff_2: "); print(all_diff_2);
printf("all_union_2: "); print(all_union_2);
#endif
set_difference(all_diff_1.begin(),all_diff_1.end(),
all_union_2.begin(),all_union_2.end(),
inserter(tmp1,tmp1.begin()));
swap(all_diff_1,tmp1);
tmp1.clear();
set_difference(all_diff_2.begin(),all_diff_2.end(),
all_union_1.begin(),all_union_1.end(),
inserter(tmp1,tmp1.begin()));
swap(all_diff_2,tmp1);
tmp1.clear();
all_diff.clear();
set_union(all_diff_2.begin(),all_diff_2.end(),
all_diff_1.begin(),all_diff_1.end(),
inserter(all_diff,all_diff.end()));
all_union.clear();
set_union(all_union_2.begin(),all_union_2.end(),
all_union_1.begin(),all_union_1.end(),
inserter(all_union,all_union.end()));
#if 0
printf("despues del merge--------\n");
printf("all_diff: "); print(all_diff);
printf("all_union: "); print(all_union);
#endif
}
void diffsym4b(lset_t &l, set<int> &all_diff) {
set<int> s;
diffsym4b(l,all_diff,s,l.begin(),l.end(),l.size());
}
//---:---<*>---:---<*>---:---<*>---:---<*>---:---<*>
// Crea un conjunto aleatorio `s' con
// `m' elements in average in range [0,N)
void make_random_set(set<int> &s,
int m,int N) {
double lambda = 1.0/(m+1); // Probability of stopping
s.clear();
while(1) {
if (drand()<lambda) break;
int w = rand() % N;
s.insert(w);
}
}
// -------------------------------------------------------------------
int main () {
int ncases=10, vrbs = 1;
for (int k=0; k<ncases; k++) {
#if 1
int N = 20; // integers in sets are in range [0,N)
int m = 10; // Nbr of elements in each set
int n = 5; // nbr of sets
#elif 0
int N = 600; // integers in sets are in range [0,N)
int m = 300; // Nbr of elements in each set
int n = 300; // nbr of sets
#elif 0
int ref=10;
int N = 200*ref; // integers in sets are in range [0,N)
int m = 100*ref; // Nbr of elements in each set
int n = 100*ref; // nbr of sets
#endif
list< set<int> > l;
double sav = 0.0;
printf("----------------\n");
for (int k=0; k<n; k++) {
l.push_back(set<int>());
set<int> &s = l.back();
make_random_set(s,m,N);
if (vrbs) {
printf("set[%d]: ",k);
print(s);
}
sav += s.size();
}
printf("average set size %f\n",sav/n);
set<int> S1,S2,S3,S4,S4b;
double start;
start = gettod();
diffsym(l,S1);
printf("diffsym(): elapsed %.2f\n",gettod()-start);
if (vrbs) print(S1,"using diffsym():");
start = gettod();
diffsym2(l,S2);
printf("diffsym2(): elapsed %.2f\n",gettod()-start);
if (vrbs) print(S2,"using diffsym2():");
start = gettod();
diffsym3(l,S3);
printf("diffsym3(): elapsed %.2f\n",gettod()-start);
if (vrbs) print(S3,"using diffsym3():");
start = gettod();
diffsym4(l,S4);
printf("diffsym4(): elapsed %.2f\n",gettod()-start);
if (vrbs) print(S4,"using diffsym4():");
start = gettod();
diffsym4b(l,S4b);
printf("diffsym4b(): elapsed %.2f\n",gettod()-start);
if (vrbs) print(S4b,"using diffsym4b():");
printf("S2==S1 OK? %d\n",S2==S1);
printf("S3==S1 OK? %d\n",S3==S1);
printf("S4==S1 OK? %d\n",S4==S1);
assert(S2==S1);
assert(S3==S1);
assert(S4==S1);
}
}
Generated by GNU Enscript 1.6.6.