Lightning Network is a payment protocol that operates on the top of the Bitcoin network (“Layer 2” solution). It’s a network of decentralised nodes that allows for sending a huge number of transactions exteremly fast and for a negligible fee (even sub-satoshi level). The Lightning Network facilitates micropayments, making the existence of a completely new type of applications possible.

To illustrate how the Lightning Network works, let’s imagine ordering drinks in the pub. The first option is to pay for every single drink separately, this means you have to pay each time you want to order a drink. Each of these transactions will be settled with the pub separately. If we imagine these are Bitcoin transactions, each of them would have to be settled (mined) on the Bitcoin blockchain.

The second option is to open a bar tab. Now, you can order as many drinks as you want without having to settle each transaction separately. At the end, you can just pay once, settling the whole bill in one transaction. This is similar approach to how the Lightning Network works and it’s consider a Layer 2 solution as not every transaction is settled on the blockchain.

Not settling every transaction on the blockchain allows for achieving a dramatic increase in both transaction speed and throughput and makes Bitcoin scalable to the level necessary to become a viable mean of payment for the whole world.

The Lightning Network was first described by Joseph Poon and Thaddeus Dryja in February 2015. You can find a link to the whitepaper here/below.

Lightning Network is built on the concept of payment channels that can be established between two parties. We are going to dive into it later in this post. Before we do that let’s understand why do we even need the Lightning Network in the first place.

Okay, this all sounds really cool, but why does Bitcoin even need the Lightning Network?

The quick answer is – scaling. Concerns about Bitcoin’s scaling problems were raised multiple times and they became obvious during the end of 2017 bull run. In Bitcoin, every single transaction has to be broadcast to every single node in the network. Broadcasted transactions have to be included in a block to be mined and settled on the blockchain. As Bitcoin’s blocks are created every 10 minutes on average and each block has a limited size, it creates a natural bottleneck on the number of transactions that can be processed.

When it comes to fixing Bitcoin’s scalability problems, developers came up with a few different approaches, mainly increasing the block size (Bitcoin Cash approach) or moving part of the transactions off-chain (Bitcoin approach). Both of the approaches come with their own tradeoffs, but the Lightning Network tries to increase the network throughput and speed by processing some of the transactions in the second layer.

Currently,

Now as we went through the necessary overview, let’s get into the nitty-gritty details of the Lightning Network. We cannot talk about the Lightning Network without mentioning payment channels which are the basic building blocks of the network. Payment channels are examples of state channels which represent an off-chain state alteration which is secured by eventual settlement on the blockchain.

In order to open a new payment channel, two parties – Alice and Bob, have to commit funds to a 2-of-2

After the channel is established, two parties can start signing and exchanging transactions (called commitment transactions) that alter the initial state and are not broadcast to the Bitcoin network. This allows for exchanging thousands of transaction without having to interact with the underlying Bitcoin network and makes the whole solution scalable.

When Alice and Bob at some point decide they want to close their payment channel, they can sign a closing transaction. This transaction will be mined and it will resolve the final balance between the two parties. In the whole lifetime of a payment channel, there are only two transactions that have to be mined and these are the opening and the closing transactions.

The Lightning Network is built on the concept of multiple payment channels connected end-to-end that allow for routing transactions to any of the network participants. In the Lightning Network, a transaction can be sent through the network of payment channels without trusting any of the intermediaries leveraging a mechanism called Hash Time Lock Contract (HTLC).

Every time a node wants to send a payment to another node, it must first construct a path. It does so by connecting channels with enough capacity using possibly the shortest route. It also takes into account fees of the intermediate nodes.

The communication between LN nodes is encrypted point-to-point. The Lightning Network achieves that by implementing an onion-routed protocol (similar to TOR) which ensures that intermediate nodes can verify and decrypt only their portion of the route and they cannot identify the length of the payment path or their own position in the path.

To deal with the bad actors the Lightning Network leverages a lot of game theoretical elements that incentivise nodes to act honestly. These are mostly timelocks, asymmetric revocable commitments and HTLC.

This was only a quick overview of the payment channels and we are going to cover more details in the following post.

The beauty of Bitcoin and the Lightning Network is that it can not only provide a better alternative to the already existing systems but also facilitates the creation of the completely new types of applications.

