From 20cb0c21d5a8553bc6e69483445e6404d5824700 Mon Sep 17 00:00:00 2001 From: Divlo Date: Sun, 21 May 2023 14:42:53 +0200 Subject: [PATCH] feat(posts): add `programming-challenges` --- package-lock.json | 196 ++++++++++ package.json | 3 + pages/blog/[slug].tsx | 2 + posts/clean-code.md | 21 +- posts/programming-challenges.md | 343 ++++++++++++++++++ .../big-o-chart-notations.webp | Bin 0 -> 26496 bytes styles/global.css | 2 +- utils/blog.ts | 17 +- 8 files changed, 570 insertions(+), 14 deletions(-) create mode 100644 posts/programming-challenges.md create mode 100644 public/images/posts/programming-challenges/big-o-chart-notations.webp diff --git a/package-lock.json b/package-lock.json index 39e3d47..a873f5a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "date-and-time": "3.0.0", "gray-matter": "4.0.3", "html-react-parser": "3.0.16", + "katex": "0.16.7", "next": "13.4.3", "next-mdx-remote": "4.4.1", "next-pwa": "5.6.0", @@ -27,9 +28,11 @@ "react": "18.2.0", "react-dom": "18.2.0", "read-pkg": "8.0.0", + "rehype-katex": "6.0.3", "rehype-raw": "6.1.1", "rehype-slug": "5.1.0", "remark-gfm": "3.0.1", + "remark-math": "5.1.1", "sharp": "0.32.1", "shiki": "0.14.2", "unified": "10.1.2", @@ -4372,6 +4375,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/katex": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.14.0.tgz", + "integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==" + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -11280,6 +11288,61 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "dev": true }, + "node_modules/hast-util-from-dom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz", + "integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==", + "dependencies": { + "hastscript": "^7.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-1.0.2.tgz", + "integrity": "sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^7.0.0", + "parse5": "^7.0.0", + "vfile": "^5.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-1.0.0.tgz", + "integrity": "sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-dom": "^4.0.0", + "hast-util-from-html": "^1.0.0", + "unist-util-remove-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/hast-util-from-parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", @@ -11319,6 +11382,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-is-element": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-2.1.3.tgz", + "integrity": "sha512-O1bKah6mhgEq2WtVMk+Ta5K7pPMqsBBlmzysLdcwKVrqzZQ0CHqUPiIVspNhAG1rvxpvJjtGee17XfauZYKqVA==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz", @@ -11408,6 +11484,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-text": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-3.1.2.tgz", + "integrity": "sha512-tcllLfp23dJJ+ju5wCCZHVpzsQQ43+moJbqVX3jNWPB7z/KFC4FyZD6R7y94cHL6MQ33YtMZL8Z0aIXXI4XFTw==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/unist": "^2.0.0", + "hast-util-is-element": "^2.0.0", + "unist-util-find-after": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", @@ -13030,6 +13121,29 @@ "node": ">=4.0" } }, + "node_modules/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-Xk9C6oGKRwJTfqfIbtr0Kes9OSv6IFsuhFGc7tW4urlpMJtuh+7YhzU6YEG9n8gmWKcMAFzkp7nr+r69kV0zrA==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.2", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.2.tgz", @@ -14210,6 +14324,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/mdast-util-math": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", + "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-mdx": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/mdast-util-mdx/-/mdast-util-mdx-2.0.1.tgz", @@ -14630,6 +14758,29 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromark-extension-math": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.1.tgz", + "integrity": "sha512-4rTUTTwHuXNL/sHy/LpmTEku+YOJIK4VYdILxv8bRI4unSpfdd/UzOv/DBV2KqgBeGQiyA3vmsARrKS7WQWwxw==", + "dependencies": { + "@types/katex": "^0.16.0", + "katex": "^0.16.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/@types/katex": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.0.tgz", + "integrity": "sha512-hz+S3nV6Mym5xPbT9fnO8dDhBFQguMYpY0Ipxv06JMi1ORgnEM4M1ymWDUhUNer3ElLmT583opRo4RzxKmh9jw==" + }, "node_modules/micromark-extension-mdx-expression": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.5.tgz", @@ -21237,6 +21388,23 @@ "jsesc": "bin/jsesc" } }, + "node_modules/rehype-katex": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-6.0.3.tgz", + "integrity": "sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/katex": "^0.14.0", + "hast-util-from-html-isomorphic": "^1.0.0", + "hast-util-to-text": "^3.1.0", + "katex": "^0.16.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rehype-raw": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz", @@ -21309,6 +21477,21 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/remark-math": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", + "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-math": "^2.0.0", + "micromark-extension-math": "^2.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-mdx": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/remark-mdx/-/remark-mdx-2.3.0.tgz", @@ -24160,6 +24343,19 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/unist-util-find-after": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-4.0.1.tgz", + "integrity": "sha512-QO/PuPMm2ERxC6vFXEPtmAutOopy5PknD+Oq64gGwxKtk4xwo9Z97t9Av1obPmGU0IyTa6EKYUfTrK2QJS3Ozw==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-is": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-generated": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/unist-util-generated/-/unist-util-generated-2.0.1.tgz", diff --git a/package.json b/package.json index 15e1428..6643d4f 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "date-and-time": "3.0.0", "gray-matter": "4.0.3", "html-react-parser": "3.0.16", + "katex": "0.16.7", "next": "13.4.3", "next-mdx-remote": "4.4.1", "next-pwa": "5.6.0", @@ -48,9 +49,11 @@ "react": "18.2.0", "react-dom": "18.2.0", "read-pkg": "8.0.0", + "rehype-katex": "6.0.3", "rehype-raw": "6.1.1", "rehype-slug": "5.1.0", "remark-gfm": "3.0.1", + "remark-math": "5.1.1", "sharp": "0.32.1", "shiki": "0.14.2", "unified": "10.1.2", diff --git a/pages/blog/[slug].tsx b/pages/blog/[slug].tsx index c7b8446..b900bac 100644 --- a/pages/blog/[slug].tsx +++ b/pages/blog/[slug].tsx @@ -4,6 +4,8 @@ import date from 'date-and-time' import Giscus from '@giscus/react' import { useTheme } from 'next-themes' +import 'katex/dist/katex.min.css' + import { Head } from 'components/Head' import { Header } from 'components/Header' import type { FooterProps } from 'components/Footer' diff --git a/posts/clean-code.md b/posts/clean-code.md index c5800f1..b65b17d 100644 --- a/posts/clean-code.md +++ b/posts/clean-code.md @@ -31,7 +31,7 @@ For example the [Linux kernel](https://www.kernel.org/), is one of the biggest o With a project of this magnitude, we can't let everyone do what they want and however they want, **we must set rules and conventions** to get everyone to agree, this allows to add features faster and will reduce possible bugs as **developers** will not struggle as much to understand the code. -## Definition : Design Patterns +## Definition: Design Patterns These **rules** and **conventions** are so called **Design Patterns**. @@ -77,8 +77,8 @@ setTimeout(restart, 86400000) ##### Example (good way) ```typescript -const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000 -setTimeout(restart, MILLISECONDS_IN_A_DAY) +const MILLISECONDS_IN_ONE_DAY = 24 * 60 * 60 * 1000 +setTimeout(restart, MILLISECONDS_IN_ONE_DAY) ``` --- @@ -131,7 +131,9 @@ const printCar = (car: Car): void => { --- -#### Boolean names (Prefix: is, has, can) +#### Boolean names + +The name of a boolean variable should be a question, and the answer should be true or false. We can use prefixes like `is`, `has`, `can` to make it more explicit and we should avoid negation. ##### Example (bad way) @@ -171,7 +173,10 @@ We have to keep it as simple as possible, not to implement features that are not import fs from 'node:fs' import path from 'node:path' -const createFile = async (name: string, isTemporary: boolean = false) => { +const createFile = async ( + name: string, + isTemporary: boolean = false +): Promise => { if (isTemporary) { return await fs.promises.writeFile(path.join('temporary', name), '') } @@ -187,11 +192,11 @@ const createFile = async (name: string, isTemporary: boolean = false) => { import fs from 'node:fs' import path from 'node:path' -const createFile = async (name: string) => { +const createFile = async (name: string): Promise => { await fs.promises.writeFile(name, '') } -const createTemporaryFile = async (name: string) => { +const createTemporaryFile = async (name: string): Promise => { await createFile(path.join('temporary', name)) } ``` @@ -210,7 +215,7 @@ The End To End (e2e) and Unit tests should document what is the behavior intende ### Avoid comments -One of the most important rule of "Clean Code" : If you need to add **comments**, it's because your code is **not clean**. +One of the most important rule of "Clean Code": If you need to add **comments**, it's because your code is **not clean**. I know that might be counter intuitive at first, as most developers will advice you to add comments to your code, to document what it does. diff --git a/posts/programming-challenges.md b/posts/programming-challenges.md new file mode 100644 index 0000000..5b9cfb5 --- /dev/null +++ b/posts/programming-challenges.md @@ -0,0 +1,343 @@ +--- +title: '🧠 Programming Challenges' +description: 'What are Programming Challenges and Competitive Programming and an introduction to Time/Space Complexity with Big O Notation.' +isPublished: true +publishedOn: '2023-05-21T10:20:18.837Z' +--- + +Hello! 👋 + +As **performance** and **reliability** is more and more important in software development, it is important to know how to write **efficient code**, and also learn to **not rely on every possible dependency of the world**, when it is not worth it. + +The more dependencies we add to our projects, the greater the complexity and maintenance overhead becomes. Each additional dependency requires understanding its functionality, API, and potential conflicts with other dependencies. This complexity makes the codebase harder to maintain, and it also poses significant security risks. + +We don't want to "reinvent the wheel" and rewrite everything from scratch for each project. In fact, you are **always depending on something** when you are writing your software. At the very least, you are dependent on the programming language you are using. Even if you are doing very low-level stuff, you are still depending on something: hardware. + +However, it is important to draw a line between what dependencies are worth the cost and which are not. + +Most likely adding a [JavaScript npm package `is-odd`](https://www.npmjs.com/package/is-odd) to check if a number is odd or even for example, is not worth it. Writing it ourselves is easier and allows a better maintenance in the long term. + +Learning **how to solve problems** and how to write efficient code is very important and also a very broad and complicated topic, so this blog post will only be an **introduction to the subject**, and will not go in depth. + +**Note:** Sources used to write this blog post are available at the [end of this post](#sources). + +## What is Competitive Programming? + +**Competitive programming** consists of solving correctly and efficiently **well-defined problems** by writing **computer programs** under specified **constraints**. Typically a solution to a problem is a combination of well-known techniques and new insights. + +There are many famous competitions: [Google Code Jam](https://codingcompetitions.withgoogle.com/codejam), [Facebook Hacker Cup](https://www.facebook.com/codingcompetitions/hacker-cup), [International Olympiad in Informatics](https://ioinformatics.org/), [International Collegiate Programming Contest](https://icpc.global/), [LeetCode](https://leetcode.com/), [CodinGame](https://www.codingame.com/), etc. + +The most common programming languages used for Competitive Programming are: **C++**, **Python** and **Java**. However the design of the algorithms and data structures are applicable to **any programming language**. + +All examples solutions on this blog post will be done in **Python**. + +## Topics to explore/learn with Competitive Programming + +- Time/Space complexity and Big O Notation + +- Sorting: Sorting algorithms and Binary search + +- Data structures: Arrays (1D, 2D: Matrix, 3D, Multidimensional), Dictionaries, Linked lists, Stack, Queue, Trees, Graphs, Heaps, etc. + +- Complete search: Generating Subsets, Permutations, Combinations, etc. + +- Greedy algorithms: Coin problem, Scheduling, Minimizing sums, etc. + +- Dynamic programming: Fibonacci, Coin problem, Knapsack, etc. + +- Bit manipulation: Bit representation, Bit operations, etc. + +- Shortest path: Dijkstra, Bellman-Ford, Floyd-Warshall, etc. + +- String: Trie structure, String hashing, Z-algorithm, etc. + +You can see there are lot of concepts to learn and explore, and it is not an exhaustive list. On this blog post, we will only see the first topic: **Time/Space complexity and Big O Notation**. + +## Time/Space complexity and Big O Notation + +### Definition + +An Algorithm is a finite sequence of well-defined instructions, that have to be given to the computer to perform a specific task. In this context, the variation can occur the way how the instructions are defined. There can be **any number of ways**, a specific set of instructions can be defined **to perform the same task**. Also, with options available to choose any one of the available programming languages, the instructions can take any form of syntax along with the performance boundaries of the chosen programming language. We also indicated the algorithm to be performed in a computer, which leads to the next variation, in terms of the operating system, processor, hardware, etc. that are used, which can also influence the way an algorithm can be performed. + +Different factors can influence the outcome of an algorithm being executed, it is wise to understand how efficiently such programs are used to perform a task. To gauge this, we require to evaluate: + +- The **Space complexity** of an algorithm **quantifies** the amount of **space or memory taken** by an algorithm to run based on the size of the input. +- The **Time complexity** of an algorithm **quantifies** the amount of **time taken** by an algorithm to run based on the size of the input. + +We more often talk about the **time complexity** than space complexity of an algorithm, because we can reuse memory unlike time and memory is cheap nowadays. + +**Big O Notation** describes the complexity of an algorithm in terms of **how quickly it grows relative to the input size $n$ (e.g: length of the string, size of the array etc.)** by defining the $N$ number of operations that are done on it. +Example of Big O notation: $O(n^2)$. + +### Time complexity + +Time complexity **measures** the **time taken** **to execute each statement** of code in an algorithm. It is not going to examine the total execution time of an algorithm. Rather, it is going to give information about the variation (increase or decrease) in execution time when the number of operations (increase or decrease) in an algorithm. + +There are many rules to calculate the time complexity of an algorithm. + +#### Loops + +A common reason why an algorithm is slow is that it contains many loops that go through the input. The more nested loops the algorithm contains, the slower it will run. + +If there are $k$ nested loops, the time complexity of the algorithm will be $O(n^k)$. + +##### Example $O(n)$ + +```python +for iteration in range(n): + pass + +# or with a while loop +iteration = 0 +while iteration < n: + pass +``` + +##### Example $O(n^2)$ + +```python +for iteration in range(n): + for iteration2 in range(n): + pass +``` + +##### Example $O(n^3)$ + +```python +for iteration in range(n): + for iteration2 in range(n): + for iteration3 in range(n): + pass +``` + +etc. + +#### Order of magnitude + +A time complexity does not tell us the exact number of times the code inside a loop is executed, but it only shows the **order of magnitude**. + +In the following examples, the time complexity of the algorithms is $O(n)$ but the number of operations is different. + +##### Example 1 + +```python +for iteration in range(0, n * 3, 1): + pass +``` + +Number of operations: $3n$ + +##### Example 2 + +```python +for iteration in range(0, n + 5, 1): + pass +``` + +Number of operations: $n + 5$ + +##### Example 3 + +```python +for iteration in range(0, n, 2): + pass +``` + +Number of operations: ${n \over 2}$ + +#### Phases + +If the algorithms consists of consecutive phases, the total time complexity is the largest time complexity of a single phase because it is usually the bottleneck of the code. + +The following code consists of 3 phases, with time complexities $O(n)$, $O(n^2)$ and $O(n)$. Thus the total time complexity is $O(n^2)$. + +```python +for iteration in range(n): + pass + +for iteration in range(n): + for iteration2 in range(n): + pass + +for iteration in range(n): + pass +``` + +#### Several variables + +Sometimes the time complexity depends on several factors. In this case, the time complexity formula contains several variables: $O(nm)$. + +```python +for iteration in range(n): + for iteration2 in range(m): + pass +``` + +#### Recursion + +The time complexity of a recursive function depends on the number of times it is called and the time complexity of a single call. The total time complexity is the product of these values. + +##### Example 1 + +The call `recursive(n)` causes $n$ calls and the time complexity of each call is $O(1)$. Thus the total time complexity is $O(n)$. + +```python +def recursive(n: int): + if n != 1: + recursive(n - 1) +``` + +##### Example 2 + +```python +def recursive(n: int): + if n != 1: + recursive(n - 1) + recursive(n - 1) +``` + +In this case, `recursive(n)` causes 2 other calls except for $n = 1$. + +The following table shows the function calls produced by this single call: + +| function call | number of calls | +| ------------- | --------------- | +| $g(n)$ | $1$ | +| $g(n - 1)$ | $2$ | +| $g(n - 2)$ | $4$ | +| ... | ... | +| $g(1)$ | $2^{n - 1}$ | + +Based on this, the time complexity is: + +$$ +1 + 2 + 4 + ... + 2^{n - 1} = 2^n - 1 = O(2^n) +$$ + +#### Complexity Classes (from fastest to slowest) + +![Big O Notation](../public/images/posts/programming-challenges/big-o-chart-notations.webp) + +Here is a list of classes of functions that are commonly encountered when analyzing the running time of an algorithm. + +- $O(1)$: **Constant** (does not depend on the input size). A typical constant-time algorithm is a direct formula that calculates the answer. + +- $O(\log_2(n))$: **Logarithmic** algorithm often halves the input size at each step. $\log_2(n)$ equals the number of times $n$ must be divided by 2 to get 1. + +- $O(\sqrt{n})$: **Square root** algorithm is slower than $O(\log_2(n))$ but faster than $O(n)$. + +- $O(n)$: **Linear** algorithm goes through the input a constant number of times. This is often the best possible time complexity, because it is usually necessary to access each input element at least once before reporting the answer. + +- $O(n \log_2(n))$: **Log linear** often indicates that the algorithm sorts the input, because the time complexity of efficient sorting algorithms is $O(n \log_2(n))$. Another possibility is that the algorithm uses a data structure where each operation takes $O(\log_2(n))$ time. + +- $O(n^2)$: **Quadratic** algorithm often contains 2 nested loops. It is possible to go trough all pairs of the input elements in $O(n^2)$ time. + +- $O(n^3)$: **Cubic** algorithm often contains 3 nested loops. It is possible to go trough all triplets of the input elements in $O(n^3)$ time. + +- $O(2^n)$: **Exponential** often indicates that the algorithm iterates through all subsets of the input elements. For example, the subsets of $\{1, 2, 3\}$ are $S = \{\{\empty\}, \{1\}, \{2\}, \{3\}, \{1, 2\}, \{1, 3\}, \{2, 3\}, \{1, 2, 3\} \}$. + +- $O(n!)$: **Factorial** often indicates that the algorithm iterates through all permutations of the input elements. For example, the permutations of $\{1, 2, 3\}$ are $P = \{\{1, 2, 3\}, \{1, 3, 2\}, \{2, 1, 3\}, \{2, 3, 1\}, \{3, 1, 2\}, \{3, 2, 1\} \}$. + +### Estimating efficiency + +By checking the time complexity of an algorithm, it is possible to check before implementing the algorithm,that it is efficient enough for the problem. + +Example: assume that the time limit for a problem is 1 second and the input size is $n = 10^5$. If the time complexity is $O(n^2)$, the algorithm will perform about $(10^5)^2 = 10^{10}$ operations. + +Given that a modern computer can perform some hundred of millions of operations per second. This should take at least 10 seconds, so the algorithm seems to be too slow for solving the problem. + +## Practical problem: Maximum subarray sum + +There are often several possible algorithms for solving a problem such that their time complexities are different. This section discusses a classic problem that can be solved using several different algorithmic techniques, including brute force, divide and conquer, dynamic programming, and reduction to shortest paths, each technique with different time complexity. + +**Maximum subarray sum**: Given an array of $n$ integers, find the contiguous subarray with the largest sum. + +Contiguous subarray is any sub series of elements in a given array that are contiguous ie their indices are continuous. The problem is interesting when there may be negative values in the array, because if the array only contains positive values, the maximum subarray sum is basically the sum of the array (the subarray being the complete array). + +### Example 1 + +#### Input + +```txt +[1, 2, 3, 4, 5, 6] +``` + +#### Output + +```txt +21 +``` + +**Explanation:** The subarray with the largest sum is the array itself (as there is no negative values) `[1, 2, 3, 4, 5, 6]` which has a sum of `21`. + +### Example 2 + +#### Input + +```txt +[-1, 2, 4, -3, 5, 2, -5, 2] +``` + +#### Output + +```txt +10 +``` + +**Explanation:** The subarray with the largest sum is `[2, 4, -3, 5, 2]` which has a sum of `10`. + +### Worst solution: Brute force + +```python +def maximum_subarray_sum_cubic(array: list[int]) -> int: + """ + Time complexity: O((array_length)^3) + + We go through all possible subarrays, calculate the sum in each subarray and maintain the maximum sum. + """ + if len(array) == 0: + return 0 + best_sum = array[0] + length = len(array) + for i in range(length): + for j in range(i, length): + sum = 0 + for k in range(i, j + 1): + sum += array[k] + if sum > best_sum: + best_sum = sum + return best_sum +``` + +### Better solution: Linear time + +```python +def maximum_subarray_sum_linear(array: list[int]) -> int: + """ + Time complexity: O(array_length) + + We loop through the array and for each array position, we calculate the maximum sum of a subarray that ends at that position. After this, the answer for the problem is the maximum of those sums. + """ + if len(array) == 0: + return 0 + best_sum = array[0] + length = len(array) + sum = 0 + for i in range(length): + sum = max(array[i], sum + array[i]) + best_sum = max(best_sum, sum) + return best_sum +``` + +## Conclusion + +Problems solving is a very complicated and large topic, and also a very important skill to have as a software developer. + +To improve our problems solving skills, we can regularly practice with [programming challenges](https://github.com/Divlo/programming-challenges). + +## Sources + +- [Wikipedia - Competitive programming](https://en.wikipedia.org/wiki/Competitive_programming) +- [Frontend Masters - The Last Algorithms Course You'll Need](https://frontendmasters.com/courses/algorithms/) +- [Big-O Cheat Sheet](https://www.bigocheatsheet.com/) +- [programming challenges](https://github.com/Divlo/programming-challenges) diff --git a/public/images/posts/programming-challenges/big-o-chart-notations.webp b/public/images/posts/programming-challenges/big-o-chart-notations.webp new file mode 100644 index 0000000000000000000000000000000000000000..a64bb15423de8bfb4212efd203cd91019102d27c GIT binary patch literal 26496 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 literal 0 HcmV?d00001 diff --git a/styles/global.css b/styles/global.css index d237e25..20027c9 100644 --- a/styles/global.css +++ b/styles/global.css @@ -28,7 +28,7 @@ } .shiki { white-space: pre-wrap !important; - word-break: break-word !important; + word-break: normal !important; word-wrap: normal; } code { diff --git a/utils/blog.ts b/utils/blog.ts index 06748f3..a31803b 100644 --- a/utils/blog.ts +++ b/utils/blog.ts @@ -7,6 +7,8 @@ import rehypeRaw from 'rehype-raw' import { serialize } from 'next-mdx-remote/serialize' import remarkGfm from 'remark-gfm' import rehypeSlug from 'rehype-slug' +import remarkMath from 'remark-math' +import rehypeKatex from 'rehype-katex' import matter from 'gray-matter' import { getHighlighter } from 'shiki' @@ -45,7 +47,10 @@ export const getPosts = async (): Promise => { const blogPostContent = await fs.promises.readFile(blogPostPath, { encoding: 'utf8' }) - const { data, content } = matter(blogPostContent) as any + const { data, content } = matter(blogPostContent) as unknown as { + data: FrontMatter + content: string + } const date = new Date(data.publishedOn) return { slug, @@ -81,12 +86,14 @@ export const getPostBySlug = async ( const source = await serialize(post.content, { mdxOptions: { remarkPlugins: [ - remarkGfm as any, - [remarkSyntaxHighlightingPlugin, { highlighter }] + remarkGfm, + [remarkSyntaxHighlightingPlugin, { highlighter }], + remarkMath ], rehypePlugins: [ - rehypeSlug as any, - [rehypeRaw, { passThrough: nodeTypes }] + rehypeSlug, + [rehypeRaw, { passThrough: nodeTypes }], + rehypeKatex ] } })