Skip to content

Various improvements #396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Feb 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/algebra/discrete-log.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ $$O(\sqrt {m} \log m).$$

### The simplest implementation

In the following code, the function `powmod` calculates $a^b \pmod m$ and the function `solve` produces a proper solution to the problem. It returns $-1$ if there is no solution and returns one of the possible solutions otherwise.
In the following code, the function `powmod` calculates $a^b \pmod m$ and the function `solve` produces a proper solution to the problem.
It returns $-1$ if there is no solution and returns one of the possible solutions otherwise.
The resulting discrete logarithm can be big, but you can make it smaller using [Euler's theorem](./algebra/phi-function.html#toc-tgt-2).

```cpp
int powmod(int a, int b, int m) {
Expand All @@ -85,7 +87,7 @@ int solve(int a, int b, int m) {
for (int p = n; p >= 1; --p)
vals[powmod(a, p * n, m)] = p;
for (int q = 0; q <= n; ++q) {
int cur = (powmod (a, q, m) * b) % m;
int cur = (powmod(a, q, m) * b) % m;
if (vals.count(cur)) {
int ans = vals[cur] * n - q;
return ans;
Expand All @@ -106,7 +108,10 @@ We also need to change the second step accordingly.

## Improved implementation

A possible improvement is to get rid of binary exponentiation. This can be done by keeping a variable that is multiplied by $a$ each time we increase $q$ and a variable that is multiplied by $a^n$ each time we increase $p$. With this change, the complexity of the algorithm is still the same, but now the $\log$ factor is only for `map`. Instead of a `map`, we can also use a hash table (`unordered_map` in C++) which has the average time complexity $O(1)$ for inserting and searching.
A possible improvement is to get rid of binary exponentiation.
This can be done by keeping a variable that is multiplied by $a$ each time we increase $q$ and a variable that is multiplied by $a^n$ each time we increase $p$.
With this change, the complexity of the algorithm is still the same, but now the $\log$ factor is only for the `map`.
Instead of a `map`, we can also use a hash table (`unordered_map` in C++) which has the average time complexity $O(1)$ for inserting and searching.

```cpp
int solve(int a, int b, int m) {
Expand Down
97 changes: 54 additions & 43 deletions src/algebra/discrete-root.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# Discrete Root

The problem of finding discrete root is defined as follows. Given a prime $n$ and two integers $a$ and $k$, find all $x$ for which:
The problem of finding a discrete root is defined as follows. Given a prime $n$ and two integers $a$ and $k$, find all $x$ for which:

$x^k \equiv a \pmod n$

Expand All @@ -14,7 +14,7 @@ Let's apply the concept of a [primitive root](./algebra/primitive-root.html) mod

We can easily discard the case where $a = 0$. In this case, obviously there is only one answer: $x = 0$.

Since we jnow that $n$ is a prime, any number between 1 and $n-1$ can be represented as a power of the primitive root, and we can represent the discrete root problem as follows:
Since we know that $n$ is a prime and any number between 1 and $n-1$ can be represented as a power of the primitive root, we can represent the discrete root problem as follows:

$(g^y)^k \equiv a \pmod n$

Expand All @@ -26,7 +26,7 @@ This, in turn, can be rewritten as

$(g^k)^y \equiv a \pmod n$

Now we have one unknown $y$, which is a discrete logarithm problem. The solution can be found using Shanks' baby-step-giant-step algorithm in $O(\sqrt {n} \log n)$ (or we can verify that there are no solutions).
Now we have one unknown $y$, which is a discrete logarithm problem. The solution can be found using Shanks' baby-step giant-step algorithm in $O(\sqrt {n} \log n)$ (or we can verify that there are no solutions).

Having found one solution $y_0$, one of solutions of discrete root problem will be $x_0 = g^{y_0} \pmod n$.

Expand All @@ -46,88 +46,99 @@ where $l$ is chosen such that the fraction must be an integer. For this to be tr

$x = g^{y_0 + i \frac {\phi (n)}{gcd(k, \phi (n))}} \pmod n \forall i \in Z$.

This is the final formula for all solutions of discrete root problem.
This is the final formula for all solutions of the discrete root problem.

## Implementation

Here is a full implementation, including routines for finding the primitive root, discrete log and finding and printing all solutions.
Here is a full implementation, including procedures for finding the primitive root, discrete log and finding and printing all solutions.

```cpp
int gcd (int a, int b) {
return a ? gcd (b%a, a) : b;
int gcd(int a, int b) {
return a ? gcd(b % a, a) : b;
}

int powmod (int a, int b, int p) {
int powmod(int a, int b, int p) {
int res = 1;
while (b)
if (b & 1)
res = int (res * 1ll * a % p), --b;
else
a = int (a * 1ll * a % p), b >>= 1;
while (b > 0) {
if (b & 1) {
res = res * a % p;
}
a = a * a % p;
b >>= 1;
}
return res;
}

int generator (int p) {
// Finds the primitive root modulo p
int generator(int p) {
vector<int> fact;
int phi = p-1, n = phi;
for (int i=2; i*i<=n; ++i)
int phi = p-1, n = phi;
for (int i = 2; i * i <= n; ++i) {
if (n % i == 0) {
fact.push_back (i);
fact.push_back(i);
while (n % i == 0)
n /= i;
}
}
if (n > 1)
fact.push_back (n);
fact.push_back(n);

for (int res=2; res<=p; ++res) {
for (int res = 2; res <= p; ++res) {
bool ok = true;
for (size_t i=0; i<fact.size() && ok; ++i)
ok &= powmod (res, phi / fact[i], p) != 1;
if (ok) return res;
for (int factor : fact) {
if (powmod(res, phi / factor, p) != 1) {
ok = false;
break;
}
}
if (ok) return res;
}
return -1;
}

// This program finds all numbers x such that x^k = a (mod n)
int main() {

int n, k, a;
cin >> n >> k >> a;
scanf("%d %d %d", &n, &k, &a);
if (a == 0) {
puts ("1\n0");
puts("1\n0");
return 0;
}

int g = generator (n);
int g = generator(n);

// Baby-step giant-step discrete logarithm algorithm
int sq = (int) sqrt (n + .0) + 1;
vector < pair<int,int> > dec (sq);
for (int i=1; i<=sq; ++i)
dec[i-1] = make_pair (powmod (g, int (i * sq * 1ll * k % (n - 1)), n), i);
sort (dec.begin(), dec.end());
vector<pair<int, int>> dec(sq);
for (int i = 1; i <= sq; ++i)
dec[i-1] = {powmod(g, i * sq * k % (n - 1), n), i};
sort(dec.begin(), dec.end());
int any_ans = -1;
for (int i=0; i<sq; ++i) {
int my = int (powmod (g, int (i * 1ll * k % (n - 1)), n) * 1ll * a % n);
vector < pair<int,int> >::iterator it =
lower_bound (dec.begin(), dec.end(), make_pair (my, 0));
for (int i = 0; i < sq; ++i) {
int my = powmod(g, i * k % (n - 1), n) * a % n;
auto it = lower_bound(dec.begin(), dec.end(), make_pair(my, 0));
if (it != dec.end() && it->first == my) {
any_ans = it->second * sq - i;
break;
}
}
if (any_ans == -1) {
puts ("0");
puts("0");
return 0;
}

int delta = (n-1) / gcd (k, n-1);
// Print all possible answers
int delta = (n-1) / gcd(k, n-1);
vector<int> ans;
for (int cur=any_ans%delta; cur<n-1; cur+=delta)
ans.push_back (powmod (g, cur, n));
sort (ans.begin(), ans.end());
printf ("%d\n", ans.size());
for (size_t i=0; i<ans.size(); ++i)
printf ("%d ", ans[i]);

for (int cur = any_ans % delta; cur < n-1; cur += delta)
ans.push_back(powmod(g, cur, n));
sort(ans.begin(), ans.end());
printf("%d\n", ans.size());
for (int answer : ans)
printf("%d ", answer);
}
```

## Practice problems

* [Codeforces - Lunar New Year and a Recursive Sequence](https://codeforces.com/contest/1106/problem/F)
2 changes: 1 addition & 1 deletion src/algebra/fft.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ $$A(x) = A_0(x^2) + x A_1(x^2).$$
The polynomials $A_0$ and $A_1$ are only half as much coefficients as the polynomial $A$.
If we can compute the $\text{DFT}(A)$ in linear time using $\text{DFT}(A_0)$ and $\text{DFT}(A_1)$, then we get the recurrence $T_{\text{DFT}}(n) = 2 T_{\text{DFT}}\left(\frac{n}{2}\right) + O(n)$ for the time complexity, which results in $T_{\text{DFT}}(n) = O(n \log n)$ by the **master theorem**.

Lets learn how we can accomplish that.
Let's learn how we can accomplish that.

Suppose we have computed the vectors $\left(y_k^0\right)\_{k=0}^{n/2-1} = \text{DFT}(A_0)$ and $\left(y_k^1\right)\_{k=0}^{n/2-1} = \text{DFT}(A_1)$.
Let us find a expression for $\left(y_k\right)_{k=0}^{n-1} = \text{DFT}(A)$.
Expand Down
4 changes: 2 additions & 2 deletions src/algebra/phi-function.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ This allows computing $x^n \bmod m$ for very big $n$, especially if $n$ is the r

There is a less known version of the last equivalence, that allows computing $x^n \bmod m$ efficiently for not coprime $x$ and $m$.
For arbitrary $x, m$ and $n \geq \log_2 m$:
$$x^{n}\equiv x^{\varphi(m)+[n \bmod \varphi(m)]} \mod m$$
$$x^{n}\equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m$$

Proof:

Expand All @@ -100,7 +100,7 @@ $$x^n \bmod m = x^k\left(x^{n-k \bmod \phi(\frac{m}{a})} \bmod \frac{m}{a}\right
This formula is difficult to apply, but we can use it to analyze the behavior of $x^n \bmod m$. We can see that the sequence of powers $(x^1 \bmod m, x^2 \bmod m, x^3 \bmod m, \dots)$ enters a cycle of length $\phi\left(\frac{m}{a}\right)$ after the first $k$ (or less) elements.
$\phi(m)$ divides $\phi\left(\frac{m}{a}\right)$ (because $a$ and $\frac{m}{a}$ are coprime we have $\phi(a) \cdot \phi\left(\frac{m}{a}\right) = \phi(m)$), therefore we can also say that the period has length $\phi(m)$.
And since $\phi(m) \ge \log_2 m \ge k$, we can conclude the desired, much simpler, formula:
$$ x^n \equiv x^{\varphi(m)} x^{(n - \varphi(m)) \bmod \varphi(m)} \bmod m \equiv x^{\varphi(m)+[n \bmod \varphi(m)]} \mod m.$$
$$ x^n \equiv x^{\phi(m)} x^{(n - \phi(m)) \bmod \phi(m)} \bmod m \equiv x^{\phi(m)+[n \bmod \phi(m)]} \mod m.$$

## Practice Problems

Expand Down
2 changes: 1 addition & 1 deletion src/data_structures/disjoint_set_union.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ The only difficulty that we face is to compute the parity in the `union_find` me

If we add an edge $(a, b)$ that connects two connected components into one, then when you attach one tree to another we need to adjust the parity.

Lets derive a formula, which computes the parity issued to the leader of the set that will get attached to another set.
Let's derive a formula, which computes the parity issued to the leader of the set that will get attached to another set.
Let $x$ be the parity of the path length from vertex $a$ up to its leader $A$, and $y$ as the parity of the path length from vertex $b$ up to its leader $B$, and $t$ the desired parity that we have to assign to $B$ after the merge.
The path contains the of the three parts:
from $B$ to $b$, from $b$ to $a$, which is connected by one edge and therefore has parity $1$, and from $a$ to $A$.
Expand Down
2 changes: 1 addition & 1 deletion src/data_structures/segment_tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ The time complexity of the construction is $O(n)$.
For now we are going to answer sum queries. As an input we receive two integers $l$ and $r$, and we have to compute the sum of the segment $a[l \dots r]$ in $O(\log n)$ time.

To do this, we will traverse the Segment Tree and use the precomputed sums of the segments.
Lets assume that we are currently at the vertex that covers the segment $a[tl \dots tr]$.
Let's assume that we are currently at the vertex that covers the segment $a[tl \dots tr]$.
There are three possible cases.

The easiest case is when the segment $a[l \dots r]$ is equal to the corresponding segment of the current vertex (i.e. $a[l \dots r] = a[tl \dots tr]$), then we are finished and can return the precomputed sum that is stored in the vertex.
Expand Down
2 changes: 1 addition & 1 deletion src/graph/2SAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ We now construct a directed graph of these implications:
for each variable $x$ there will be two vertices $v_x$ and $v_{\lnot x}$.
The edges will correspond to the implications.

Lets look at the example in 2-CNF form:
Let's look at the example in 2-CNF form:

$$(a \lor \lnot b) \land (\lnot a \lor b) \land (\lnot a \lor \lnot b) \land (a \lor \lnot c)$$

Expand Down
2 changes: 1 addition & 1 deletion src/graph/edmonds_karp.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ The following image show a flow network.
The first value of each edge represents the flow, which is initially 0, and the second value represents the capacity.
<center>![Flow network](&imgroot&/Flow1.png)</center>

We value of a flow of a network is the sum of all flows that gets produced in source $s$, or equivalent or the flows that is consumed in the sink $t$.
The value of a flow of a network is the sum of all flows that gets produced in source $s$, or equivalently of the flows that are consumed in the sink $t$.
A **maximal flow** is a flow with the maximal possible value.
Finding this maximal flow of a flow network is the problem that we want to solve.

Expand Down
2 changes: 1 addition & 1 deletion src/graph/fixed_length_paths.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ It is obvious that the constructed adjacency matrix if the answer to the problem
It contains the number of paths of length $1$ between each pair of vertices.

We will build the solution iteratively:
Lets assume we know the answer for some $k$.
Let's assume we know the answer for some $k$.
Here we describe a method how we can construct the answer for $k + 1$.
Denote by $C_k$ the matrix for the case $k$, and by $C_{k+1}$ the matrix we want to construct.
With the following formula we can compute every entry of $C_{k+1}$:
Expand Down
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy