Blog | Autzoko Lang

THIS IS MY KINGDOM COME

Given an integer array coins representing coins of different denominations and integer amount representing a total amount of money.

Return the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of coins, return $-1$.

Example:

1
2
3
4
Input: coins = [1, 2, 5], amount = 11;
Output: 3;
Input: coins = [2], amount = 3;
Output: -1;

Constraints:

  • $1 \le l_{coins} \le 12$
  • $1 \le coins_i \le 2^{31}-1$
  • $0 \le amount \le 10^4$

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class CoinChange{
public:
int coinChange(vector<int>& coins, int amount) {
int *dp = new int[amount + 1];
fill_n(dp, amount + 1, amount + 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.size(); j++) {
if (coins[j] <= i)
dp[i] = dp[i - coins[j]] + 1 < dp[i] ? dp[i - coins[j]] + 1 : dp[i];
}
}
return dp[amount] > amount ? -1 : dp[amount];
}
}

Explanation:

$dp_i$ represents the minimum number of coins that makes up the amount $i$. Assume that the values from $dp_0$ to $dp_{i-1}$ are already known, thus the transition equation of $dp_i$ can be deduced as
$$
dp_i = min_{j=0}^{l_{coins}-1}dp_{i-coins_j}+1
$$
which means the value of $dp_i$ is determined by $dp_{i-coins_j}$. If $dp_i$ can transfer from $dp_{i-coins_j}$ via $coins_j$, the number of coins that used should add $1$.

什么是个人图床

简单举例,当在使用markdown或者HTML等文本框架时,插入图片(或其他媒体资源)通常使用两种插入方式:

  • 通过本地路径获取;一般通过指定图片在本机或服务器上的路径(一般是绝对路径或者相对路径)来获取图片,图片信息保存在个人媒介上。
  • 通过网络URL获取;一般通过指定图片在其他云服务提供商上的URL路径来实时加载图片资源。这个用来保存图片的云服务一般被称为图床

而在写个人博客时,通常希望博文中可以插入一些图片来方便撰写。除了直接在markdown中插入本机图片,使用个人图床也是一个较好的选择,尤其适合我这种访问量低也不会上传大量图片资源的博主。

Telegraph-image

Telegraph-image相当于一个Flickr/imgur的平替,通过在Cloudfare Pages中部署该Function可以起到图片上传和引用等寄存服务。

Telegraph-image的github仓库:Telegraph-Image

该仓库的README中也介绍了如何部署该服务,这里只是简单的重复一下。

注册Cloudfare

前往Cloudflare(这里使用的国区代理)注册即可。(建议)直接使用个人邮箱注册即可,注册成功后邮箱会收到一封验证邮件,点击验证邮件即可。

Fork仓库

将Telegraph-image的仓库Fork到自己属下。Fork都是用默认配置,只Fork main分支即可。另一种方法是将该仓库clone到本地再部署,这样比较麻烦故不推荐。

部署Telegraph-image

点击Cloudfare侧边栏的Works & Pages栏

Cloudfare部署:前往Pages

前往Pages界面然后点击”连接到Git“:

链接Git

之后点击添加Github账户,添加自己的Github账户。添加成功后选择Fork过来的Telegraph-Image仓库来部署:

选择Github仓库开始部署

然后点击右下方的开始设置按钮。进入下一个页面后可以更改项目名称,项目分支仍然是main分支,其余配置无需改动(都是空即可)。这一页的内容完成后点击保存并部署即可开始部署。

此时网页内会有仿命令行窗口来提示部署进度,最后直至完成。

点击访问站点

代理生成之后会出现访问站点的链接,点击该链接即可前往个人图床网站。在图床网站可以选择文件上传或者直接ctrl-v粘贴,拖拽上传图片。上传图片成功之后会在下方自动生成一个图片链接,复制该链接至需要用到的地方即可(如Markdown,本博客的图片均来自个人图床的链接)。

个人图床界面

到这里一个简单的个人图床就大功告成啦。后续添加图像上传验证Token之类也会慢慢更新,尝试发掘出更多的拓展功能。

Introduction

SHA-256 encryption algorithm is a division from the SHA-2 group, a.k.a. Secure Hash Algorithm 2. It can be known from the name that, the SHA is a type of hash function. This encryption algorithm encode the input message by hashing to encrypt and compress. The result of SHA encryption usually contains random alphanumeric characters.