Have you ever realised you kept paying a subscription fee, even though you barely used a service? What if the service instead of a monthly fee was charging you by a minute or maybe even by a second? A good example would be a movie streaming service which actually charges you a small fee for every second of watched content. This type of business is currently not a viable option due to high fees for processing card transactions.

How about receiving your salary every hour (or even every minute) instead of once or twice per month. This is where the Time Value of Money comes to play. With the

Lightning Network although mostly discussed as a Bitcoin Lightning Network is not only limited to Bitcoin. In fact, most of the cryptocurrencies supporting basic constructs like timelocks or HTLC can make use of the Lightning Network to exchange value off-chain. There is also a possibility of exchanging one coin to another via Lightning Network in a process called atomic swap. This concept can power Decentralized Exchanges and allow people to exchange coins without losing control of their private keys.

As the Lightning Network continues to grow at a very high rate, it’s important to make sure that different implementations can communicate with each other. To achieve that, the community came up with a BOLT (Basis of Lightning Network) standard which specifies guidelines which allow for seamless interoperability between different implementations. This also allows different teams to work on their own Lightning Network implementation at their own pace which speeds up innovation. The most popular implementations with the highest number of developers are c-lightning, LND and Eclair.

When it comes to wallet apps supporting Lightning Network there are already a few good solution, but we have to remember that most of them are still in beta stage and they can contain bugs, so it might not be the best idea to deposit a lot of money into them. The most popular wallets are Blue Wallet, Wallet of Satoshi, and Zap.

As we mentioned before, the Lightning Network grows at a very hight rate. These are some of the current network statistics:

Number of nodes: 7991 (+8.35%)

Number of channels: 38707 (+2.3%)

Network capacity: 1066 BTC (+9%)

If you’re curious about more Lightning Network stats, check a link to a website that tracks them in the description box below.

The Lightning Network is currently one of the most exciting projects in the crypto industry and it has a big chance of bringing a scalable second layer solution to Bitcoin which will enable micropayments and create a new way of building financial applications.

We need to remember that the Lightning Network is still a very new technology and despite its potential, it is also facing a lot of challenges. Some of them are: lack

The Lightning Network recently started attracting more and more attention from the broader community with people like Jack Dorsey (Twitter CEO) endorsing the technology.

Lightning Network Whitepaper ► https://lightning.network/lightning-network-paper.pdf

Mastering Bitcoin by Andreas M. Antonopoulos ► https://amzn.to/2Lr4iS0 (affiliate)

]]>The Two Generals’ Problem, also known as the Two Generals’ Paradox or the Two Armies Problem, is a classic computer science and computer communication thought experiment that we’re going to talk about in this post.

First of all, to avoid any confusion, we need to remember that the Two Generals’ Problem, although related to the Byzantine Generals’ Problem is not the same. Byzantine Generals’ Problem is a more general version of the Two Generals’ Problem and it’s often discussed when talking about distributed systems, fault tolerance and blockchain. We’ll be talking about it in the following post.

But now let’s move to the story of the two generals.

Let’s imagine two armies, led by two generals, planning an attack on a common enemy. The enemy’s city is in a valley and has a strong defence that can easily fight off a single army. The two generals have to communicate with each other to plan a synchronised attack as this is their only chance to win. The only problem is that to communicate with each other they have to send a messenger across the enemy’s territory. If a messenger is captured the message he’s carrying is lost. Also, each general wants to know that the other general knows when to attack. Otherwise, a general wouldn’t be sure if he’s attacking alone and as we know attacking alone is rather pointless.

Now, let’s go through a simple scenario. Let’s call our generals A and B and let’s assume everything goes perfectly fine. General A, who is the leader, sends a message – “Attack tomorrow at dawn”. General B receives a message and sends back an acknowledgement – “I confirm, attack tomorrow at dawn”. A receives B’s confirmation. Is this enough to form a consensus between the generals? Unfortunately not, as General B still doesn’t know if his confirmation was received by General A. Ok, so what if General A confirms General’s B confirmation? Then, of course, that confirmation has to be also confirmed and we end up with an infinite exchange of confirmations.

