Algorithms Mini Field Guide 2.0: Fast Fourier Transform
Algorithms Mini Field Guide 2.0: Fast Fourier Transform
Algorithms Mini Field Guide 2.0: Fast Fourier Transform
0
Written by: Krishna Parashar
Published by: The OCM
x(ab) =
x(a+b) (xa )b
k2
k=1
ark1 =
k=1
k=
2. From the FFT we get the roots of unity evaluated at p(x) and q(x) and we
multiply the respected values from each root together to get 2n + 1
respective pairs of values of (p q)(x). O(n)
k=1
n
n(n+1)
2
2k 1 = n2
function FFT(A, w)
k=1
n(n+1)(2n+1)
Finite Geometric Series:
6
a(1r )
1r
Us for most edge removal problems, use this to check if still strongly connected.
Properties:
3. Finally we interpolate these points using the inverse FFT to get the
coeicients of (p q)(x). O(n log n)
Basic Series
= x(ab) x( ) = x
1
2
xa
xb
The Fast Fourier Transform in the scope of our knowledge thus far is used for
Polynomial Multiplication. We want to know the coeicient of (p q)(x) knowing
only the coeicients of p(x) and q(x). The naive way is the one we all use in
Algebra which runs in O(n2 ). Here is a faster algorithm:
1. Take the coeicients of p(x) and q(x) and plug them into the FFT with
the Roots of Unity (We need 2n + 1 points). O(n log n)
ark1 =
k=1
if w = 1: return A(1)
a
1r
0()
f (n)
< (0)
lim
n g(n)
= c, 0 < c <
f (n) (g(n))
f (n) O(g(n))
f (n) (g(n))
return A(w_0),...,A(w_n1)
def bfs(G,s):
Search Algorithms
Depth First Search O(V + E) (Stack)
Explores all the way down to a tree, then climbs back up and explores alt paths.
Use for Topological Sort and Finding Connected Components.
for all u in V:
dist(u) = infinity
dist(s) = 0
Q = [s] (Queue containing just s)
while Q is not empty:
u = eject(u)
for all edges (u,v) in E:
if dist(v) = infinity:
inject(Q,v)
dist(v) = dist(u) + 1
previsit(v)
for each edge(v,u) in E:
explore(u)
postvisit(v)
If
for
a > 0, b > 1,
O(nd )
T (n) =
O(nd logn)
O(nlogb a )
if d > logb a
if d = logb a
if d < lobb a
Branching Factor: a
Depth of Tree = logb n
Width of Tree: alogb n = nlogb a
visited(v) = true
Masters Theorem
then,
if not visited(u):
for j = 0 to n 1:
reachable from v
pre/post
[u[v v]u] is a Tree/Forward Edge
[v[u u]v] is a Back Edge
[v v][u u] is a Cross Edge
The node that receives the highest post number in a depth-first search
must lie in a source strongly connected component.
and
d 0,
def dijkstra(G,l,s):
for all u in V:
dist(u) = infinity
prev(u) = nil
dist(s) = 0
def dfs(G):
for all v in V:
if not visited(v):
explore(v)
dist(v) = dist(u)+l(u,v)
prev(v) = u
decreasekey(H,v)
if find(u) != find(v):
add edge {u,v} to X
union(u,v)
procedure shortest-paths(G, l, s)
Input: Directed graph G = (V, E);
edge lengths {l_e: e in E} with no negative cycles;
vertex s in V
The above algorithm utilizes disjoint sets to determine whether adding a given
edge creates a cycle. Basically by checking whether or not both sets have the same
root ancestor.
Contains a function, find that returns the root a given set. pi refers to the parent
node. rank refers to the height subtree hanging form that node (number of levels
below it).
For any x, rank(x) < rank((x)).
Any root node of rank k has at least 2k nodes in its tree.
If there are n elements overall, there can be at most nk nodes of rank k.
2
The maximum rank is logn.
dist(u) = infinity
prev(u) = nil
pi(x) = x
rank(x) = 0
dist(s) = 0
for all e in E:
update(e)
procedure prim(G, w)
Input: A connected undirected graph G = (V, E) with weights
Output: A minimum spanning tree defined by the array prev
for all u in V :
cost(u) = infinity
prev(u) = nil
Pick any initial node u_0
Greedy Algorithms
cost(u_0) = 0
H = makequeue (V) (priority queue with cost-values as keys)
Definitions
A Greedy Algorithm always takes the cheapest least weight edge for its next step,
no matter the future consequences.
A Tree is an acyclic, undirected, connected graph with |V | 1 edges (or for MST
exactly |V | 1 edges).
A Fringe is all the vertices exactly one hop from you current vertex.
while x != pi(x):
x=pi(x)
return x
Cut Property
Suppose edges X are part of a minimum spanning tree of G = (V, E). Pick any
subset of nodes S for which X does not cross between S and V-S, and let e be the
lightest edge across the partition. Then X e is part of some Minimum Spanning
Tree.
v = deletemin(H)
for each {v, z} in E:
if cost(z) > w(v, z):
cost(z) = w(v, z)
prev(z) = v
decreasekey(H, z)
Path Compression
function find(x):
if x != pi(x): pi(x) = find(pi(x))
return pi(x)
Using path compression allows for an amortized cost of O(1) for out Disjoint Sets
union(x, y) and f ind(x) operations.
Union Find
Uses Disjoint Sets Data Structure
Runs in per operation Olog n which is the number of times you can take a log of
n before it becomes 1 or less. It is very slow and for all practical cases is constant.
Basically if find(x) and find(y) return the same value they are in the same graph so
do nothing, else add the edge. Then union(x, y).
Union: Worst case is O(logN ) Avg for all Ops is O(nlog n) where n is number
of elements in Data Structure.
Human Encoding
A means to encode data using the optimal number of bits for each character given
a distribution.
2 |)
func huffman(f):
Input: An array f[1...n] of frequencies
makeset(u)
X = {}
Sort the edges E by weight
for all edges {u,v} in E, in increasing order of weight:
i=deletemin(H), j=deletemin(H)
Repeat until all elements of B are covered:
Pick the set Si with the largest number of
uncovered elements.
Dynamic Programming
Fundamentally DP is carefully bruteforcing the solutions to a problem by turning it
into smaller and smaller nested subproblems that remember useful information
about its bigger or parent subproblem so that it can eventually reconstruct itself to
solve the original problem in a reasonable amount of time. This remembrance is
o en done using memoization or parent pointers.
Dynamic Programming has two approaches, which both have the same
asymptotic runtime (dier by a constant):
Parenthesization O(n3 )
fib = []
fib(n):
for k in range(1, n):
Floyd-Warshall O(|V |3 )
if k <= 2: f = 2
else: f = fib[k-1] + fib[n-2]
fib[k] = f
return fib[n]
for i=1 to n:
Shortest Paths (V E)
for j=1 to n:
1. Top Down: The top down approach uses the recursive idea of breaking the
problem into trivially (but still helpful) smaller subproblems and finding a
way (through brute force and memorization) to find the maximum or
minimum of every permutation what is be le with ever permutation of
the subproblems. This is unlike Divide and Conquer which garners its
eiciency from reducing its subproblems to massively smaller problems.
For DAGs: For a shortest path (s,v) that uses a limit of, guess take the min of the
incoming edge weights into v, say from a node u and then add it to the prefix
subproblem of shortest path from (s, u).
For General: Sk (s, v) = weight of shortest path from s to v that uses k
edges. Sk (s, v) = min(u,v)inE (Sk1 (s, u) + w(u, v)
This is Bellman-Ford.
Please note that subsequences are any sequences found in another sequences
that are not necessarily next to each other (contiguous). Contiguous subsequences
are substrings. The following algorithm starts at one side of the list and finds the
max length of sequences terminating at that given node, recursively following
backlinks. Then given all the lengths of paths terminating at that given node
choose the max length. Without memoization, this solution would be exponential
time.
dist(i,j,0) = infinity
for all (i,j) in E:
dist(i,j,0) = l(i,j)
for k = 1 to n:
for i = 1 to n:
for j = 1 to n:
dist(i,j,k) = min{dist(i,k,k-1)+
dist(k,j,k-1),
dist(i,j,k-1)}
L = {}
C(S,1) = infinity
for j=1,2,...,n:
L[j] = 1+max{L[i]:(i,j) in E}
# The (i,j) represents all the edges that go from
# a node to j.
return max(L)
Linear Programming
Feed into a LP solver like Simplex an Objective Function which states if you want to
maximize or minimize the equation (max(x + 2y)), Constraints which are
limitations for the variables of the Objective Function (x 0, y 600).
This algorithm works by basically choosing the min of the options for every given
letter. (The 3 options being adding a gap inbetween letters of one of the strings or
matching the two letters and moving on.)
ex) Snowy and Sunny have an edit distance of 3 with this configuration
S _ N O W Y
for i = 0,1,2,...,m:
for j = 1,2,...,n:
Fibonacci O(n)
Used for finding shortest paths in a weighted graph with positive or negative edge
weights (but with no negative cycles/
S U N N _ Y
E(i,0) = i
E(0,j) = j
for i = 1,2,...,m:
Simplex
for j = 1,2,...,n:
E(i,j) = min{E(i-1,j)+1,E(i,j-1)+1,E(i-1,j-1)
+diff(i,j)}
Recursive:
return E(m,n)
memo = {}
Knapsack O(nW )
fib(n):
if n in memo: return memo[n]
if n <= 2: f = 2
else: f = fib(n-1) + fib(n-2)
memo[n] = f
return f
Iterative:
set v = v
Items have a weight and a value, you want to maximize the value within a given
weight. (The amount you can carry in your knapsack)
With repetition:
K(, j) = maxavailable
items
return v
Max Flow
Construct graph G that is a simple directed graph with source s and sink t. No
negative capacities are possible.
Construct a Residual Graph with forward edges for the amount of capacity that is
not being used (capacity - current use) and a back edge for what is currently being
used.
Ford-Fulkerson
Start with no flow on your graph. Find a path using DFS. Then create a residual
graph. We can now use DFS for finding a path from s to t in the residual graph. If
one exists we are not optimally using our flow. We then find the edge with the
LEAST capacity edge - this is our bottleneck - and add flow onto all the edges in
that path up to the capacity that is not being used by the bottleneck edge, hereby
maximizing the flow of the path. Our new flow is guaranteed to be better. Create a
new residual graph and repeat until no path in the residual graph can be found
from s to t. This will happen when capacity = current use, as we lose our forward
edge and only have a back edge.
This algorithm will sometimes decrease flow in one area to increase flow in
another. Max flow is the total weights of incoming edges to the sink. Runtime
would be O(E M ), where M is the number of iterations and E is the time to
find a path. This can also be stated as O(maxflowE). However this may not
terminate. If you use BFS instead of DFS you are using the Edmonds-Karp
Algorithm which will terminate and has complexity O(V (E)2 ), which is better
for large networks.
Rudrata/Hamiltonian Path
Independent Set
Given a path starting at s and ending at t that goes through each vertex exactly
once.
Given a graph and a number g , the aim is to find g vertices that are independent
meaning that no two of which have share an edge.
Graph 3-Coloring
Given an undirected graph G = (V,E) find a valid 3-coloring C such that no two
vertices sharing the same edge have the same color, or report that such an
ordering doesnt exist.
3SAT
Traveling Salesman Problem
Longest Path
3D Matching
Knapsack
Independent Set
Integer Linear Programming
Rudrata Path
Balanced Cut
Bipartite Matching
Explained by Example: list of boys, list of girls, if boy likes girl, a direct edge exists
from boy to girl. Is there a perfect matching? Create source node s and sink node t,
s has outgoing edges to all the boys, and t has incoming edges from all the girls.
Give every edge a capacity of one (obviously). A flow exists if there is a flow into t
with size equal to number of couples.
Computational Complexity
We use Computational Complexity to determine classifications for our algorithms
to know if they are feasible.
Classifications
P: The set of all search problems that are solvable in a reasonable amount
of time (Polynomial time).
NP (Nondeterministic Polynomial): The set of all search problems whose
solution can be checked in Polynomial time (includes P) - there might exist
search problems whose solutions can not be checked in Polynomial time.
A solution may not necessary be found in a reasonable amount of time
(2n and n! algorithms can be in NP). Called NP because if you had the
power to guess correct every time it would work in Polynomial time,
making it non-deterministic. Should be called Guessing in P.
Satisfiability (SAT)
This is the prototypical NP-Complete problem that everything started from. Say
you have some Boolean expressions written using only AND, OR, NOT, variables,
and parentheses (Example: x1 x2 x3 ). The SAT problem is given any one of
these expressions, is there some assignment of TRUE and FALSE values to the
variables that will make the entire expression TRUE?
3SAT
This is a stricter version of the SAT problem in which the statement is divided into
clauses where each clause can have exactly 3 literals. (Example:
(x1 x2 x3 ) (x4 x5 x6 )). For these you want to find whether there
exists values for x1 ...x6 such that the boolean evaluates to TRUE.
CircuitSAT
Given a circuit of logic gates with a single output and no loops find there a setting
of the inputs that causes the circuit to output 1.
Rudrata/Hamiltonian Cycle
Given a graph find if there a cycle that passes through each vertex exactly once, or
report one doesnt exist.
Reductions
Reductions are an incredible useful tool for either turning a search problem we
dont know how to solve into one we do, or proving that a search problem can not
be solved or is hard to solve by reducing it to problem that is one of those two
things.
- We know how to solve B in a reasonable amount of time and we want to use this
knowledge to solve A.
- We denote a reduction from A to B as A B . Diicultly flows in the direction
of the arrow.
- If we can reduce our unknown problem A into a known problem B then B must
be as hard if not even harder to solve than A. A way to mathematically write this is
A p B . A thereby provides a lower bound of hardness for B .
- Reduction can be composed as well: if you can reduce A B and B C
then A C .
- Any search problem in NP can be reduced to an NP-Hard Problem in Polynomial
Time but this is not always useful (like Halting Problem)
- Any search problem in NP can also be reduced to an NP-Complete Problem in
Polynomial Time.
- Any problem that is NP-Complete is reducible to any other problem that is also
NP-Complete which is very useful.
Reduction Tree
disregard the brute force calculation of the longer path, since it wont be better
than what you already have.
Finding the lowerbound is the really tricky part.
Start with some problem P0
Let S = {P0}, the set of active subproblems
bestsofar = infinity
Repeat while S is nonempty:
choose a subproblem (partial solution)
P in S and remove it from S
expand it into smaller subproblems P_1, P_2, ..., P_k
For each P_i:
If P_i is a complete solution: update bestsofar
else if lowerbound(Pi) < bestsofar: add Pi to S
return bestsofar
K Nearest Neighbors
We are working in a multidimensional space, with a dimension for each feature
index. We then take all of our points and store them in some data structure that
allows us to query for the nearest neighbors to an arbitrary point. The way the
classification works is given a new point, we plot it, and get the K nearest
neighbors to it. (Those neighbors are the test points we used to train the classifier.)
Once we get those neighbors, we take a majority vote on their labels and figure out
what this new unseen point actually is.
A small example: Imagine you are applying to a credit card. They ask for you age,
income, and number of credit cards. From this, they create a 3 dimensional feature:
age, income, count. The company takes your data and checks their classifier. The
classifier plots your point and sees that you fall right in the middle of a cluster of
people who always pay their bills on time. You are then judged as a good client.
This is our classify algorithm:
Runs in (nd)
def Classify(x):
Approximation Ratio
set i* = 1
A(I)
OP T (I)
A(I)
Vertex Cover Input: An undirected graph G = (V, E). Output: A subset of the vertices
S contained in V that touches every edge. Goal: Minimize |S|.
Special Case of Set Cover, works in (logn)
Use big data set to train a classifier, which can then categorize new data. Data
partitions: take 80% of data and use it as training, ie, feed into classifier both the
data and expected result. use 20
Data is encoded as features, which is usually a vector of numbers, o en
normalized to floats between 0.0 and 1.0, These features are used to train the
classifier.
for i - 2, 3,..., n:
if ||x-x_i|| < ||x-x_i*||, set i* = i
return y_i*