Explanations of SHA-256 Algorithm

Before explicitly explain the detailed algorithm of SHA-256, it is essential to figure out what should be done in advance. Three sections can be extracted: the constants that are used in the algorithm, how to preprocess the data, and the logical operations that are used. After understanding these, the algorithm itself is easy to handle.

Before Dive into SHA-256

Constants

In the SHA-256 algorithm, 8 hash initial values and 64 hash constants are used. First, the eight initial values are as follow:

1
2
3
4
uint32_t H[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};

These numbers are selected from the first 32 bits of the decimal part of the square roots of the first eight natural prime numbers.

And the 64 hash constants are:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

Similar to before, these 64 constants are selected from the first 32 bits of the cube roots of the first 64 natural prime numbers.

Data Preprocessing

The preprocessing stage of the SHA-256 algorithm has two steps:

  • Append ‘1’ followed by enough ‘0’s at the end of the data so that the length of the message modulo 512 equals 448.
  • Then append 64 bits of message length information at the end with big endian.

Notice that: albeit current length modulo 512 equals 448, the append operation is still necessary!

For example, now here’s data:

1
01100001 01100010 01100011

Then we need to append a ‘1’ and 423 ‘0’s:

1
2
01100001 01100010 01100011 1000000
00000000 ...... 0000000

Finally, the length of the original message should be appended to the end of current data by big endian.

Logical Operations

All of logical operations that involved with SHA-256 algorithm are as follow:
$$
Ch(x,y,z)=(x \land y) \oplus (\urcorner x \land z)
$$
$$
Maj(x,y,z)=(x\land y)\oplus (x \land z) \oplus (y \land z)
$$
$$
\Sigma_0(x) = S_R^2(x) \oplus S_R^{13}(x) \oplus S_R^{22}(x)
$$
$$
\Sigma_1(x) = S_R^6(x) \oplus S_R^{11}(x) \oplus S_R^{25}(x)
$$
$$
\sigma_0(x) = S_R^7(x) \oplus S_R^{18} \oplus R^3(x)
$$
$$
\sigma_1(x) = S_R^{17}(x) \oplus S_R^{19}(x) \oplus R^{10}(x)
$$

Explanations:

  • $\land$: and.
  • $\urcorner x$: not.
  • $\oplus$: xor.
  • $S_R^n$: Cycled right shift.
  • $R^n$: Right shift.

Abstraction

How here comes to ultimately main part of the SHA-256 algorithm! All the details related to the algorithm will be elaborate here.

The first things is: break message into 512-bit chunks.

Suppose that the message $m$ can be splited into $n$ chunks, so the whole algorithm needs to iterate for $n$ times in order to get a 256-bit hash result. Initially, a 256-bit initial value $H_0$ is given, then operate with data chunk 1 and get $H_1$. Then operate with data chunk 2 and get $H_2$. Repeat above step until all data chunks are operated.

The pseudo-code are:

1
2
3
4
5
data_chunks = divide_by_512_bit(message)
handle = H0
for (chunk in data_chunks):
handle = operate(handle, chunk)
result = handle

Among each operation, each data chunk with 512-bit chunk are divided into 16 words, each word contains 32 bits and encoded with big endian, as $W_0,…,W_{15}$. Which means the first sixteen words can be directly derived from the $i^{th}$ chunk data. The rest words are derived through following equation:

$$
W_t=\sigma_1(W_{t-2})+W_{t-7}+\sigma_0(W_{t-15})+W_{t-16}
$$

Then, iterate by 8 words for 64 times follow these rules:

$$
A=H+\Sigma_1(E)+Ch(E,F,G)+K_i+W_i+Maj(A,B,C)+\Sigma_0(A)
$$
$$
B=A
$$
$$
C=B
$$
$$
D=C
$$
$$
E=D+H+\Sigma_1(E)+Ch(E,F,G)+W_i+K_i
$$
$$
F=E
$$
$$
G=F
$$
$$
H=G
$$
Notice that: the initial value of word A-H is $H_0$-$H_7$. And $K_i$ is the $i^{th}$ value of the initial hash value.

Code Implementation

This notes is according to MIT’s course: missing-semester: Editors(Vim)

Vim

Vim is a command line based code editor, designed for programmers and designed by programmers. And other visual editors like VS Code can also integrate vim mode for advanced coding experience.