In the second scenario, let’s also assume that General A sends a message to General B. Some time has passed and General A starts wondering what happened to his message as there is no confirmation coming back from General B. There are two possibilities here. Either the messenger sent by General A has been captured and hasn’t delivered a message or maybe B’s messenger carrying B’s confirmation has been captured. In both scenarios, they cannot come to a consensus again as A is not able to tell if his message was lost or if it was B’s confirmation that didn’t get through. Again, we ended up in an inconsistent state which would result in either General A or B attacking by himself.

We can quickly realise that no matter how many different scenarios we try and how many messages we send we cannot guarantee that consensus is reached and each general is certain that his ally will attack at the same time. To make it even worse, there is no solution to the Two Generals’ Problem, so the problem remains unsolvable.

I hope you can clearly see an analogy to computers’ communication here.

Instead of two generals, let’s imagine two computer systems talking to each other. The main problem here is again the untrusted communication channel and inconsistent state between two machines. A very common example that always comes up when talking about the Two Generals’ Problem is the TCP protocol.

As we probably know, TCP uses a mechanism called 4-way handshake to terminate the connection. In this mechanism, a system that wants to terminate a connection sends a FIN message. The system on the other side of the communication channel replies with an ACK and sends its own FIN message which is followed by another ACK from the system which initialised termination. When all of those messages are received correctly, both sides know that the connection is terminated. So far it looks ok, but the problem here is again the shared knowledge between the two systems. When, for example, the second FIN is lost we end up with a half-open connection where one side is not aware that the connection has been closed. That’s why even though TCP is very reliable protocol it doesn’t solve the Two Generals’ Problem.

I’m happy you’re not giving up. Unsurprisingly, there was a number of people trying to solve unsolvable Two General’s Problem and they came up with a few practical approaches. The main assumption here is to accept the uncertainty of the communication channel and mitigate it to a sufficient degree.

Let’s go back to our generals. What if General A instead of sending only 1 messenger sends 100 of them assuming that General B will receive at least 1 message. How about marking each message with a serial number starting from 1 up to 100. General B, based on the missing numbers in the sequence, would be able to gauge how reliable the communication channel is and reply with an appropriate number of confirmations. These approaches, even though, quite expensive are helping the generals to build up their confidence and come to a consensus.

If sacrificing messengers is a problem, we can come up with yet another approach where the absence of the messengers would build up generals’ confidence. Let’s assume that it takes 20 minutes to cross the valley, deliver a message and come back. General A starts sending messengers every 20 minutes until he gets a confirmation from General B. Whenever confirmation arrives General A stops sending messengers. In the meantime, General B after sending his messenger with his confirmation awaits for the other messengers coming from General A, but this time an absence of a messenger builds up General’s B confidence as this is what the Generals agreed on.

In this case, we have a clear speed vs cost tradeoff and it’s up to us which approach is more suitable to our problem.

That’s the end of the story of the Two Generals. Time for a quick summary.

Two Generals’ Problem is a classic computer science problem that remains unsolvable.

It comes up whenever we talk about communication over an unreliable channel.

The main problem is an inconsistent state caused by lack of common knowledge

There are some pragmatic approaches to the Two Generals’ Problem.

Two Generals’ Problem was first published in 1975 in “Some Constraints and Trade-offs in the Design of Network Communications” paper and described a problem with communication between two groups of gangsters. If you want to read the original version check this link.

]]>

Actor Model is a conceptual model of concurrent computation originated in 1973. In the Actor Model, an actor is a fundamental unit of computation and everything is an actor.

The only allowed operations for an actor are:

- to create another actor,
- to send a message
- or to designate how to handle the next message

First two operations are quite easy to understand. Let’s focus on the last one. An actor can hold its own private state and it can decide how to process the next message based on that state. Let’s imagine an actor that stores a total balance of our account. If our actor receives a message with a new transaction, it updates its state by adding the new amount to the already calculated total balance which means that for the next message the state of the actor will be different.

Actors are lightweight and it is easy to create thousands or even millions of them as they require fewer resources than threads.

Let’s have a look at the actors in more details. Actors are isolated from each other and they do not share memory. They have a state, but the only way to change it is by receiving a message. Every actor has its own mailbox, which is similar to a message queue. Messages are stored in actors’ mailboxes until they are processed. Actors, after created, are waiting for messages to arrive. Actors can communicate with each other only through messages. Messages are sent to actors’ mailboxes and processed in FIFO (first in, first out) order. Messages are simple, immutable data structures that can be easily sent over the network.

