Tải bản đầy đủ (.docx) (87 trang)

Basic algorithm Sưu tầm các thuật toán trong lập trình

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (3.5 MB, 87 trang )

MỤC LỤC
Algebra (23)
basic algorithms (20)
• Euler function and its calculation
[TeX]

• Binary exponentiation of O (log N)
[TeX]

• Euclid's algorithm finding the GCD (greatest common divisor)
[TeX]

• Sieve of Eratosthenes
[TeX]

• Advanced Euclidean algorithm
[TeX]

• Fibonacci numbers and their rapid calculation
[TeX]

• The inverse of the ring modulo
[TeX]

• Gray code
[TeX]

• Long arithmetic
[TeX]

• Discrete logarithm modulo M algorithm baby-step-giant-step


Shanks for O (sqrt (M) log M)
[TeX]

• Diophantine equations with two unknowns: AX + BY = C
[TeX]

• Modular linear equation of the first order: AX = B
[TeX]

• Chinese remainder theorem. Garner's algorithm
[TeX]

• Finding degree divider factorial
[TeX]

• Balanced ternary notation
[TeX]

• The factorial N! modulo P for O (P log N)
[TeX]

• Enumeration of all subpatterns this mask. Grade 3
N
to the total
number of all subpatterns masks
[TeX]

1
• A primitive root. Algorithm for finding
[TeX]


• Discrete root extract
[TeX]

• Sieve of Eratosthenes with linear time work
[TeX]

complex algorithms (3)
• Test BPSW the simplicity of numbers in O (log N)
• Efficient algorithms for factorization Pollard p-1, Pollard p, Bent,
Pollard Monte Carlo Farm
• Fast Fourier transform of O (N log N). Application to the
multiplication of two polynomials or long numbers
[TeX]

Euler function
Determination
Euler function (sometimes labeled or ) - the number of
numbers to prime to . In other words, the number of segment numbers
, the greatest common divisor with which is unity.
The first few values of this function ( A000010 in OEIS encyclopedia ):
2
Properties
The following three simple properties of the Euler - enough to know how to calculate
it for any numbers:
• If - a prime number, then .
(This is obvious, since any number except the prime to it.)
• If - simple - a natural number, then .
(As with the number of not only relatively prime numbers of the form ,
which pieces.)

• If and are relatively prime, then ("multiplicative" Euler
function).
(This follows from the Chinese remainder theorem . Consider an arbitrary
number . We denote by and the remnants of the division at and ,
respectively. Then prime to if and only if is prime to and separately, or what
is the same, mutually simply and relatively prime to . Using the Chinese
remainder theorem, we see that any pair of numbers and the number of one-to-
one correspondence , which completes the proof.)
From here you can get the Euler function for all through
his factorization (decomposition into prime factors):
3
if
(Where everything - simple), the
Implementation
The simplest code calculating the Euler function, the quotient of the number of
elementary method for :
int phi (int n) {
int result = n;
for (int i=2; i*i<=n; ++i)
if (n % i == 0) {
while (n % i == 0)
n /= i;
result -= result / i;
}
if (n > 1)
result -= result / n;
return result;
}
4
A key place for the calculation of the Euler function - is to find the factorization of

. It can be done in a time much smaller : see. Efficient algorithms for
factorization .
The application of Euler's function
The most famous and important property of Euler expressed in Euler's theorem :
where and are relatively prime.
In the particular case when a simple Euler's theorem turns into the so-
called Fermat's little theorem :
Euler's theorem occurs quite often in practical applications, for example,
see. Reverse element in the modulus .
Tasks in the online judges
A list of tasks that require a function to calculate the Euler or use Euler's theorem,
either by value of the Euler function to restore the original number:
• UVA # 10179 "Irreducible Basic Fractions" [Difficulty: Low]
• UVA # 10299 "Relatives" [Difficulty: Low]
• UVA # 11327 "Enumerating Rational Numbers" [Difficulty: Medium]
• TIMUS # 1673 "Admission to the exam" [Difficulty: High]
Binary exponentiation
5
Binary (binary) exponentiation - is a technique that allows you to build any
number of th degree of multiplications (instead of multiplications in the
usual approach).
Moreover, the technique described here is applicable to any of the
associative operation, and not only to the multiplication numbers. Recall operation
is called associative if for any executed:
The most obvious generalization - on balances of some module (obviously,
associativity is maintained). The next "popularity" is a generalization to the matrix
product (it is well known associativity).
Algorithm
Note that for any number and an even number of feasible obvious identity (which
follows from the associativity of multiplication):

