1. Trang chủ
  2. » Công Nghệ Thông Tin

Algorithms for programmers phần 7 docx

21 271 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 21
Dung lượng 295,82 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

int is_valid_permutationconst ulong *f, ulong n, bitarray *bp/*=0*/// check whether all values 0...n-1 appear exactly once { // check whether any element is out of range: for ulong k=0;

Trang 1

int is_valid_permutation(const ulong *f, ulong n, bitarray *bp/*=0*/)

// check whether all values 0 n-1 appear exactly once

{

// check whether any element is out of range:

for (ulong k=0; k<n; ++k) if ( f[k]>=n ) return 0;

// check whether values are unique:

One can apply arbitrary many permutations to an array, one by one The resulting permutation is called

the composition of the applied permutations As an example, the check whether some permutation g is equal to f applied twice, or f · f , or f squared use:

int is_square(const ulong *f, const ulong *g, ulong n)

int is_inverse(const ulong *f, const ulong *g, ulong n)

// check whether f[] is inverse of g[]

int is_involution(const ulong *f, ulong n)

// check whether max cycle length is <= 2

{

for (ulong k=0; k<n; ++k) if ( f[f[k]] != k ) return 0;

return 1;

}

Finding the inverse of a given permutation is trivial:

void make_inverse(const ulong *f, ulong * restrict g, ulong n)

// set g[] to the inverse of f[]

Trang 2

ulong print_cycles(const ulong *f, ulong n, bitarray *bp=0)

// print the cycles of the permutation

// return number of fixed points

ulong g = f[i]; // next index

if ( g==i ) // fixed point ?

Trang 3

The bitarray is used to keep track of the elements already processed.

For the computation of the inverse we have to reverse each cycle:

void make_inverse(ulong *f, ulong n, bitarray *bp/*=0*/)

// set f[] to its own inverse

Similarly for the straighforward

void make_square(const ulong *f, ulong * restrict g, ulong n)

// set g[] = f[] * f[]

{

for (ulong k=0; k<n; ++k) g[k] = f[f[k]];

}

whose inplace version is

void make_square(ulong *f, ulong n, bitarray *bp/*=0*/)

ulong t = f[i]; // save

ulong g = f[i]; // next index

Trang 4

if ( 0==bp ) delete tp;

}

Random permutations are sometimes useful:

void random_permute(ulong *f, ulong n)

// randomly permute the elements of f[]

void random_permutation(ulong *f, ulong n)

// create a random permutation of the canonical sequence

{

for (ulong k=0; k<n; ++k) f[k] = k;

random_permute(f, n);

}

8.6.3 Applying permutations to data

The following routines are from [FXT: file perm/permapply.h]

The inplace analogue of the routine apply shown near the beginning of section 8.6 is:

template <typename Type>

void apply(const ulong *x, Type *f, ulong n, bitarray *bp=0)

// apply x[] on f[] (inplace operation)

template <typename Type>

void apply_inverse(const ulong *x, const Type *f, Type * restrict g, ulong n)

// apply inverse of x[] on f[]

Trang 5

// i.e g[x[k]] < f[k] \forall k

{

for (ulong k=0; k<n; ++k) g[x[k]] = f[k];

}

whereas the inplace version is

template <typename Type>

void apply_inverse(const ulong *x, Type * restrict f, ulong n,

bitarray *bp=0)// apply inverse of x[] on f[] (inplace operation)

8.7 Generating all Permutations

In this section a few algorithms for the generation of all permutations are presented These are typicallyuseful in situations where an exhausive search over all permutations is needed At the time of writing

the pre-fascicles of Knuths The Art of Computer Programming Volume 4 are available Therefore (1) the

title of this section is not anymore ‘Enumerating all permutations’ and (2) I won’t even try to elaborate

on the underlying algorithms Consider the reference to the said place be given between any two lines inthe following (sub-)sections

TBD: perm-visit cf [FXT: file perm/permvisit.h]

Trang 6

The sign given is plus or minus if the (minimal) number of transpositions is even or odd, respectively.

The minimalistic class perm_lex implementing the algorithm is

class perm_lex