Conceptually an actor can handle **only 1** message at a time. Actors are decoupled, they work asynchronously and they don’t need to wait for a response from another actor.

Actors have addresses, so it’s possible for an actor to send a message to another actor by knowing its address. An actor can **only** communicate with actors whose addresses it has. An actor has addresses of the actors it has itself created and it can obtain other addresses from a message it receives. One actor can have **many** addresses. We need to remember that address is not equal to identity, so it doesn’t mean that two actors with the same identity have the same address.

Actors can run locally or remotely on another machine. It is completely transparent for the system as actors communicate through addresses which can be local or remote.

Now, let’s look at the fault tolerance. In the Actor Model, actors can supervise other actors. An actor can supervise the actors it creates and can decide what to do in case of failure. A supervisor can, for example, restart a supervised actor or redirect messages to another actor. It leads us to self-healing systems.

Let’s have a look at the pros and cons of the Actor Model.

Pros | Cons |
---|---|

easy to scale | actors are susceptible to deadlocks |

fault tolerance | overflowing mailboxes |

geographical distribution | |

not sharing state |

We need to remember that the Actor Model is only a conceptual model and a lot of properties of your system depends on the chosen implementation. The best-known implementations of the Actor Model are Akka (Scala and Java) and Elixir (Erlang).

Akka ► https://akka.io

Elixir ► https://elixir-lang.org

If you want to learn more about the Actor Model please check this great talk by Carl Hewitt

If you want to learn more about different concurrency models including the Actor Model, I recommend the following book.

]]>Let’s start with what latency really means. A quick Google search gives us the following definition: “latency is a time interval between the stimulation and response”. If you’re still confused by this definition let’s imagine you’re playing an online computer game. If your Internet connection is slow you will experience something called ‘lag’. Let’s say you want to move your game character from one place to another, you’re clicking a mouse button and nothing is happening. You notice that there was a delay between the time you clicked the mouse button and the time your character started moving. This is what latency really is. It is the delay between the time the action is initialized and the time the action actually takes place.

We already know what latency is, but how to define low latency? First of all, we need to find out what ‘low’ really means. Human perception of latency is completely different from the machine perception. All of our reactions are roughly delayed by 300 milliseconds (if you don’t believe me click on this link). This means that everything below 300 ms is classified by our brains as real-time or 0 delay. Of course, this is completely different for the machines. For a trading system, 300ms latency would be a true nightmare.

Term ultra-low latency is used by many companies to describe sub 1ms latencies. Ultra-low latencies are very often associated with trading as this is one of the areas where speed plays the biggest role. Very often the winner takes all and the second place is worth nothing.

There are many systems where low latency plays a major role. Let’s have a look at some examples:

- trading systems (order execution, matching engines, pricing engines)
- video/audio streaming
- online games
- real-time data applications

Java, because of its virtual machine and garbage collection is very often perceived slow. Fortunately, with the right optimisations, we can make it extremely fast.

Let’s focus on the key elements influencing low latency:

- Garbage Collection. GC pauses can dramatically increase our latency, but they become manageable with the right optimisation. We can also use non-standard, low-latency JVMs like Zing from Azul.
- JIT. Compiling hot code paths to the machine code can seriously speed up our system. We need to take extra care to warm up our system.
- Locking. Using lock-free algorithms and I/O becomes crucial and has a big impact on latency.
- Classloading. All key classes should be loaded before we even start using our system.
- Data structures. We need to know what data structures are optimal for storing our data and how to make sure they’re not slowing us down. For example, using ConcurrentHashMap for a greater degree of concurrency over synchronizedMap.
- The complexity of algorithms. Our latency can be greatly reduced if the time complexity of our algorithms is poor. We need to make sure that our algorithms run at the best possible speed.

Let’s have a look at pros and cons of using Java for low latency systems:

Pros | Cons |
---|---|

Time to market | It might be still slower than C++ |

Time to stability | Finding right GC/JIT optimisation might take a while |

Team's productivity |