The core philosophy of vim editor is command, all operations are done by typing keys, using without mouse. After being familiar with these key settings, the speed of programming can be extremely improved.

Modes of vim

Vim contains several operating modes for different using:

  • Normal: navigating, moving, and simple editing. Press ESC for back to normal mode.
  • Insert: inserting text. Press i for switching to insert mode.
  • Replace: replacing text. Press shift-r for switching to replace mode.
  • Visual:
    • Visual-plain: selecting text by cursor movement. Press v.
    • Visual-line: selecting text by line. Press shift-v.
    • Visual-block: selecting text by block. Press ctrl-v.
  • Command-line: running command. Press shift-;.

Command-line

By pressing : to get into command-line mode, where can send command to operating thw vim in a whole.

  • :q | quit (or close current window).
  • :w | write and save.
  • :wq | save and quit.
  • :e {filename} | open file and edit.
  • :ls | show open buffers.
  • :help {topic} | open help
    • :help :w | help info for command :w.
    • :help w | help info for command w.

Cursor movement

  • Basic movement: hjkl | left, down, up, right.
  • Move by word: w, b, e | next word, beginning of word, end of word.
  • Move by line: 0, ^, $ | beginning of line, first non-blank character, end of line.
  • Move by screen: H, M, L | top of screen, middle of screen, bottom of screen.
  • Scroll: ctrl-u, ctrl-d | scroll up, scroll down.
  • Move by file: gg, G | beginning of file, end of file.
  • Misc: % | corresponding item.
  • Find: f{char}, t{char}, F{char}, T{char} | find\to forward\backward character on the current line.
  • Search: /{regex}, n, N | navigating match items.

Edits

  • i | enter insert mode.
  • o, O | insert line below, insert line above.
  • d{motion} | delete by motion command.
    • dw | delete word.
    • d$ | delete to end of line.
    • d0 | delete to beginning of line.
  • c{motion} | change by motion command.
  • x | delete character.
  • s | substitute character.
  • u | undo.
  • ctrl-r | redo.
  • y | copy
  • p | paste.

Counts

By using command pattern {number}{command} can do this command by times.

  • 3w move 3 words forward.
  • 5j move 5 lines down.
  • 7dw delete 7 words.

Modifiers

