• No results found

LetSbe a set andC:=S×S the set of all ordered pairs (x, y) withx, y∈S. Abinary relationR onS is a subset ofC. Anequivalence relation is a binary relation with the following properties:

• reflexive: x≡x∀x.

• symmetric: x≡y ⇐⇒ y≡x∀x, y.

• transitive: x≡y, y≡z =⇒ x≡z ∀x, y, z. Here we wrotex≡y for (x, y)∈Rwhere x, y∈S.

We want to determine theequivalence classes: an equivalence relation partitions a set into 1≤q ≤n subsetsE1, E2, . . . , Eq so thatx≡y whenever bothxandy are in the same subset but x6≡y ifxand

y are in different subsets.

For example, the usual equality relation is an equivalence relation, with a set of (different) numbers each number is in its own class. With the equivalence relation thatx≡ywheneverx−yis a multiple of some fixed integer m >0 and the set Zof all natural numbers we obtain msubsets and x≡y if and only if x≡ymodm.

3.5.1

Algorithm for decomposition into equivalence classes

LetS be a set ofnelements, represented as a vector. On termination of the following algorithmQk =j

ifj is the least index such thatSj ≡Sk (note that we consider the elements of S to be in a fixed but

arbitrary order here):

1. Put each element in its own equivalence class: Qk :=kfor all 0≤k < n

3.5: Determination of equivalence classes 149 3. (Search for an equivalent element:)

(a) Setj:= 0.

(b) IfSk ≡Sj setQk =Qj and goto step 4.

(c) Set j:=j+ 1 and goto step 3b

4. Setk:=k+ 1 and ifk < ngoto step 3, else terminate.

The algorithm needs n−1 equivalence tests when all elements are in the same equivalence class and n(n−1)/2 equivalence tests when each element is alone in its own equivalence class.

In the following implementation the equivalence relation must be supplied as a functionequiv_q()that returns true when its arguments are equivalent [FXT: sort/equivclasses.h]:

1 template <typename Type>

2 void equivalence_classes(const Type *s, ulong n, bool (*equiv_q)(Type,Type), ulong *q)

3 // Given an equivalence relation ’==’ (as function equiv_q())

4 // and a set s[] with n elements,

5 // write to q[k] the index j of the first element s[j] such that s[k]==s[j].

6 {

7 for (ulong k=0; k<n; ++k) q[k] = k; // each in own class

8 for (ulong k=1; k<n; ++k) 9 { 10 ulong j = 0; 11 while ( ! equiv_q(s[j], s[k]) ) ++j; 12 q[k] = q[j]; 13 } 14 }

3.5.2

Examples of equivalence classes

3.5.2.1 Integers modulo m

Choose an integerm ≥1 and let any two integers a andb be equivalent if a−b is an integer multiple of m (with m = 1 all integers are in the same class). We can choose the numbers 0,1 . . . , m−1 as representatives of the m classes obtained. Now we can do computations with those classes via the modular arithmetic as described in section 39.1 on page 764. This is easily the most important example of all equivalence relations.

The concept also make sense for a real (non-integral) modulusm >0. We still put two numbersaand b into the same class if a−b is an integer multiple of m. Finally, the modulus m = 0 leads to the equivalence relation ‘equality’.

3.5.2.2 Binary necklaces

Consider the set S of n-bit binary words with the equivalence relation in which two wordsxand y are equivalent if and only if there is a cyclic shifthk(x) by 0≤k < n positions such that hk(x) =y. The

equivalence relation is supplied as the function [FXT: sort/equivclass-necklaces-demo.cc]:

1 static ulong nb; // number of bits

2 bool n_equiv_q(ulong x, ulong y) // necklaces

3 {

4 ulong d = bit_cyclic_dist(x, y, nb);

5 return (0==d);

6 }

The function bit_cyclic_dist()is given in section 1.13.4 on page 32. Forn= 4 we find the following list of equivalence classes:

0: .... [#=1] 1: 1... .1.. ...1 ..1. [#=4] 3: 1..1 11.. ..11 .11. [#=4] 5: .1.1 1.1. [#=2] 7: 11.1 111. 1.11 .111 [#=4] 15: 1111 [#=1] # of equivalence classes = 6

These correspond to thebinary necklacesof length 4. One usually chooses the cyclic minima (or maxima) among equivalent words as representatives of the classes.

3.5.2.3 Unlabeled binary necklaces

Same set but the equivalence relation is defined to identify two wordsxandywhen there is a cyclic shift hk(x) by 0≤k < npositions so that eitherhk(x) =y or hk(x) =ywhere yis the complement of y:

1 static ulong mm; // mask to complement

2 bool nu_equiv_q(ulong x, ulong y) // unlabeled necklaces

3 { 4 ulong d = bit_cyclic_dist(x, y, nb); 5 if ( 0!=d ) d = bit_cyclic_dist(mm^x, y, nb); 6 return (0==d); 7 } Withn= 4 we find 0: 1111 .... [#=2] 1: 111. 11.1 1.11 1... .111 ...1 ..1. .1.. [#=8] 3: .11. 1..1 11.. ..11 [#=4] 5: .1.1 1.1. [#=2] # of equivalence classes = 4

These correspond to theunlabeled binary necklaces of length 4.

3.5.2.4 Binary bracelets

Thebinary bracelets are obtained by identifying two words that are identical up to rotation and possible reversal. The corresponding comparison function is

1 bool b_equiv_q(ulong x, ulong y) // bracelets

2 {

3 ulong d = bit_cyclic_dist(x, y, b);

4 if ( 0!=d ) d = bit_cyclic_dist(revbin(x,b), y, b);

5 return (0==d);

6 }

There are six binary bracelets of length 4:

0: .... [#=1] 1: 1... .1.. ...1 ..1. [#=4] 3: 1..1 11.. ..11 .11. [#=4] 5: .1.1 1.1. [#=2] 7: 11.1 111. 1.11 .111 [#=4] 15: 1111 [#=1]

Theunlabeled binary bracelets are obtained by additionally allowing for bit-wise complementation:

1 bool bu_equiv_q(ulong x, ulong y) // unlabeled bracelets

2 { 3 ulong d = bit_cyclic_dist(x, y, b); 4 x ^= mm; 5 if ( 0!=d ) d = bit_cyclic_dist(x, y, b); 6 7 x = revbin(x,b); 8 if ( 0!=d ) d = bit_cyclic_dist(x, y, b); 9 x ^= mm; 10 if ( 0!=d ) d = bit_cyclic_dist(x, y, b); 11 12 return (0==d); 13 }

There are four unlabeled binary bracelets of length 4:

0: 1111 .... [#=2]

1: 111. 11.1 1.11 1... .111 ...1 ..1. .1.. [#=8] 3: .11. 1..1 11.. ..11 [#=4]

5: .1.1 1.1. [#=2]

The shown functions are given in [FXT: sort/equivclass-bracelets-demo.cc] which can be used to produce listings of the equivalence classes.

3.5: Determination of equivalence classes 151

n: N B N/U B/U

[312]# A000031 A000029 A000013 A000011

1: 2 2 1 1 2: 3 3 2 2 3: 4 4 2 2 4: 6 6 4 4 5: 8 8 4 4 6: 14 13 8 8 7: 20 18 10 9 8: 36 30 20 18 9: 60 46 30 23 10: 108 78 56 44 11: 188 126 94 63 12: 352 224 180 122 13: 632 380 316 190 14: 1182 687 596 362 15: 2192 1224 1096 612

Figure 3.5-A:The number of binary necklaces ‘N’, bracelets ‘B’, unlabeled necklaces ‘N/U’, and unlabeled bracelets ‘B/U’. The second row gives the sequence number in [312].

3.5.2.5 Binary words with reversal and complement

The set S of n-bit binary words and the equivalence relation identifying two words x and y whenever they are mutual complements or bit-wise reversals.

3 classes with 3-bit words: 10 classes with 5-bit words:

0: 111 ... 0: 11111 ...

1: ..1 .11 1.. 11. 1: 1111. 1.... .1111 ....1

2: 1.1 .1. 2: 1.111 111.1 .1... ...1.

3: 111.. ...11 ..111 11... 4: ..1.. 11.11

6 classes with 4-bit words: 5: 11.1. 1.1.. ..1.1 .1.11

0: 1111 .... 6: ..11. .11.. 11..1 1..11 1: 111. 1... .111 ...1 9: .11.1 1.11. .1..1 1..1. 2: ..1. .1.. 1.11 11.1 10: .1.1. 1.1.1 3: 11.. ..11 14: 1...1 .111. 5: 1.1. .1.1 6: .11. 1..1

Figure 3.5-B:Equivalence classes of binary words where words are identified if either their reversals or complements are equal.

For example, the equivalence classes with 3-, 4- and 5-bit words are shown in figure 3.5-B. The sequence of numbers of equivalence classes for word-sizesnis (entry A005418 in [312])

n: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ... #: 1, 2, 3, 6, 10, 20, 36, 72, 136, 272, 528, 1056, 2080, 4160, 8256, 16512, ...

The equivalence classes can be computed with the program [FXT: sort/equivclass-bitstring-demo.cc]. We have chosen examples where the resulting equivalence classes can be verified by inspection. For example, we could create the subsets of equivalent necklaces by simply rotating a given word and marking the words visited so far. Such an approach, however, is not possible if the equivalence relation does not have an obvious structure.

3.5.3

The number of equivalence relations for a set of

n

elements

We write B(n) for the number of possible partitionings (and thereby equivalence relations) of the set {1,2, . . . , n}. These are calledBell numbers. The sequence of Bell numbers is entry A000110 in [312], it starts as (n≥1):

1, 2, 5, 15, 52, 203, 877, 4140, 21147, 115975, 678570, 4213597, ...

The can be computed easily as indicated in the following table:

0: [ 1] 1: [ 1, 2] 2: [ 2, 3, 5] 3: [ 5, 7, 10, 15] 4: [15, 20, 27, 37, 52] 5: [52, 67, 87, 114, 151, 203] n: [B(n), ... ]

The first element in each row is the last element of the previous row, the remaining elements are the sum of their left and upper left neighbors. As GP code:

1 N=7; v=w=b=vector(N); v[1]=1;

2 { for(n=1,N-1,

3 b[n] = v[1];

4 print(n-1, ": ", v); \\ print row

5 w[1] = v[n];

6 for(k=2,n+1, w[k]=w[k-1]+v[k-1]);

7 v=w;

8 ); }

An implementation in C++ is given in [FXT: comb/bell-number-demo.cc]. An alternative way to compute the Bell numbers is shown in section 17.2 on page 358.

153

Chapter 4

Data structures

We give implementations of selected data structures like stack, ring buffer, queue, double-ended queue (deque), bit-array, heap and priority queue.

4.1

Stack (LIFO)

push( 1) 1 - - - #=1 push( 2) 1 2 - - #=2 push( 3) 1 2 3 - #=3 push( 4) 1 2 3 4 #=4 push( 5) 1 2 3 4 5 - - - #=5 push( 6) 1 2 3 4 5 6 - - #=6 push( 7) 1 2 3 4 5 6 7 - #=7 pop== 7 1 2 3 4 5 6 - - #=6 pop== 6 1 2 3 4 5 - - - #=5 push( 8) 1 2 3 4 5 8 - - #=6 pop== 8 1 2 3 4 5 - - - #=5 pop== 5 1 2 3 4 - - - - #=4 push( 9) 1 2 3 4 9 - - - #=5 pop== 9 1 2 3 4 - - - - #=4 pop== 4 1 2 3 - - - #=3 push(10) 1 2 3 10 - - - - #=4 pop==10 1 2 3 - - - #=3 pop== 3 1 2 - - - #=2 push(11) 1 2 11 - - - #=3 pop==11 1 2 - - - #=2 pop== 2 1 - - - #=1 push(12) 1 12 - - - #=2 pop==12 1 - - - #=1 pop== 1 - - - #=0 push(13) 13 - - - #=1 pop==13 - - - #=0 pop== 0 - - - #=0

(stack was empty)

push(14) 14 - - - #=1 pop==14 - - - #=0 pop== 0 - - - #=0

(stack was empty)

push(15) 15 - - - #=1

Figure 4.1-A:Inserting and retrieving elements with a stack.

A stack (or LIFO, for last-in, first-out) is a data structure that supports the operations: push() to save an entry, pop() to retrieve and remove the entry that was entered last, and peek() to retrieve the element that was entered last without removing it. The methodpoke() modifies the last entry. An implementation with the option to let the stack grow when necessary is [FXT:class stackin ds/stack.h]:

1 template <typename Type>

2 class stack

3 {

4 public:

5 Type *x_; // data

7 ulong p_; // stack pointer (position of next write), top entry @ p-1

8 ulong gq_; // grow gq elements if necessary, 0 for "never grow"

9

10 public:

11 stack(ulong n, ulong growq=0)

12 { 13 s_ = n; 14 x_ = new Type[s_]; 15 p_ = 0; // stack is empty 16 gq_ = growq; 17 } 18 19 ~stack() { delete [] x_; } 20

21 ulong num() const { return p_; } // Return number of entries.

Insertion and retrieval from the top of the stack are implemented as follows:

1 ulong push(Type z)

2 // Add element z on top of stack.

3 // Return size of stack, zero on stack overflow.

4 // If gq_ is nonzero the stack grows if needed.

5 { 6 if ( p_ >= s_ ) 7 { 8 if ( 0==gq_ ) return 0; // overflow 9 grow(); 10 } 11 12 x_[p_] = z; 13 ++p_; 14 15 return s_; 16 } 17

18 ulong pop(Type &z)

19 // Retrieve top entry and remove it.

20 // Return number of entries before removing element.

21 // If empty return zero and leave z is undefined.

22 { 23 ulong ret = p_; 24 if ( 0!=p_ ) { --p_; z = x_[p_]; } 25 return ret; 26 } 27 28 ulong poke(Type z)

29 // Modify top entry.

30 // Return number of entries.

31 // If empty return zero and do nothing.

32 {

33 if ( 0!=p_ ) x_[p_-1] = z;

34 return p_;

35 }

36

37 ulong peek(Type &z)

38 // Read top entry, without removing it.

39 // Return number of entries.

40 // If empty return zero and leave z undefined.

41 {

42 if ( 0!=p_ ) z = x_[p_-1];

43 return p_;

44 }

The growth routine is implemented as

1 private:

2 void grow()

3 {

4 ulong ns = s_ + gq_; // new size

5 x_ = ReAlloc<Type>(x_, ns, s_);

6 s_ = ns;

7 }

8 };

here we use the functionReAlloc()that imports the C functionrealloc().