It is the main method in binary exponentiation. Indeed, for an even we
showed how, by spending only one multiplication, we can reduce the
problem to a half less.
It remains to understand what to do, if the degree is odd . Here we do is very
simple: move on to the extent that would have an even:
So, we actually found the recurrence formula: on the degree we go, if it chёtna
to , and otherwise - to . It is clear that there will be no more
transitions, before we come to (the basis of the recurrence formula). Thus,
we have an algorithm that works for multiplications.
6
Implementation
A simple recursive implementation:
int binpow (int a, int n) {
if (n == 0)
return 1;
if (n % 2 == 1)
return binpow (a, n-1) * a;
else {
int b = binpow (a, n/2);
return b * b;
}
}
Non-recursive implementation also optimized (divide-by-2 replaced with bit
operations):
int binpow (int a, int n) {
int res = 1;
while (n)
if (n & 1) {
res *= a;
n;

}
else {
a *= a;
n >>= 1;
}
return res;
}
This implementation can be more simplified somewhat by noting that the
construction of the square is always performed, regardless of whether the
condition is odd worked or not:
int binpow (int a, int n) {
7
int res = 1;
while (n) {
if (n & 1)
res *= a;
a *= a;
n >>= 1;
}
return res;
}
Finally, it is worth noting that the binary exponentiation is already implemented in
the language of Java, but only for long arithmetic class BigInteger (pow function of
this class is working on the construction of the binary algorithm).
Examples of solving problems
Efficient computation of Fibonacci numbers
Condition . Given the number . You want to calculate where - Fibonacci
sequence .
Solution . More details are described in the decision paper on the Fibonacci
sequence . Here we only briefly we present the essence of the decision.

The basic idea is as follows. The calculation of the next Fibonacci number is based
on the knowledge of the previous two Fibonacci numbers: namely, each successive
Fibonacci number is the sum of the previous two. This means that we can construct
a matrix that will fit this transformation: how two Fibonacci numbers
and calculate the next number, ie go to the pair , . For example,
applying this transformation to a couple of times and we get a couple
and .Thus, elevating the matrix of this transformation in the -th degree, we
thus find the required time for what we required.
8
Erection reshuffle of th degree
Condition . Given permutation of length . Required to build it in -s degree,
ie find out, if to the identity permutation permutation times apply .
Solution . Simply apply to the interchange of the algorithm described above
binary exponentiation. No differences as compared with the construction numbers in
degree - no. With the asymptotic solution is obtained .
(Note. This problem can be solved more efficiently in linear time . To do this, simply
select all the cycles in the permutation, then consider separately each cycle and
taking the modulo length of the current cycle, to find an answer for this cycle.)
Rapid application of a set of geometric operations to points
Condition . Given points and are transformations that should be applied to
each of these points. Each transformation - a shift to a predetermined vector, or
scaling (multiplication coefficients to specified coordinates), or around a
predetermined rotation axis by a predetermined angle. Furthermore, there is a
component of a cyclic repetition operation: it has the form "a predetermined number
of times to repeat a predetermined list of transformations" (cyclic repetition of
operations can be nested in each other).
You want to calculate the result of applying these operations to all points (effectively,
ie in less than than where - the total number of transactions
that need to be done).
Solution . Look at the different types of transformations in terms of how they

change the coordinates:
• Shift operation - it just adds to all the coordinates of the unit, the
multiplication by constants.
• Scaling operation - it multiplies each coordinate by some constant.
9
• Operation rotational axis - it can be represented as follows: new
coordinates obtained can be written as a linear combination of the old
ones.
(We will not specify how this is done. For example, for simplicity, imagine it as a
combination of five-dimensional rotations: first, in the planes , and so
that the axis of rotation coincides with the positive direction of the axis , then the
desired rotation around an axis in the plane , then reverse rotation in the
plane and so that the axis of rotation back to its starting position.)
Easy to see that each of these transformations - a recalculation of coordinates on
linear equations. Thus, any such transformation can be written in matrix form :
which when multiplied by (left) to the line of the old coordinates and the constant
unit gives a string of new coordinates and constants units:
(Why need to enter a dummy fourth coordinate is always equal to one? Without this
we would have to implement the shift operation: after the shift - this is just adding to
the coordinates of the unit, the multiplication by some factors. Without the dummy
unit we could only implement linear combinations of the coordinates themselves,
and add to them given constants - could not.)
Now the solution of the problem becomes almost trivial. Once each elementary
operation is described by the matrix, then the process described by the product of
these matrices, and the operation of the cyclic repetition - the erection of this matrix
10
to a power. Thus, during the time we can predposchitat
matrix describing all the changes, and then simply multiply each point on
the matrix - thus, we will respond to all requests for time .
The number of paths in a fixed length column