We need to remember that optimisation of our Java code and JVM is only a tip of the iceberg and if we want to get to sub 50ms latencies we need to look into other directions. The next section is going to shed some light on the other major elements of low latency systems.

There are many other factors influencing latency. We just described Java and JVM. Now, let’s quickly describe the other elements:

- Operating system: pinning our process to dedicated cores, page cache, power management tuning,
- Hardware: commodity hardware, FPGA,
- Internet connection: fibre or microwaves (for example for High-Frequency Trading)

When it comes to developing low latency systems there are many different aspects that we need to consider. A programming language is only one of them and Java with the right JVM configuration might be a good option, especially if we want to balance between speed and time to market. In general, low latency is a huge area to study and this article only touches the surface of this interesting topic. I will be delving into the details in the future posts.

]]>Have you ever come across a term robo-advisor? Have you ever wondering what that is? The first thing that comes to your mind is probably the “robo” bit, but if you’re imagining a human-like, metal creature you cannot be further from the truth. Robo-advisors are digital, financial advising platforms that make investment decisions without or with a minimal human intervention. They make use of algorithms to allocate assets and manage portfolios. After the 2008 financial crisis, lots of people lost trust in the financial sector and were reluctant to pay financial advisors hefty fees. This is when low-cost robo-advisors came to play and started attracting lots of customers. The other factor that drives robo-advisors is that the majority of millennials are comfortable with online tools, so they expect to have a similar experience when it comes to investing.

So how exactly those robo-advisors work? Let’s get into the details.

Robo-advisors collect client’s personal information, risk tolerance and investment goals. Based on that, they choose an appropriate portfolio structure. Most robo-advisors make use of Modern Portfolio Theory to allocate money between different types of assets like stocks and bonds maximising returns while minimising risk. Instead of investing money in particular stocks or bonds, robo-advisors prefer to choose exchange-traded funds (ETFs) to diversify their portfolios even further. Majority of robo-advisors make use of tax-loss harvesting to optimise customers’ taxes. The first thing that a tech-savvy person might notice is the fact that robo-advisors are not that clever. They allocate money based on predefined rules and rebalance your portfolio to bring it back to the base level if there is too much money allocated to one type of assets. Let’s look at this example. Let’s assume you are a fairly young investor and a robo-advisor decides to put 90% of your cash into a well-diversified portfolio of stocks and 10% of your cash into government bonds. If the value of your stock allocation rises to let’s say 95% of the value of your total portfolio a robo-advisor rebalances your portfolio by selling some of your shares and buying more bonds to bring the levels back to 90/10. It is as simple as that. It’s not rocket science at all.

Although the most popular robo-advisors work in this way, there is also a small subset of robo-advisors that make use of AI and machine learning to predict what the most profitable investments in the future will be. These types of robo-advisors are way more exciting, but they also carry a substantially higher risk. Of course, it all depends on how big chunk of our portfolio is actively managed by AI, but from my perspective, it looks like those robo-advisors start to resemble more hedge funds than anything else.

As we know from the previous chapter, not every single robo-advisor is the same and there are some substantial differences between them. Let’s have a look at some of the common types of robo-advisors:

- Standard robo-advisors – these robo-advisors work just like described in the previous section. They allocate money between stocks and bonds based on predefined rules. They facilitate passive investment strategy.
- AI-driven robo-advisor – this is the new kid on the block. One of the most popular AI-driven robo-advisor is Responsive that rebalances your portfolio automatically as the economy changes.
- Theme based investing – Motif is a good example of a robo-advisor which allows its customers to have a greater control over where their money goes. Investors can choose from different theme based investments in their Impact Portfolio. For example, Sustainable Planet, Fair Labour or Good Corporate Behaviour.

Like with everything there are always pros and cons. Let’s start with the pros:

- lower management fees – robo-advisors offer lower management fees when compared to the traditional financial advisors. The reason for that is quite simple. There is a minimal or no human time required to manage your portfolio and you don’t need to pay machines (besides paying for electricity, space, maintenance and developers’ salaries…)
- low barrier to entry – some robo-advisors can look after your portfolio from as low as $1 which is particularly beneficial for millennials who are, as we know, not very keen on saving money.
- automated process – your portfolio is managed online, your balance and chosen investments are visible all the time and you don’t need to waste time meeting your financial advisor.