While using delete or change command, add i or a can specify the command is done inside or around.

  • ci( | change content inside current pair of parentheses.
  • ci[ | change content inside current pair of square brackets.
  • da' | delete a single-quoted string, including the surrounding single quotes.

Attention is All You Need is a paper published in 2017 by Google Brain team. In this paper, the prominent self-attention mechanism and multi-headed self-attention mechanism are proposed to enhance the general capacity of neural networks. Meanwhile, a basic neural network architecture Transformer is proposed to adopt such mechanism and its been tested on translation task.

What is Attention?

The attention mentioned in neural network is similar with the one we mentioned about human intelligence: an ability to capture information which seems to be more important (subjectively). The “attention mechanism” in human congnition is prominently manifested across various sensory perceptions. For instance, when using our eyes, certain features such as bright colors, rapid movements, or familiar faces tend to capture our gaze. Similarly, during reading, specific names, events, or elegant phrases can leave a lasting impression. This attention mechanism also extends to other senses like smell, touch, and hearing. Furthermore, the stream of consciousness during thought processes is often influenced by particularly striking information, all of which illustrate the attention mechanism in human cognition.

In the context of a nerual network, what does the attention mechanism refer to? For example, when we want a neural network to process an image, how does it determine which elements of the image are more important. Consider objects like people, pets, or cars in an image—how does the network decide which one is more noteworthy in the image’s “context”? The attention mechanism addresses this problem. When processing an image, the neural network assigns higher weights, or “attention scores”, to what it deems more important information. During subsequent forwarding process, the high-weight information is emphasized, enabling the network to capture more detailed and deeper features. This, in turn, aids in handling the semantic importance of the image. Besides image processing, the mechanism is applicable to text, audio, graph, and other data types. Since data in neural networks exists as discrete tensors, the fundamental principles of calculating attention scores remain consistent across different data types.

Self-Attention Mechanism

The so-called self-attention mechanism refers to a process where the attention scores are computed by comparing the elements within the sequence itself. In other words, the reference for calculating attention scores is the sequence as a whole. For a tensor sequence input into a neural network, the self-attention mechanism requires computing the attention scores between each element and every other element in the sequence. The basic process can be represented in pseudocode as follows:

1
2
3
4
5
for (element_x : input_sequence) {
for (element_y : input_sequence) {
attention_score[x][y] = compute_score(element_x, element_y);
}
}

However, for deep learning tasks, using to nested loops to compute values is highly time-consuming, especially when the input sequence length can be in the tens of thousands. Therefore, researchers use vectorized operations to calculate all pairwise attention scores efficiently. By using vectorized multiplication, we can obtain all attention scores with a single dot product operation between the input tensor sequence and its transposed version.

$$
X= \left[ \begin{matrix} x_1, …, x_n \end{matrix} \right]
$$

$$
X^T= \left[ \begin{matrix} x_1 \\ \vdots \\ x_n \\ \end{matrix} \right]
$$

$$
AttentionScore = X \cdot X^T = \left[ \begin{matrix} x_1 \cdot x_1 & … & x_1 \cdot x_n\\ \vdots & & \vdots \\ x_n \cdot x_1 & … & x_n \cdot x_n\\ \end{matrix} \right]
$$

Through this matrix multiplication operation, combined with parallel computing technology and the parallel processing capabilities of GPU devices, the efficiency of this computation can be greatly enhanced. To ensure that the attention scores are not fixed and be gradually optimized during the training process, we use parameter matrices to adjust the calculation of attention scores. This enable the model to truely capture the information that deserves attention. Thus, the equation for computing attention score can be expressed as follows:
$$
Attention(Q,K,V) = softmax(\frac{QK^T}{\sqrt{d_k}})V
$$
Here, $ Q, K, V $ refer to each element in the input sequence being multiplied by three shared weighted matrices $ W_q, W_k $ and $ W_v $. The data multiplied by $ W_q $ represents the element that will compute attention scores with other elements in the sequence. The data multiplied by $ W_k $ represents the data being compared. After comparing each element with every other element, the resulting sequence is the attention score sequence for that element. This sequence is processed through the softmax function and then multiplied by $ W_v $ to perform a weighted sum. The entire process essentially calculates the correlations between all elements and the performs a weighted sum, with the result being the output of the attention mechanism. Through this process, the information that is considered more important is multiplied by greater weights, as a result their “information” can be magnified in the subsequent processes.

Transformer Model

The Transformer model adopts an encoder-decoder architecture which is a popular architecture in current deep learning field. Two independent models are separatedly used as an encoder and a decoder. For most cases, they are used to process different types of data. For example, in image captioning, encoder is used for process image signal and decoder is for generating captions. In this paper, the Transformer is tested on English-German translation task, so, unprecisely, you can image the encoder is trying to “understand” a sentence in English and then generate some kind of metaphysical or abstract information, these information contains the concepts, subjects and objectives that are “mentioned” in the original language. Then, these information is passed to decoder, and the task of decode is to decrypt these information into German.

Importantly, the comparison I mentioned above is extremely unprecise, this model cannot translate as human do, in fact it’s still a prediction or simulation, but not understanding.

Given two string word1 and word2, return the minimum number of operation required to converted word1 to word2.

Following three operations are permitted on a word:

  • Insert a character
  • Delete a character
  • Replace a character

Example:

1
2
Input: word1 = "horse", word2 = "ros";
Output: 3;

Constraints:

  • 0 <= len(word1), len(word2) <= 500
  • word1 and word2 consist of lower case English letters

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class EditDistance {
public:
int minDistance(string word1, string word2) {
word1 = " " + word1;
word2 = " " + word2;

int **matrix = new int*[word1.size()];
for(int i = 0; i < word1.size(); i++) {
matrix[i] = new int[word2.size()];
fill_n(matrix[i], word2.size(), 0);
}

for(int i = 0; i < word1.size(); i++) {
for(int j = 0; j < word2.size(); j++) {
if(_min_(i, j) == 0) matrix[i][j] = _max_(i, j);
else {
if(word1[i] == word2[j]) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = _trimin_(
matrix[i - 1][j - 1],
matrix[i][j - 1],
matrix[i - 1][j]
) + 1;
}
}
}
}
return matrix[word1.size() - 1][word2.size() - 1];
}

private:
int _min_(int i, int j) {
return i < j ? i : j;
}
int _max_(int i, int j) {
return i > j ? i : j;
}
int _trimin_(int x, int y, int z) {
return _min_(x, y) < _min_(y, z) ? _min_(x, y) : _min_(y, z);
}
};

Explanation:

Use matrix[i][j] to represents the edit distance between string word1[0…i] and word2[0…j]. The index of string’s content begins at 1, matrix[0][0] indicates the case that word1 and word2 are both vacant, which means their edit distance is 0. So, it is obvious that matrix[0][j] is the case when word1 is empty and the length of word2 is j, and their edit distance should be just j. Similarly, matrix[0][i] is the case when the length of word1 is i and word2 is empty, their edit distance is i.

Then we can deduce the equation for the above three operations accordingly:

  • matrix[i - 1][j] + 1: insert the last character of word1 into the tail of word2
  • matrix[i][j - 1] + 1: delete the last character of word2
  • matrix[i - 1][j - 1] + 1: replace the last character of word2 by the last character of word1

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.

Example:

1
2
Input: n = 3;
Output: ["((()))", "(()())", "(())()", "()(())", "()()()"]

Constraints:

  • 1 <= n <= 8

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class GenerateParentheses {
public:
vector<string> res;
vector<string> generateParentheses(int n) {
if(n <= 0) return res;
p("", n, n);
return res;
}

void p(string str, int left, int right) {
if(left == 0 && right == 0) {
res.push_back(str);
return;
}
if(left == right) {
p(str + "(", left - 1, right);
} else {
if(left > 0) {
p(str + "(", left - 1, right);
}
p(str + ")", left, right - 1);
}
}
};

Explanation:

The number of remained left parenthese must be less or equal to the number of remained right parenthese. If the number of remained left parenthese equals to the remained right one, the next can only be a left parenthese. If the number of remained left parenthese is less than the right one, the next one can be either a left parenthese or a right parenthese. This problem can be solved by using recursion algorithm.

Given a m * n grid filled with non-negative numbers, find a path from top left to bottom right, which minizes the sum of all numbers along its path.

  • Moves can only be taken down or right at any point in time.

Example:

1
2
Input: grid = [[1, 3, 1], [1, 5, 1], [4, 2, 1]];
Output: 7;

Constraints:

  • 1 <= m, n <= 200
  • 0 <= grid[i, j] <= 200

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class MinimumPathSum {
public:
int minPathSum(vector<vector<int>>& grid) {
int n_row = grid.size();
int n_col = grid[0].size();
int **dp = new int*[n_row];
for(int i = 0; i < n_row; i++) {
dp[i] = new int[n_col];
fill_n(dp[i], n_col, INT_MAX);
}
dp[0][0] = grid[0][0];

for(int i = 0; i < n_row; i++) {
for(int j = 0; j < n_col; j++) {
if(i) {
dp[i][j] = dp[i - 1][j] + grid[i][j] < dp[i][j] ? dp[i - 1][j] + grid[i][j] : dp[i][j];
}
if(j) {
dp[i][j] = dp[i][j - 1] + grid[i][j] < dp[i][j] ? dp[i][j - 1] + grid[i][j] : dp[i][j];
}
}
}
return dp[n_row - 1][n_col - 1];
}
};

Explanation:

Using a dp array to indicate the current minimum path sum. And this sum can only be the sum of the up dp point and current grid point or the left dp point and current grid point. The dp function can be defined as follow:

1
dp[i, j] := min(dp[i - 1, j] + grid[i, j], dp[i, j - 1] + grid[i, j])

Welcome to my personal blog. I open this blog when I am about to graduate from my university, at the time I extremely want to use something to document my life and things I’ve learned. And now I’m on my way to the United States to start a brand new experience of studying. I want to keep some memories through this blog, and meanwhile take some notes about the things that might be useful to my future development.

About This Blog

Due to my major is computer science/engineering, so most of study notes are related to fields like computer science, computer engineering, software developing, etc., I have to admit that I’m not excel at such subjects, but I am still keen on them and wish to make something big from my own hands. Hope this blog can witness my improvements :)

This blog does not only focus on such academic matters. For other parts, I will upload some of my daily life records, like photos, music, and maybe some unexpected inspiration and ideas. And I will write each blog article in English as possible as I can, to improve my English writing skills, and maybe it’s benefit to my future experience in the US🙂

Hope you enjoy my blog, and discover something new!

Crescat Scientia Vita Excolatur

益智厚生

0%