{

protected:

ulong n; // number of elements to permute

ulong *p; // p[n] contains a permutation of {0, 1, , n-1}

ulong idx; // incremented with each call to next()

ulong sgn; // sign of the permutation

ulong current() const { return idx; }

ulong sign() const { return sgn; } // 0 for sign +1, 1 for sign -1

const ulong *data() const { return p; }

Trang 7

// do something, e.g just print the permutation:

for (ulong i=0; i<n; ++i) cout << x[i] << " ";

class perm_minchange

{

protected:

ulong n; // number of elements to permute

ulong *p; // p[n] contains a permutation of {0, 1, , n-1}

ulong *ip; // ip[n] contains the inverse permutation of p[]

ulong *d; // aux

ulong *ii; // aux

ulong sw1, sw2; // index of elements swapped most recently

ulong idx; // incremented with each call to next()

public:

9 There is more than one minimal change order, e.g reversing the order yields another one.

Trang 8

perm_minchange(ulong nn);

~perm_minchange();

void first();

ulong next() { return make_next(n-1); }

ulong current() const { return idx; }

ulong sign() const { return idx & 1; } // 0 for sign +1, 1 for sign -1

const ulong *data() const { return p; }

const ulong *invdata() const { return ip; }

void get_swap(ulong &s1, ulong &s2) const { s1=sw1; s2=sw2; }

protected:

ulong make_next(ulong m);

};

[FXT: class perm minchange in perm/permminchange.h]

The algorithm itself can be found in [FXT: perm minchange::make next in perm/permminchange.cc]ulong perm_minchange::make_next(ulong m)

const ulong *x = perm.data();

const ulong *ix = perm.invdata();

ulong sw1, sw2;

do

Trang 9

// do something, e.g just print the permutation:

for (ulong i=0; i<n; ++i) cout << x[i] << " ";

// sometimes one only uses the indices swapped

perm.get_swap(sw1, sw2);

cout << " swap: (" << sw1 << ", " << sw2 << ") ";

// inverse permutation courtesy of the algorithm

for (ulong i=0; i<n; ++i) cout << ix[i] << " ";

}

while ( perm.next() );

Cf also [FXT: file demo/permminchange-demo.cc]

An alternative implementation using the algorithm of Trotter (based on code by Helmut Herold) can befound in [FXT: perm trotter::make next in perm/permtrotter.cc]

Trang 10

There is no such sequence for n = 3.

The utility class, that implements the underlying algorithm is [FXT: class perm derange

in perm/permderange.h] The central piece of code is [FXT: perm derange::make next inperm/permderange.cc]:

cout << " #"; cout.width(3); cout << perm.current() << ": ";

for (ulong i=0; i<n; ++i) cout << x[i] << " ";

Trang 11

cout << " #"; cout.width(3); cout << ct << ": ";

for (ulong i=0; i<n; ++i) cout << x[i] << " ";

cout << " swap: (" << 0 << ", " << perm.get_swap() << ") ";

8.7.5 Yet another order

to enumerate all permutations of n elements was given in [32]:

Trang 12

as not yet visited (−1) or to contain at which point in the path (0 for starting point n − 1 for end

point) it was visited A recursive implementation looks like

cout << " #"; cout.width(3); cout << perm.current() << ": ";

for (ulong i=0; i<n; ++i) cout << x[i] << " ";

cout << endl;

}

while ( perm.next() );

Trang 13

Sorting and searching

template <typename Type>

void selection_sort(Type *f, ulong n)

A verification routine is always handy:

template <typename Type>

int is_sorted(const Type *f, ulong n)

While the quicksort-algorithm presented below scales ∼ n log(n) (in the average case) it does not just

obsolete the more simple schemes because (1) for arrays small enough the ‘simple’ algorithm is usually

140

Trang 14

the fastest method because of its minimal bookkeeping overhead and (2) therefore it is used inside thequicksort for lengths below some threshold.

The main ingredient of quicksort is to partition the array: The corresponding routine reorders some ments where needed and returns some partition index k so that max(f0, , f k−1 ) ≤ min(f k , , f n−1):

ele-template <typename Type>

ulong partition(Type *f, ulong n)

// rearrange array, so that for some index p

which we want to be able to verify:

template <typename Type>

Type inline min(const Type *f, ulong n)

// returns minimum of array

template <typename Type>

inline Type max(const Type *f, ulong n)

// returns maximum of array

template <typename Type>

int is_partitioned(const Type *f, ulong n, ulong k)

{

++k;

Type lmax = max(f, k);

Type rmin = min(f+k, n-k);

return ( lmax<=rmin );

}

Quicksort calls partition on the whole array, then on the parts left and right from the partition indexand repeat When the size of the subproblems is smaller than a certain threshold selection sort is used.template <typename Type>

void quick_sort(Type *f, ulong n)

Trang 15

The reason why some data was sorted may be that a fast search has to be performed repeatedly The

following bsearch is ∼ log(n) and works by the obvious subdivision of the data:

template <typename Type>

ulong bsearch(const Type *f, ulong n, Type v)

// return index of first element in f[] that is == v

// return ~0 if there is no such element

// f[] must be sorted in ascending order

{

ulong nlo=0, nhi=n-1;

while ( nlo != nhi )

if ( f[nhi]==v ) return nhi;

else return ~0UL;

}

A simple modification of bsearch makes it search the first element greater than v: Replace the operator

== in the above code by >= and you have it [FXT: bsearch ge in sort/search.h]

Approximate matches are found by

template <typename Type>

ulong bsearch_approx(const Type *f, ulong n, Type v, Type da)

// return index of first element x in f[] for which |(x-v)| <= a

// return ~0 if there is no such element

// f[] must be sorted in ascending order

Trang 16

template <typename Type>

inline long search_down(const Type *f, Type v, ulong &i)

// search v in f[], starting at i (so i must be < length)

// f[i] must be greater or equal v

// f[] must be sorted in ascending order

// returns index k if f[k]==v or ~0 if no such k is found

// i is updated so that it can be used for a following

// search for an element u where u < v

template <typename Type>

void idx_selection_sort(const Type *f, ulong n, ulong *x)

The verification code looks like:

template <typename Type>

int is_idx_sorted(const Type *f, ulong n, const ulong *x)

The index-sort routines reorder the indices in x such that x applied to f as a permutation (in the sense

of section 8.6.3) will render f a sorted array

While the transformation of partition is straight forward:

template <typename Type>

ulong idx_partition(const Type *f, ulong n, ulong *x)

// rearrange index array, so that for some index p

// max(f[x[0]] f[x[p]]) <= min(f[x[p+1]] f[x[n-1]])

Trang 17

The index-quicksort itself deserves a minute of contemplation comparing it to the plain version:

template <typename Type>

void idx_quick_sort(const Type *f, ulong n, ulong *x)

Thereby pointer sort is the perfect way to highly cryptic and powerful programs that segfault when youleast expect it Admittedly, all the ‘dangerous’ features of pointer sort except the unaligned one are also

there in index sort However, with index sort you will not so often use them by accident.

Just to make the idea clear, the array of indices is replaced by an array of pointers:

template <typename Type>

void ptr_selection_sort(const Type *f, ulong n, Type **x)

{

for (ulong i=0; i<n; ++i)

{

Type v = *x[i];

Trang 18

ulong m = i; // position-ptr of minimum

9.5 Sorting by a supplied comparison function

The routines in [FXT: file sort/sortfunc.h] are similar to the C-quicksort qsort that is part of thestandard library A comparison function cmp has to be supplied by the called so that compound datatypes can be sorted with respect to some key contained Citing the manual page for qsort:

The comparison function must return an integer less than, equal to, or greater than

zero if the first argument is considered to be respectively less than, equal to, or

greater than the second If two members compare as equal, their order in the

sorted array is undefined

Note that the numerous calls to cmp do have a negative impact on the performance And then with C++you can provide a comparision ‘function’ for compound data by overloading the operators <, <, <= and >=

and use the plain version Back in performance land Isn’t C++ nice? TBD: add a compile-time inlined version?

As a prototypical example here the version of selection sort:

template <typename Type>

void selection_sort(Type *f, ulong n, int (*cmp)(const Type &, const Type &))

template <typename Type>

void quick_sort(Type *f, ulong n, int (*cmp)(const Type &, const Type &))

Trang 19

Sorting complex numbers

You want to sort complex numbers? Fine for me, but don’t tell your local mathematician To see the mathematical problem we ask whether i is smaller or greater than zero Assume i > 0: follows i · i > 0 (we multiplied with a positive value) which is −1 > 0 and that is false So, is i < 0? Then i · i > 0 (multiplication with a negative value, as assumed) So −1 > 0, oops! The lesson is that there is no way

to impose an arrangement on the complex numbers that would justify the usage of the symbols < and >

in the mathematical sense

Nevertheless we can invent a relation that allows us to sort: arranging (sorting) the complex numbersaccording to their absolute value (modulus) leaves infinitely many numbers in one ‘bucket’, namely all

those that have the same distance to zero However, one could use the modulus as the major ordering parameter, the angle as the minor Or the real part as the major and the imaginary part as the minor.

The latter is realized in

static inline int

cmp_complex(const Complex &f, const Complex &g)

which, when used as comparison with the above function-sort as in

void complex_sort(Complex *f, ulong n)

// major order wrt real part

// minor order wrt imag part

Testing whether all values are unique:

template <typename Type>

int test_unique(const Type *f, ulong n)

// for a sorted array test whether all values are unique

// (i.e whether no value is repeated)

//

// returns 0 if all values are unique

// else returns index of the second element in the first pair found

//

// this function is not called "is_unique()" because it

// returns 0 (=="false") for a positive answer

Trang 20

return 0;

}

The same thing, but for inexact types (floats): the maximal (absolute) difference within which twocontiguous elements will still be considered equal can be provided as additional parameter One subtlepoint is that the values can slowly ‘drift away’ unnoticed by this implementation: Consider a long array

where each difference computed has the same sign and is just smaller than da, say it is d = 0.6·da The difference of the first and last value then is 0.6 · (n − 1) · d which is greater than da for n ≥ 3.

template <typename Type>

int test_unique_approx(const Type *f, ulong n, Type da)

// for a sorted array test whether all values are

// unique within some tolerance

// (i.e whether no value is repeated)

//

// returns 0 if all values are unique

// else returns index of the second element in the first pair found

An alternative way to deal with inexact types is to apply

template <typename Type>

void quantise(Type *f, ulong n, double q)

//

// in f[] set each element x to q*floor(1/q*(x+q/2))

// e.g.: q=1 ==> round to nearest integer

// q=1/1000 ==> round to nearest multiple of 1/1000

// For inexact types (float or double)

Ngày đăng: 09/08/2014, 12:22