The cons:

- lack of human interaction – for some people, especially the older generation, it is a disadvantage.
- managed portfolios are less personalized – most robo-advisors allocate your money based on the personal information and risk tolerance, but they do not treat customers individually like the real financial advisors. This might change with the help of AI.
- lack of active investment – your portfolio is well diversified and passive. There is no space for excitement and some people find it very boring, but as George Soros said: “Good investing is boring”.

Globally, there are over 300 robo-advisors looking after people’s money. The US takes the sheer amount of that market with over 200 of them. The biggest robo-advisors in the US are Betterment and Wealthfront. In the UK the market leaders are Nutmeg, Moneyfarm and Wealthify. Also, some bigger players in the investment business started looking into the robo-advisors space and came up with their own solutions or acquired some already existing companies. Robo-advisors industry is expanding rapidly and its asset under management (AUM) is expected to grow to stunning $4.6 trillion by 2022 (prediction by BI Intelligence).

Robo-advisors seem to be a good solution for someone who’s looking for a passive investment strategy and don’t want to necessarily go into the details of their investments. They definitely fill up the gap in the industry. From the technology point of view, it seems like robo-advisors were inevitable and I’m not surprised they are taking more and more of the financial advising business. In the future, we can only expect more solutions like that and hopefully that will be beneficial for the consumers. I’m also expecting a rise of AI-driven robo-advisors which may dominate the wealth management space.

]]>Let’s imagine you just wrote a new search algorithm. How would you measure its performance? How would you compare it with other already existing algorithms? One of the most important elements of every algorithm is its time complexity. Measuring time complexity allows us to predict how long it takes for an algorithm to complete its execution and this is crucial for every algorithm that has changeable input. To be able to compare different algorithms we use asymptotic notation. Big O, Big Ω and Big Θ are all examples of asymptotic notation, they belong to the family of Bachmann-Landua notations and they can precisely describe the complexity of algorithms. Although all three previously mentioned notations are accurate ways of describing algorithms, software developers tend to use only Big O notation.

There is also a difference between what academics and non-academics mean by Big O notation. In the academic environment Big O puts an upper bound on the algorithm. This means that an algorithm with an average time complexity of n can be described by Big O notation as \(O(n^2)\) as the algorithm is not slower than \(n^2\) (and it’s also not slower than \(n log n\)). Software developers however, tend to use the tightest bound possible when it comes to Big O notation. When your fellow programmer tells you that his algorithm has O(n) complexity it means that on average it executes in linear time not in logarithmic or constant time. In the academic setup that tight bound is described by Big Θ.

I know it’s a little bit confusing, but for some reason software developers use Big O as if it was Big Θ. It might be too late to change it now so I think we should just accept this discrepancy. It’s also worth mentioning that very often programmers just drop the *“O”* and just say that, for example, their algorithm has \(n^2\) complexity (in most cases they just mean \(O(n^2)\)).

As we mentioned before Big O is a tech industry standard for describing the complexity of algorithms. Big O notation can be used to describe both time and space complexity. In this article we’ll focus on time complexity, but it’s also important to understand space complexity. Time complexity describes how the runtime scales regarding the input parameters. On the other hand, space complexity describes how memory scales regarding the input parameter. It is useful to determine how much memory is needed for running our algorithms smoothly.

In Big O notation, the complexity of algorithms is described using well known mathematical functions. The most common ones are constant, logarithmic, linear, quadratic, exponential and factorial functions. Big O simplifies more complicated functions to show the growth rate of a function rather than every detail of it.

**Average, best and worst cases**

Big O notation can be used to describe best, worst and average case. In most cases, we just focus on the average and worst scenario as best execution is not that relevant. Let’s use bubble sort as an example. As we know bubble sort is a simple sorting algorithm which compares each pair of adjacent items and swaps them if they are in the wrong order. We can easily imagine a situation when our input array is already sorted. In that situation, our bubble sort goes through our input array only once as there are no swaps required. It means that it’s best time complexity is O(n). What’s the probability of this scenario? From basic combinatorics we can calculate that it’s \(1/n!\) If we have an array with just 10 random elements our chances are \(1/3628800\). That’s why we almost never focus on the best performance. What matters in most cases is the average and worst-case performance. Although the worst case might be also very unlikely, it’s important to be aware of it as it might degenerate our algorithm and cause huge trouble (going from O(log n) to O(n) for big enough input may freeze the whole system for a while). This is exactly why we focus mostly on the average and worst cases and not on the best one.

