Understanding BlockDAGs and GHOSTDAG
My WebsiteSupport My Work
  • Welcome!
  • Preface
  • Introduction
  • Part 1: BlockChains and BlockDAGs
    • Introduction
    • Chapter 1: From BFT to PoW
      • Byzantine Fault Tolerance
      • Proof-of-Work (PoW)
      • How PoW Works*
      • PoS Vs. PoW
      • Exercises
    • Chapter 2: the Block Chain Paradigm
      • A Graph Theory Primer
      • The Paradigm
      • Honesty and Rationality
      • Three Chain Selection Rules
      • Security Notions
      • A Security Model for Blockchain Consensus
      • Selfish Mining in Bitcoin
      • Safety
      • Liveness
      • Confirmation Times
      • Proving Bitcoin's Security*
      • Exercises
  • Part 2: The GHOSTDAG Protocol
    • Page 2
  • Supplementary Material
    • Math
      • Stuff You Should Know
        • Asymptotics, Growth, and Decay
        • Binomial Coefficients
        • The Binomial Formula
        • Geometric Sums and Series
      • Probability Theory
        • Probability Spaces
        • Random Variables
        • The Math of Block Creation
    • Computer Science
      • Stuff you Should Know
        • Asymptotic Notation
        • Negligible Functions
      • Cryptography
        • Random Oracles
        • Merkle Trees
        • One Time Pads
Powered by GitBook
On this page
  • Asymptotic Notation
  • Exponentials
  • Logarithms
  • Negligible Functions
  1. Supplementary Material
  2. Math
  3. Stuff You Should Know

Asymptotics, Growth, and Decay

PreviousStuff You Should KnowNextBinomial Coefficients

Last updated 3 months ago

When considering stuff like the security and efficiency of algorithms and procedures, and in particular cryptographic schemes and block chains, we need a way to relate them to the various parameters. Consider for example a procedure that sorts an array: it is given a list of numbers, and returns them in increasing order. How can we describe "how fast" that procedure is? We can ask how fast it takes to sort ten numbers, or twenty, but that won't tell us much if in the future we want to use it to sort 1357 or 20545 numbers. We need some way to talk about how the length of the computation grows.

For this we use a function fff that is given the input length nnn and outputs "how long" it would take to sort an array of this size.

Now, the term "how long" is also ill conceived. How do we count? In seconds? CPU cycles? Don't all such metrics depend on the computer running it? Can't they be different for two different lists with the same length?

The answer to all of that is yes. The whole point of discussing growth is to put that aside, and just look at the trajectory. This is often called the asymptotics of the function.

It is true that the asymptotics doesn't have all the information, but it tells us what happens in the long run. That "other part", that doesn't tell us about the rate of growth, but gives us concrete numbers (which is often just referred to as "the constants"), is also very important. But from some vantages, like the one permeating most of this book, the asymptotic point of view is the appropriate one.

Asymptotic Notation

To streamline the notations, computer scientists came up with a bit confusing yet very powerful notational system that captures the "type of growth" of the function while ignoring everything else.

Say that fff is some function that interests us, like the runtime of a sorting algorithm given the length nnn of the list it needs to sort. As we said, fff is quite complicated and not even completely defined. However, we can say something like fff grows "at most linearly". What do we mean by that? That while fff might be a bit erratic and unexpected, we know that there is a function that looks like a line such that fff is almost always below it. This would look something like this:

We use the notation f=O(n)f=O(n)f=O(n) to say that there is some line such that fff is below it from some point onward. In other words, we say that f=O(n)f=O(n)f=O(n) if there exist two numbers mmm and bbb such that for any sufficiently large nnn we have that f(n)<mn+bf(n) < mn + bf(n)<mn+b. Note that the notation f=O(n)f=O(n)f=O(n) hides the actual numbers m,bm,bm,b and only tells us they exist. This is the power of asymptotic notation, it hides the constants and allows us to qualitatively talk about the growth trend. So yeah, the constants may change based on the exact architecture of the system that will eventually run the code, but for any system there will be some constants fitting the bill, and that is what captured by this notation.

This is often called the big oh notation.

Exercise: Prove that n2≠O(n)n^2 \ne O(n)n2=O(n)

Solution

Say that n2=O(n)n^2 = O(n)n2=O(n), then there are some mmm and bbb and NNN such that for any n>Nn>Nn>N we have that n2<mn+bn^2 < mn+bn2<mn+b. Dividing both sides by nnn we get that n<m+bnn < m + \frac{b}{n}n<m+nb​. However, for n>bn>bn>b we have that m+bn<m+1m+\frac{b}{n} < m+1m+nb​<m+1, but m+1m+1m+1 is a constant so we can't have that n<m+1n<m+1n<m+1 for all sufficiently large nnn.