Condition . Given an undirected graph with vertices, and are given a number
. Is required for each pair of vertices and the number of ways to find among them
with exactly edges.
Solution . More details on this problem is considered in a separate article . Here we
only recall the essence of the decision: we simply erect a -th degree of adjacency
matrix of the graph, and the elements of this matrix and will be a solution. The
resulting asymptotic behavior - .
(Note. In the same article, consider other variation of this problem: when the graph
is weighted, and you want to find the path of minimum weight, containing exactly
edges. As shown in this paper, this problem is also solved by means of binary
exponentiation of the adjacency matrix of the graph, but instead of the usual
operation of multiplication of two matrices to be used modified: instead of
multiplying the amount taken, and instead of summation - taking a minimum.)
Variation binary exponentiation: multiplication of two integers
modulo
We give here an interesting variation of the binary exponentiation.
Suppose we are faced with such a challenge : to multiply two numbers and
modulo :
11
Suppose that the numbers can be quite large: so that the numbers themselves are
placed in a built-in data types, but their immediate work - no longer exists (note
that we also require that the sum of the numbers placed in a built-in data
type). Accordingly, the task is to find the unknown quantity ,
without the help of a long arithmetic .
The decision is as follows. We simply apply the algorithm binary exponentiation
described above, but instead of multiplication, we will make the addition. In other
words, multiplication of two numbers, we have reduced to the operations
of addition and multiplication by two (which is also, in fact, there is the addition).
(Note. This problem can be solved in another way , by resorting to the help of
operations with floating-point numbers. Namely, the count in the floating point

expression , and round it to the nearest integer. Thus we find the
approximate quotient. take it away from the product (ignoring the overflow), we
are likely to obtain a relatively small number that can be taken modulo - and
return it as an answer. This solution looks pretty unreliable, but it is very fast, and
very briefly implemented.)
Tasks in the online judges
List of tasks that can be solved using a binary exponentiation:
• SGU # 265 "Wizards" [Difficulty: Medium]

Euclid's algorithm finding the GCD
(greatest common divisor)
12
Given two non-negative integers and . Required to find their greatest common
divisor, ie the largest number that divides both a and . English "greatest common
divisor" is spelled "greatest common divisor", and its common designation is :
(Here the symbol " "denotes the divisibility, ie," "means" divide ")
When it is of the numbers is zero, and the other is non-zero, their greatest common
divisor, by definition, be it the second number. When both numbers are equal to
zero, the result is not defined (any suitable infinite number), we put in this case the
greatest common divisor of zero. Therefore, we can speak of such a rule: if one of
the numbers zero, the greatest common divisor equal to the second number.
Euclid's algorithm , discussed below, solves the problem of finding the greatest
common divisor of two integers and over .
This algorithm was first described in the book of Euclid's "Elements" (about 300
BC), although it is quite possible, this algorithm has an earlier origin.
Algorithm
The algorithm itself is extremely simple and is described by the following formula:
Implementation
int gcd (int a, int b) {
if (b == 0)

return a;
else
return gcd (b, a % b);
13
}
Using the ternary conditional operator C ++, the algorithm can be written even
shorter:
int gcd (int a, int b) {
return b ? gcd (b, a % b) : a;
}
Finally, we present and non-recursive algorithm form:
int gcd (int a, int b) {
while (b) {
a %= b;
swap (a, b);
}
return a;
}
Proof of correctness
First, we note that for each iteration of the algorithm of Euclid its second argument
is strictly decreasing, therefore, since it is non-negative, then the Euclidean
algorithmalways terminates .
To prove the correctness we need to show that for
any .
We show that the quantity on the left-hand side is divided by this in the right, and
the right-hand - is divided into the left-hand. Obviously, this will mean that the left
and right sides of the same, and that proves the correctness of Euclid's algorithm.
Denote . Then, by definition, and .
Next, we expand the remainder of the division on through their private:
14

But then it follows:
So, recalling the statement , we obtain the system:
Now we use the following simple fact: if for any three numbers fulfilled:
and then executed and: . In our situation, we get:
Or, substituting its definition as we obtain:
So, we spent half the evidence is shown that divides the left side of the right. The
second half of the proof is similar.
Operation time
Time of the algorithm is evaluated Lame theorem , which establishes a surprising
connection of the Euclidean algorithm and the Fibonacci sequence:
If and for some , the Euclidean algorithm performs no
more recursive calls.
Moreover, it can be shown that the upper bound of the theorem -
optimal. When it will be done recursive call. In other
15
words, successive Fibonacci numbers - the worst input to the Euclidean
algorithm.
Given that the Fibonacci numbers grow exponentially (as a constant in degree ),
we find that the Euclidean algorithm is performed for
multiplication.
LCM (least common multiple)
The calculation of the least common multiple (least common multiplier, lcm) is
reduced to the calculation of the following simple statement:
Thus, the calculation of the NOC can also be done using the Euclidean algorithm,
with the same asymptotic behavior:
int lcm (int a, int b) {
return a / gcd (a, b) * b;
}
(Here divided into profitable first , and only then is multiplied by , as this will
help to avoid overflows in some cases)

Literature
• Thomas feed, Charles Leiserson, Ronald Rivest, Clifford Stein. Introduction
to Algorithms [2005]
Sieve of Eratosthenes
16
Sieve of Eratosthenes - an algorithm for finding all prime numbers in the
interval of operations.
The idea is simple - we will write a series of numbers , and we will strike out
at first all numbers divisible by , except for the number and then dividing by ,
except for the numbers , then on , then , and everything else is just up .
Implementation
Immediately we present the implementation of the algorithm:
int n;
vector<char> prime (n+1, true);
prime[0] = prime[1] = false;
for (int i=2; i<=n; ++i)
if (prime[i])
if (i * 1ll * i <= n)
for (int j=i*i; j<=n; j+=i)
prime[j] = false;
This code first checks all numbers except zero and one, as simple, and then begins
the process of sifting composite numbers. To do this, we loop through all the
numbers from before , and if the current number of simple, it ticks all the
multiples of him as a constituent.
At the same time we start to go from as fewer multiples , be sure to have a prime
divisor less , which means that they have already been eliminated earlier.
(However, as can easily overwhelm type in the code before the second nested
loop is an additional check using the type .)
17
With this realization algorithm consumes memory (obviously) and

performs actions (this is proved in the next section).
Asymptotics
We prove that the asymptotic behavior of the algorithm is .
So, for every prime inner loop will be executed, which will make
action. Therefore, we need to estimate the following value:
Let us recall here two well-known fact: that the number of primes less than or equal
to approximately equal , and that the -th prime number is approximately equal
to (this follows from the first statement). Then the sum can be written as
follows:
Here, we have identified the first prime of the sum, since the approximation
according to the will , which will lead to a division by zero.
Now we estimate this sum by the integral of the same function on from before
(we can produce such an approximation, since, in fact, refers to the sum of the
integral as his approach to the rectangle formula):
18
Antiderivative for the integrand there . Substitute and removing members of
the lower order, we obtain:
Now, returning to the original sum, we obtain an approximate estimate of its:
QED.
More rigorous proof (and gives a more accurate estimate, up to constant factors)
can be found in the book of Hardy and Wright "An Introduction to the Theory of
Numbers" (p. 349).
Various optimizations sieve of Eratosthenes
The biggest drawback of the algorithm - the fact that he "walks" from memory,
constantly go beyond the cache, which is why the constant hidden in
the relatively large.
In addition, for sufficiently large volume becomes the bottleneck of memory
consumption.
The following are the methods to both reduce the number of operations performed,
and significantly reduce memory consumption.

Sifting easy to root
The most obvious point - that in order to find all simple to sufficiently perform only
simple sieving not exceeding root .
Thus, change the outer loop of the algorithm:
for (int i=2; i*i<=n; ++i)
On the asymptotic behavior of this optimization does not affect (in fact, repeating
the proof given above, we obtain an estimate that, on the
19
properties of logarithms, asymptotically have the same thing), although the number
of transactions decreased markedly.
Sieve only in odd numbers
Since all the even numbers, except - components, we can not process any way at
all even numbers and odd numbers only operate.
Firstly, it will halve the amount of memory required. Secondly, it makes the algorithm
will reduce the number of operations by about half.
To reduce the amount of memory consumed
Note that the algorithm of Eratosthenes actually operates with bits of
memory. Consequently, we can save significant memory consumption is not
stored bytes - booleans, and bits, ie bytes of memory.
However, this approach - "bit compression" - significantly complicate handling
these bits. Any read or write bits will be of a few arithmetic operations, which
ultimately lead to slower algorithm.
Thus, this approach is justified only if so large that bytes of memory to allocate
anymore. Save memory (in time), we will pay for it a substantial slowing of the
algorithm.
In conclusion, it is worth noting that in C ++ containers have already been
implemented, automatically performing bit compression: vector <bool> and bitset
<>. However, if speed is important, it is better to implement a bit compressed
manually using bit operations - today compilers still not able to generate enough
fast code.