Now let’s look at common ways of simplifying Big O notation.

**Dropping constants**

When using Big O and other asymptotic notations it’s standard practice to drop all the constants. What’s the reason for this? Let’s have a look at an example. Let’s say our algorithm works in \(3n^2\) time complexity. How does our constant “3” influence our algorithm? Let’s have a look at the table below.

n | Number of Operations for 3n^2 | number of operations for n^2 |
---|---|---|

1 | 3 | 1 |

10 | 300 | 100 |

100 | 30000 | 10000 |

As we can see the constant can change the final result. However, we are interested in the rate of increase and the major factor for that increase is \(n^2\), not the constant. To simplify Big O we omit constants.

**Dropping non-dominant terms**

Dropping non-dominant terms has exactly the same purpose as dropping constants. It simplifies our function and focuses on what is important which is the rate of increase. Again, let’s have a look at a quick example: \(n^2 + n\)

n | number of operations for n^2+n | number of operations for n^2 |
---|---|---|

1 | 2 | 1 |

10 | 110 | 100 |

100 | 10100 | 10000 |

As we can see when input grows our non-dominant n term means less and less and becomes irrelevant. That’s why we can easily drop it.

**Common pitfalls**

**Different input variables**

Let’s take a function which accepts two different lists and for every element in the first list it iterates through every element of the second list.

void printAll(String[] firstNames, String[] lastNames)

Some developers might be tempted to classify this as \(O(n^2)\) which is not true. Although we iterate through the internal array the input lists are not of the same size thus the complexity is O(ab) where a is the length of the first list and b is the length of the second one. This is a very common mistake while calculating algorithm’s performance and we need to be able to avoid it.

**When to add and when to multiply**

This is going to be our last rule for calculating Big O. Let’s imagine we have two loops in our algorithm. When should we add and when should we multiply their complexity? The rule is quite simple. If your algorithm processes one loop and goes to the other one while the processing of the first one is finished we add. If for every element in the first loop the algorithm goes to every single element of the second loop we multiply. Of course adding doesn’t really bring any value as we’re going to drop constants after that, so if we have 2 loops going through n elements our complexity is still O(n).

Let’s have a look at the chart with all of our common functions plotted.

As we can see the difference between every single function is huge. Let’s have a closer look at each complexity.

O(1) means that complexity does not depend on the size of the input. No matter how big our input is it never changes the speed of our algorithm. On the chart, it’s represented as a horizontal line.

n | Number of Operations |
---|---|

1 | 5 |

10 | 5 |

100 | 5 |

Although sometimes our constant might be quite big it’s still a constant and it doesn’t grow when the input changes.

Examples:

- Searching for an element in a HashMap (although it can degenerate to O(n) if hashes are not efficient)
- Adding two integers

O(log n) algorithms grow in a logarithmic time regarding the input. What’s the base of the logarithm? The answer is that it’s not really relevant as logarithms with different bases are different by a constant factor that we drop in Big O notation anyway. O(log n) logarithms are very often based on halving problems to find a result.

n | Number of Operations |
---|---|

1 | 0 |

10 | ≈3.32 |

100 | ≈6.64 |

Examples:

- Binary search
- Insertion, deletion and search in a binary tree (although it may degenerate to O(n))

O(n) algorithms have linear growth rate which means their complexity is strictly related to the input. Let’s have a look at the table below.

n | Number of Operations |
---|---|

1 | 1 |

10 | 10 |

100 | 100 |

Examples:

- finding an element in an array by iterating through the whole array
- printing all the elements of an array

O(n*log n) is a very common complexity of algorithms. It’s especially common amongst efficient sorting algorithms.

n | Number of Operations |
---|---|

1 | 0 |

10 | ≈33.22 |

100 | ≈664.39 |

Examples:

- the average case of Quick Sort
- fast Fourier transform