More generally, given two functions fff and ggg we can denote f=O(g)f=O(g)f=O(g) to mean that there are some constants mmm and bbb such that, for sufficiently large nnn, it holds that f(n)≤m⋅g(n)+bf(n)\le m\cdot g(n) + bf(n)≤m⋅g(n)+b. This allows us to write stuff like f=O(n2)f = O(n^2)f=O(n2) to say "look, I can't tell you exactly how fff grows, but the rate at which it grows is at most quadratic, if you put in an input three times longer, the growth in runtime is at most by a factor of three squared, that is nine".

Exercise: Prove that n2=O(n3)n^2 = O(n^3)n2=O(n3). More generally, prove that if aaa and bbb are natural numbers then na=O(nb)n^a = O(n^b)na=O(nb) if and only if a≤ba\le ba≤b.

Like the big oh notation helps us bound things from above, we have the big omega notation to bound things from below. By writing that f(n)=Ω(n)f(n) = \Omega(n)f(n)=Ω(n) we say that fff grows at least linearly, but it could actually grow faster, just like a sublinear function is still O(n)O(n)O(n).

Exercise: Write a formal definition for f=Ω(g)f=\Omega(g)f=Ω(g)

Solution

There are some constants mmm and bbb such that, for sufficiently large nnn, it holds that f(n)≥m⋅g(n)+bf(n)\ge m\cdot g(n) + bf(n)≥m⋅g(n)+b.

Finally, we have the theta notation, which we use to say that we know exactly what the asymptotics are. In brief, we write that f=θ(g)f = \theta(g)f=θ(g) if it holds that both f=O(g)f = O(g)f=O(g) and f=Ω(g)f =\Omega(g)f=Ω(g). The notation f=θ(g)f=\theta(g)f=θ(g) essentially means "we might not know what fff is, but as long as we talk about growth rate, we can just pretend that it is ggg". Typically, ggg will be much simpler than fff. For example, fff could be the running time of some complex algorithm, or the probability to revert a transaction in some concrete protocol, and instead of computing fff explicitly we could just prove that, say, f=θ(n2)f = \theta(n^2)f=θ(n2).

Exercise: A function fff is called bounded if there is some BBB such that f(n)<Bf(n) < Bf(n)<B for all nnn. Let fff be bounded and let ggg be any function, prove that f⋅g=θ(g)f\cdot g = \theta(g)f⋅g=θ(g).

Exercise: Find a function fff that is not linear, but such that f=θ(n)f=\theta(n)f=θ(n).

Sometimes we don't care about growth but about decay. That is, we want to know how small fff gets as nnn increases. For example, fff could be the probability that an attacker manages to break your encryption as a function of the length of your secret-key. We can do it using the same notation. For example, f(n)=O(1/n)f(n) = O(1/n)f(n)=O(1/n) is what we mean when we say that a function decays linearly.

Exponentials

Exponentials and logarithms are how we typically describe things that are large or small. Note that I did not end the latest sentence with "respectively", and this is no coincidence. When we talk about growth (that is, how fast things go to infinity), exponential growth is large, while logarithmic growth is very slow (and in between we have e.g. the polynomial growth: linear, quadratic, quartic, etc...). When we talk about decay (that is, how fast things go to zero), then exponential decay is small.

As an example, consider generating a secret-key. In any encryption scheme, a secret-key can be represented as a uniformly random string of bits, where the length is chosen by the user. Say we use a key of length nnn, then how many possible keys are there? The first bit is either 000 or 111, so two options. The second bit is either 000 or 111 too, so two more options on top of each of these two options, leading to four options, and so on. It is easy to get convinced that the number of possible keys is 2n2^n2n. Hence, the number of keys grows exponentially with nnn.

Now say I am trying to guess your secret key. Since all the bits are uniformly random, we get that they are all equally likely, so that the probability I guess correctly (no matter how I guess) is one in 2n2^n2n, that is, 1/2n1/2^n1/2n which is also commonly written as 2−n2^{-n}2−n. So since the possible number of keys grows exponentially (and the keys are uniform), then the probability to guess a key decays exponentially.

Exercise: Let b>1b>1b>1, let fff satisfy that there is some c>0c>0c>0 such that f=θ(bcn)f=\theta\left(b^{cn}\right)f=θ(bcn), prove that fff is exponential.

Logarithms

Negligible Functions

To be more concrete, we say that fff is exponential if there is some constant c>0c>0c>0 such that f=θ(ecn)f=\theta(e^{cn})f=θ(ecn) where eee is the infamous . Why eee and not any other number? Because it is comfortable. The fact is that for any number bbb there is a number ccc such that bn=ecnb^n = e^{cn}bn=ecn. (This number ccc is called the natural logarithm of bbb, denoted ln⁡b\ln blnb, and we will discuss it shortly.) So you can replace eee with any positive number larger than 111 and obtain a completely equivalent definition.

Euler's number
Illustration of f=O(n)f=O(n)f=O(n), the red line is the function 2n+62n+62n+6