Block sieve
Optimization of the "simple screening to the root" it follows that there is no need to
store all the time the whole array . To perform screening sufficient to
store only simple to root , that is , the remainder of the
array to build block by block, keeping the current time, only one block.
20
Let - constant that determines the block size, then all will be blocks, th block
( ) contains a number in the interval . Will process
the blocks of the queue, i.e. for each th block will go through all the simple (from
before ) and perform their screening only within the current block. Gently should
handle the first block - firstly, from simple do not have to remove
themselves, and secondly, the number and must be marked as not particularly
simple. When processing the last block should also not forget that the latter desired
number is not necessarily the end of the block.
We present the implementation of a sieve block. The program reads the number
and the number of finds from the simple to :
const int SQRT_MAXN = 100000; // корень из максимального
значения N
const int S = 10000;
bool nprime[SQRT_MAXN], bl[S];
int primes[SQRT_MAXN], cnt;

int main() {

int n;
cin >> n;
int nsqrt = (int) sqrt (n + .0);
for (int i=2; i<=nsqrt; ++i)
if (!nprime[i]) {
primes[cnt++] = i;

if (i * 1ll * i <= nsqrt)
for (int j=i*i; j<=nsqrt; j+=i)
nprime[j] = true;
21
}

int result = 0;
for (int k=0, maxk=n/S; k<=maxk; ++k) {
memset (bl, 0, sizeof bl);
int start = k * S;
for (int i=0; i<cnt; ++i) {
int start_idx = (start + primes[i] - 1) /
primes[i];
int j = max(start_idx,2) * primes[i] -
start;
for (; j<S; j+=primes[i])
bl[j] = true;
}
if (k == 0)
bl[0] = bl[1] = true;
for (int i=0; i<S && start+i<=n; ++i)
if (!bl[i])
++result;
}
cout << result;

}
Asymptotics sieve block is the same as a conventional sieve of Eratosthenes
(unless, of course, the size of the blocks is not very small), but the amount of
memory used will be reduced to decrease and "walk" from memory. On

the other hand, for each block for each of the simple division will be
performed that will strongly affect in a smaller unit. Consequently, the choice of the
constant need to keep a balance.
22
Experiments show that the best speed is achieved when the value is about
to .
Upgrade to linear time work
Eratosthenes algorithm can be converted into a different algorithm, which already
works in linear time - see. Article "Sieve of Eratosthenes linear-time
work" . (However, this algorithm has some limitations.)

Advanced Euclidean algorithm
While the "normal" Euclidean algorithm simply finds the greatest common divisor of
two numbers and , extended Euclidean algorithm is in addition to the GCD and
the coefficients and such that:
Ie it finds the coefficients by which the GCD of two numbers expressed in terms of
these numbers themselves.
Algorithm
Make the calculation of these coefficients in the Euclidean algorithm is easy enough
to deduce the formula by which they change from pair to pair
(percent sign we mean taking the remainder of the division).
Thus, suppose we have found the solution of the problem for a new
pair :
23
and want to get a solution for our couples :
To do this, we transform the value of :
Substituting this in the above expression and and get:
and performing regrouping terms, we obtain:
Comparing this with the original expression of the unknown , and we obtain the
desired expression:

Implementation

int gcd (int a, int b, int & x, int & y) {
if (a == 0) {
x = 0; y = 1;
return b;
}
int x1, y1;
int d = gcd (b%a, a, x1, y1);
x = y1 - (b / a) * x1;
24
y = x1;
return d;
}
This is a recursive function that still returns the GCD of the numbers and , but
apart from that - also sought coefficients and parameters as a function passed by
reference.
Base of recursion - case . Then GCD is , and obviously, the desired ratio
and are and , respectively. In other cases, the usual solution is working, and the
coefficients are translated at the above formulas.
Advanced Euclidean algorithm in this implementation works correctly even for
negative numbers.
Literature
• Thomas feed, Charles Leiserson, Ronald Rivest, Clifford Stein. Introduction
to Algorithms [2005]
Fibonacci numbers
Determination
The Fibonacci sequence is defined as follows:
The first few of its members:
25

×