\(O(n^2)\) algorithms grow in a quadratic fashion in the relation to the input. This is where our algorithms start to become very inefficient and it is a very common practice to try to reduce quadratic complexity to O(n log n) if possible.

Let’s have a look at another table. We can easily see that the rate of growth is quite fast.

n | Number of Operations |
---|---|

1 | 1 |

10 | 100 |

100 | 10000 |

Examples:

- Bubble Sort
- Insertion Sort

This is where our algorithms become really slow. Unfortunately, there is a set of problems that cannot be sped up any further and they end up having exponential time complexity.

n | Number of Operations |
---|---|

1 | 2 |

10 | 1024 |

100 | 1.267651e+30 |

Examples:

- solving the travelling salesman problem using dynamic programming

The worst of them all. These types of algorithms are incredibly slow. Let’s have a look at the main chart. The orange line shoots up dramatically.

n | Number of Operations |
---|---|

1 | 1 |

10 | 3628800 |

100 | 9.332622e+157 |

Example:

- solving travelling salesman problem by the brute force method
- generating all the possible permutations of a list

It’s all about being able to predict how well your algorithm performs when the input scales. When it comes to small numbers it may not really matter. Your bubble sort may work exactly the same as merge sort (or even faster), but when it comes to the big numbers, complexity plays a huge role. Imagine that your algorithm accepts an array as its input. Let’s also imagine that the same algorithm processes every single element of that input array in one millisecond. Let’s pass an array with 1 million elements to our algorithm and let’s assume we just wrote 2 versions of that algorithm: one which works in O(log n) time and another one which works in O(n) time. This is how long it takes for 2 versions of our algorithm to complete its execution.

O(n): n=1 000 000 ms = 1000 seconds = 16 minutes

O(log n): log(1 000 000ms) = log(1000 seconds) = 9.96 seconds

The numbers speak for themselves. There is nothing really to add here. Let me just quote a famous proverb “time is money”. It may cost our company a fortune if we decide to use inefficient algorithms.

As you can see being able to determine the speed of an algorithm is a crucial skill for a software developer (and any other technologist too). There is a huge performance difference between particular algorithms and we always need to be aware of that. I hope I convinced you in this article that the complexity of algorithms and Big O notation matter and it’s extremely important to understand them.

]]>

My favourite talk was definitely “HFT to Laplace Demon, when timed data technology curves the market” by Eric Horesnyi who shed some light on the history of HFT and offered some insights into the future of AI-driven hedge funds processing hundreds of data sources in real-time to gain a competitive advantage in trading. Needless to say, I started reading “Flash Boys” straight after the conference.

The other memorable talks were:

- “Data Hunters: The Rise of Quant Consultants” by Pierce Crosby – focused primarily on the rise of alternative data sources,
- “Social Media, Real Time AI and the search for Alpha” by Dr Jamie Allsop – Twitter as an alternative data source for generating signals and insights of the architecture of Yedup,
- “A Glimpse of Python for Finance Folk” by Dr Russel Winder and Burkhard Kloss – nicely presented how easy it is to get financial data from Yahoo Finance and plot it on the screen in Python,
- The last, but not least was an eye-opening talk on hacking and security presented by FreakyClown which included some real-life stories of physically breaking into banks and other large companies. It was definitely one of the most interesting talks at the conference.

Don’t get me wrong, the other talks were really good too, I just found these particular ones the most interesting to me.

JAX Finance is not the biggest conference (yet?) I’ve been to, but the organisation and selection of topics were great. If you’re also interested in DevOps you’ll be happy to know that JAX DevOps runs in parallel to JAX Finance and you can easily switch between talks. I went to some Docker and Kubernetes talks and I enjoyed them quite a lot. I think that software developers should all aim to have a broad knowledge and specialise in one or few key areas. The DevOps part of the conference was definitely a good opportunity to broaden my knowledge.

The conference was a huge motivation to me and it was one of the reasons why I decided to start this blog. Learning about different programming languages, algorithms, ai, low latency, electronic trading, blockchain and other code/tech/finance related stuff is pure joy to me and I hope I will be able to share it with you.

To summarise, in my opinion, JAX Finance is a great conference especially for people interested in technology and software development in the financial sector and I’m looking forward to taking part in 2018 edition.

]]>