2023-06-30

This commit is contained in:
Gennadiy 2023-12-17 07:56:19 +03:00
parent c7c111e85d
commit 5731feea25
32 changed files with 2549 additions and 0 deletions

2
.gitattributes vendored
View file

@ -0,0 +1,2 @@
jekyll_site/ru/** linguist-language=Java
jekyll_site/en/** linguist-language=Java

3
.gitignore vendored
View file

@ -1,2 +1,5 @@
.idea
*.iml
*.zip
_site*
.repo_*.sh

61
DIRECTORY-TREE.md Normal file
View file

@ -0,0 +1,61 @@
## Дерево каталогов
<pre>
.
├─ <a href='jekyll_site'>jekyll_site</a>
│ ├─ <a href='jekyll_site/_includes'>_includes</a>
│ │ ├─ <a href='jekyll_site/_includes/counters_body.html'>counters_body.html</a>
│ │ └─ <a href='jekyll_site/_includes/counters_head.html'>counters_head.html</a>
│ ├─ <a href='jekyll_site/en'>en</a>
│ │ ├─ <a href='jekyll_site/en/2021'>2021</a>
│ │ │ └─ <a href='jekyll_site/en/2021/12'>12</a>
│ │ │ ├─ <a href='jekyll_site/en/2021/12/10'>10</a>
│ │ │ │ └─ <a href='jekyll_site/en/2021/12/10/optimizing-matrix-multiplication.md'>optimizing-matrix-multiplication.md</a>
│ │ │ ├─ <a href='jekyll_site/en/2021/12/13'>13</a>
│ │ │ │ └─ <a href='jekyll_site/en/2021/12/13/matrix-rotation-90-degrees.md'>matrix-rotation-90-degrees.md</a>
│ │ │ └─ <a href='jekyll_site/en/2021/12/17'>17</a>
│ │ │ └─ <a href='jekyll_site/en/2021/12/17/matrix-rotation-180-degrees.md'>matrix-rotation-180-degrees.md</a>
│ │ ├─ <a href='jekyll_site/en/2022'>2022</a>
│ │ │ └─ <a href='jekyll_site/en/2022/02'>02</a>
│ │ │ ├─ <a href='jekyll_site/en/2022/02/09'>09</a>
│ │ │ │ └─ <a href='jekyll_site/en/2022/02/09/matrix-multiplication-parallel-streams.md'>matrix-multiplication-parallel-streams.md</a>
│ │ │ └─ <a href='jekyll_site/en/2022/02/11'>11</a>
│ │ │ └─ <a href='jekyll_site/en/2022/02/11/winograd-strassen-algorithm.md'>winograd-strassen-algorithm.md</a>
│ │ └─ <a href='jekyll_site/en/index.md'>index.md</a>
│ ├─ <a href='jekyll_site/img'>img</a>
│ │ ├─ <a href='jekyll_site/img/block-matrices.svg'>block-matrices.svg</a>
│ │ ├─ <a href='jekyll_site/img/products.svg'>products.svg</a>
│ │ ├─ <a href='jekyll_site/img/sums1.svg'>sums1.svg</a>
│ │ ├─ <a href='jekyll_site/img/sums2.svg'>sums2.svg</a>
│ │ └─ <a href='jekyll_site/img/sums3.svg'>sums3.svg</a>
│ ├─ <a href='jekyll_site/ru'>ru</a>
│ │ ├─ <a href='jekyll_site/ru/2021'>2021</a>
│ │ │ └─ <a href='jekyll_site/ru/2021/12'>12</a>
│ │ │ ├─ <a href='jekyll_site/ru/2021/12/09'>09</a>
│ │ │ │ └─ <a href='jekyll_site/ru/2021/12/09/optimizing-matrix-multiplication.md'>optimizing-matrix-multiplication.md</a>
│ │ │ ├─ <a href='jekyll_site/ru/2021/12/12'>12</a>
│ │ │ │ └─ <a href='jekyll_site/ru/2021/12/12/matrix-rotation-90-degrees.md'>matrix-rotation-90-degrees.md</a>
│ │ │ └─ <a href='jekyll_site/ru/2021/12/16'>16</a>
│ │ │ └─ <a href='jekyll_site/ru/2021/12/16/matrix-rotation-180-degrees.md'>matrix-rotation-180-degrees.md</a>
│ │ ├─ <a href='jekyll_site/ru/2022'>2022</a>
│ │ │ └─ <a href='jekyll_site/ru/2022/02'>02</a>
│ │ │ ├─ <a href='jekyll_site/ru/2022/02/08'>08</a>
│ │ │ │ └─ <a href='jekyll_site/ru/2022/02/08/matrix-multiplication-parallel-streams.md'>matrix-multiplication-parallel-streams.md</a>
│ │ │ └─ <a href='jekyll_site/ru/2022/02/10'>10</a>
│ │ │ └─ <a href='jekyll_site/ru/2022/02/10/winograd-strassen-algorithm.md'>winograd-strassen-algorithm.md</a>
│ │ └─ <a href='jekyll_site/ru/index.md'>index.md</a>
│ ├─ <a href='jekyll_site/Gemfile_color'>Gemfile_color</a>
│ ├─ <a href='jekyll_site/Gemfile_older'>Gemfile_older</a>
│ ├─ <a href='jekyll_site/_config_color.yml'>_config_color.yml</a>
│ ├─ <a href='jekyll_site/_config_older.yml'>_config_older.yml</a>
│ └─ <a href='jekyll_site/robots.txt'>robots.txt</a>
├─ <a href='CONTRIBUTING.md'>CONTRIBUTING.md</a>
├─ <a href='DIRECTORY-TREE.md'>DIRECTORY-TREE.md</a>
├─ <a href='LICENSE.md'>LICENSE.md</a>
├─ <a href='OPEN_LICENSE.txt'>OPEN_LICENSE.txt</a>
├─ <a href='README.en.md'>README.en.md</a>
├─ <a href='README.md'>README.md</a>
├─ <a href='build.sh'>build.sh</a>
├─ <a href='package.sh'>package.sh</a>
└─ <a href='serve.sh'>serve.sh</a>
</pre>

17
README.en.md Normal file
View file

@ -0,0 +1,17 @@
## Website pages
- [Winograd — Strassen algorithm](https://pomodoro3.mircloud.ru/en/2022/02/11/winograd-strassen-algorithm.html) — 11.02.2022.
- [Matrix multiplication in parallel streams](https://pomodoro3.mircloud.ru/en/2022/02/09/matrix-multiplication-parallel-streams.html) — 09.02.2022.
- [Matrix rotation 180 degrees](https://pomodoro3.mircloud.ru/en/2021/12/17/matrix-rotation-180-degrees.html) — 17.12.2021.
- [Matrix rotation 90 degrees](https://pomodoro3.mircloud.ru/en/2021/12/13/matrix-rotation-90-degrees.html) — 13.12.2021.
- [Optimizing matrix multiplication](https://pomodoro3.mircloud.ru/en/2021/12/10/optimizing-matrix-multiplication.html) — 10.12.2021.
## [Source texts](README.md)
- Series of the static websites [«Pomodori»](https://hub.mos.ru/golovin.gg/pomodoro/blob/master/README.en.md).
- Used formats — Markdown, Liquid, YAML.
- Build tool — Jekyll with tomato design themes.
- Automation of processes — Bash scripts for command line.
- [build.sh](build.sh) — Building a site in two tomato themes and optimizing the results.
- [serve.sh](serve.sh) — Local deployment to verify the correctness of the build.
- [package.sh](package.sh) — Preparing an archive for subsequent deployment.

17
README.md Normal file
View file

@ -0,0 +1,17 @@
## Страницы вёб-сайта
- [Алгоритм Винограда — Штрассена](https://pomodoro3.mircloud.ru/ru/2022/02/10/winograd-strassen-algorithm.html) — 10.02.2022.
- [Умножение матриц в параллельных потоках](https://pomodoro3.mircloud.ru/ru/2022/02/08/matrix-multiplication-parallel-streams.html) — 08.02.2022.
- [Поворот матрицы на 180 градусов](https://pomodoro3.mircloud.ru/ru/2021/12/16/matrix-rotation-180-degrees.html) — 16.12.2021.
- [Поворот матрицы на 90 градусов](https://pomodoro3.mircloud.ru/ru/2021/12/12/matrix-rotation-90-degrees.html) — 12.12.2021.
- [Оптимизация умножения матриц](https://pomodoro3.mircloud.ru/ru/2021/12/09/optimizing-matrix-multiplication.html) — 09.12.2021.
## [Исходные тексты](README.en.md)
- Серия статических вёб-сайтов [«Помидоры»](https://hub.mos.ru/golovin.gg/pomodoro/blob/master/README.md).
- Используемые форматы — Markdown, Liquid, YAML.
- Инструмент сборки — Jekyll с помидорными темами оформления.
- Автоматизация процессов — Bash скрипты для командной строки.
- [build.sh](build.sh) — Сборка сайта в двух помидорных темах и оптимизация результатов.
- [serve.sh](serve.sh) — Локальное развёртывание для проверки корректности сборки.
- [package.sh](package.sh) — Подготовка архива для последующего развёртывания.

51
build.sh Executable file
View file

@ -0,0 +1,51 @@
#!/bin/bash
echo "Сборка сайта в двух помидорных темах и оптимизация результатов."
milliseconds=$(date '+%s%3N')
rm -rf _site
rm -rf _site_older
rm -rf _site_color
echo "Сборка старого помидора."
mkdir -p _site_older
cp -r jekyll_site/_includes _site_older
cp -r jekyll_site/ru _site_older
cp -r jekyll_site/en _site_older
cp -r jekyll_site/ru/index.md _site_older
cp -r jekyll_site/_config_older.yml _site_older/_config.yml
cp -r jekyll_site/Gemfile_older _site_older/Gemfile
cd _site_older || exit
jekyll build
cp -r _site ..
cd ..
echo "Сборка цветного помидора."
mkdir -p _site_color
cp -r jekyll_site/_includes _site_color
cp -r jekyll_site/ru _site_color
cp -r jekyll_site/en _site_color
cp -r jekyll_site/ru/index.md _site_color
cp -r jekyll_site/_config_color.yml _site_color/_config.yml
cp -r jekyll_site/Gemfile_color _site_color/Gemfile
cd _site_color || exit
jekyll build
cp -r _site ../_site/color
cd ..
echo "Копирование без сборки."
cp -r jekyll_site/img _site
cp -r jekyll_site/robots.txt _site
echo "Оптимизация собранного контента."
cd _site || exit
cp -r assets/* .
rm -r assets
rm -r color/assets/favicon.ico
cp -r color/assets/* .
rm -r color/assets
rm -r color/404.html
find . -type f -name '*.html' | sort -r | while read -r file; do
sed -i 's/layout-padding=""/layout-padding/g' "$file"
sed -i 's/ class="language-plaintext highlighter-rouge"//g' "$file"
sed -i 's/ class="language-java highlighter-rouge"//g' "$file"
sed -i 's/<div><div class="highlight"><pre class="highlight">/<div class="highlight"><pre class="highlight">/g' "$file"
sed -i 's/<\/code><\/pre><\/div><\/div>/<\/code><\/pre><\/div>/g' "$file"
sed -i 's/<hr \/>/<hr>/g' "$file"
sed -i -r 's/<img(.+) \/>/<img\1>/g' "$file"
done
echo "Время выполнения сборки: $(("$(date '+%s%3N')" - "$milliseconds")) мс."

View file

@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "jekyll"
gem "color-tomato-theme"

View file

@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "jekyll"
gem "older-tomato-theme"

View file

@ -0,0 +1,20 @@
# site parameters
name: "Код с комментариями"
name_translated: "Code with comments"
url: "https://pomodoro3.mircloud.ru"
baseurl: "/color"
homepage_url: "https://git.org.ru/pomodoro/3"
homepage_name: "GIT.ORG.RU"
older_tomato_baseurl: ""
timezone: "Europe/Moscow"
author: "Головин Г.Г."
author_translated: "Golovin G.G."
translation_caption: "translation from Russian"
# build parameters
disable_disk_cache: true
theme: color-tomato-theme
defaults:
- scope:
path: ""
values:
layout: default

View file

@ -0,0 +1,20 @@
# site parameters
name: "Код с комментариями"
name_translated: "Code with comments"
url: "https://pomodoro3.mircloud.ru"
baseurl: ""
homepage_url: "https://git.org.ru/pomodoro/3"
homepage_name: "GIT.ORG.RU"
color_tomato_baseurl: "/color"
timezone: "Europe/Moscow"
author: "Головин Г.Г."
author_translated: "Golovin G.G."
translation_caption: "translation from Russian"
# build parameters
disable_disk_cache: true
theme: older-tomato-theme
defaults:
- scope:
path: ""
values:
layout: default

View file

@ -0,0 +1,2 @@
<noscript><div><img src="https://mc.yandex.ru/watch/87058815" style="position:absolute; left:-9999px;" alt=""></div></noscript>
<!-- /Yandex.Metrika counter -->

View file

@ -0,0 +1,16 @@
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-209134013-3"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-209134013-3');
</script>
<!-- Yandex.Metrika counter -->
<script>
(function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
m[i].l=1*new Date();for(var j=0;j<document.scripts.length;j++){if(document.scripts[j].src===r){return;}}
k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
(window,document,"script","https://mc.yandex.ru/metrika/tag.js","ym");
ym(87058815,"init",{clickmap:true,trackLinks:true,accurateTrackBounce:true,webvisor:true});
</script>

View file

@ -0,0 +1,248 @@
---
title: Optimizing matrix multiplication
description: Consider an algorithm for multiplying matrices using three nested loops. The complexity of such an algorithm by definition should be O(n³), but there are...
sections: [Permutations,Nested loops,Comparing algorithms]
tags: [java,arrays,multidimensional arrays,matrices,rows,columns,layers,loops]
canonical_url: /en/2021/12/10/optimizing-matrix-multiplication.html
url_translated: /ru/2021/12/09/optimizing-matrix-multiplication.html
title_translated: Оптимизация умножения матриц
date: 2021.12.10
lang: en
---
Consider an algorithm for multiplying matrices using three nested loops. The complexity of such
an algorithm by definition should be `O(n³)`, but there are particularities related to the execution
environment the speed of the algorithm depends on the sequence in which the loops are executed.
Let's compare different permutations of nested loops and the execution time of the algorithms.
Let's take two matrices: {`L×M`} and {`M×N`} &rarr; three loops &rarr; six permutations:
`LMN`, `LNM`, `MLN`, `MNL`, `NLM`, `NML`.
The algorithms that work faster than others are those that write data to the resulting matrix
*row-wise in layers*: `LMN` and `MLN`, the percentage difference to other algorithms is substantial
and depends on the execution environment.
*Further optimization: [Matrix multiplication in parallel streams]({{ '/en/2022/02/09/matrix-multiplication-parallel-streams.html' | relative_url }}).*
## Row-wise algorithm {#row-wise-algorithm}
The outer loop bypasses the rows of the first matrix `L`, then there is a loop across the *common side*
of the two matrices `M` and it is followed by a loop across the columns of the second matrix `N`.
Writing to the resulting matrix occurs row-wise, and each row is filled in layers.
```java
/**
* @param l rows of matrix 'a'
* @param m columns of matrix 'a'
* and rows of matrix 'b'
* @param n columns of matrix 'b'
* @param a first matrix 'l×m'
* @param b second matrix 'm×n'
* @return resulting matrix 'l×n'
*/
public static int[][] matrixMultiplicationLMN(int l, int m, int n, int[][] a, int[][] b) {
// resulting matrix
int[][] c = new int[l][n];
// bypass the indexes of the rows of matrix 'a'
for (int i = 0; i < l; i++)
// bypass the indexes of the common side of two matrices:
// the columns of matrix 'a' and the rows of matrix 'b'
for (int k = 0; k < m; k++)
// bypass the indexes of the columns of matrix 'b'
for (int j = 0; j < n; j++)
// the sum of the products of the elements of the i-th
// row of matrix 'a' and the j-th column of matrix 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
## Layer-wise algorithm {#layer-wise-algorithm}
The outer loop bypasses the *common side* of the two matrices `M`, then there is a loop across the rows
of the first matrix `L`, and it is followed by a loop across the columns of the second matrix `N`.
Writing to the resulting matrix occurs layer-wise, and each layer is filled row-wise.
```java
/**
* @param l rows of matrix 'a'
* @param m columns of matrix 'a'
* and rows of matrix 'b'
* @param n columns of matrix 'b'
* @param a first matrix 'l×m'
* @param b second matrix 'm×n'
* @return resulting matrix 'l×n'
*/
public static int[][] matrixMultiplicationMLN(int l, int m, int n, int[][] a, int[][] b) {
// resulting matrix
int[][] c = new int[l][n];
// bypass the indexes of the common side of two matrices:
// the columns of matrix 'a' and the rows of matrix 'b'
for (int k = 0; k < m; k++)
// bypass the indexes of the rows of matrix 'a'
for (int i = 0; i < l; i++)
// bypass the indexes of the columns of matrix 'b'
for (int j = 0; j < n; j++)
// the sum of the products of the elements of the i-th
// row of matrix 'a' and the j-th column of matrix 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
### Other algorithms {#other-algorithms}
The bypass of the columns of the second matrix `N` occurs before the bypass of the *common side* of the
two matrices `M` and/or before the bypass of the rows of the first matrix `L`.
{% capture collapsed_md %}
```java
public static int[][] matrixMultiplicationLNM(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int i = 0; i < l; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < m; k++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationNLM(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int j = 0; j < n; j++)
for (int i = 0; i < l; i++)
for (int k = 0; k < m; k++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationMNL(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int k = 0; k < m; k++)
for (int j = 0; j < n; j++)
for (int i = 0; i < l; i++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationNML(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int j = 0; j < n; j++)
for (int k = 0; k < m; k++)
for (int i = 0; i < l; i++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Code without comments" content=collapsed_md -%}
## Comparing algorithms {#comparing-algorithms}
To check, we take two matrices `A=[500×700]` and `B=[700×450]`, filled with random numbers. First, we
compare the correctness of the implementation of the algorithms all results obtained must match.
Then we execute each method 10 times and calculate the average execution time.
```java
// start the program and output the result
public static void main(String[] args) throws Exception {
// incoming data
int l = 500, m = 700, n = 450, steps = 10;
int[][] a = randomMatrix(l, m), b = randomMatrix(m, n);
// map of methods for comparison
var methods = new TreeMap<String, Callable<int[][]>>(Map.of(
"LMN", () -> matrixMultiplicationLMN(l, m, n, a, b),
"LNM", () -> matrixMultiplicationLNM(l, m, n, a, b),
"MLN", () -> matrixMultiplicationMLN(l, m, n, a, b),
"MNL", () -> matrixMultiplicationMNL(l, m, n, a, b),
"NLM", () -> matrixMultiplicationNLM(l, m, n, a, b),
"NML", () -> matrixMultiplicationNML(l, m, n, a, b)));
int[][] last = null;
// bypass the methods map, check the correctness of the returned
// results, all results obtained must be equal to each other
for (var method : methods.entrySet()) {
// next method for comparison
var next = methods.higherEntry(method.getKey());
// if the current method is not the last compare the results of two methods
if (next != null) System.out.println(method.getKey() + "=" + next.getKey() + ": "
// compare the result of executing the current method and the next one
+ Arrays.deepEquals(method.getValue().call(), next.getValue().call()));
// the result of the last method
else last = method.getValue().call();
}
int[][] test = last;
// bypass the methods map, measure the execution time of each method
for (var method : methods.entrySet())
// parameters: title, number of steps, runnable code
benchmark(method.getKey(), steps, () -> {
try { // execute the method, get the result
int[][] result = method.getValue().call();
// check the correctness of the results at each step
if (!Arrays.deepEquals(result, test)) System.out.print("error");
} catch (Exception e) {
e.printStackTrace();
}
});
}
```
{% capture collapsed_md %}
```java
// helper method, returns a matrix of the specified size
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// helper method for measuring the execution time of the passed code
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// execution time of one step
System.out.print(" | " + time);
avg += time;
}
// average execution time
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Helper methods" content=collapsed_md -%}
Output depends on the execution environment, time in milliseconds:
```
LMN=LNM: true
LNM=MLN: true
MLN=MNL: true
MNL=NLM: true
NLM=NML: true
LMN | 191 | 109 | 105 | 106 | 105 | 106 | 106 | 105 | 123 | 109 || 116
LNM | 417 | 418 | 419 | 416 | 416 | 417 | 418 | 417 | 416 | 417 || 417
MLN | 113 | 115 | 113 | 115 | 114 | 114 | 114 | 115 | 114 | 113 || 114
MNL | 857 | 864 | 857 | 859 | 860 | 863 | 862 | 860 | 858 | 860 || 860
NLM | 404 | 404 | 407 | 404 | 406 | 405 | 405 | 404 | 403 | 404 || 404
NML | 866 | 872 | 867 | 868 | 867 | 868 | 867 | 873 | 869 | 863 || 868
```
All the methods described above, including collapsed blocks, can be placed in one class.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
```
{% endcapture %}
{%- include collapsed_block.html summary="Required imports" content=collapsed_md -%}

View file

@ -0,0 +1,97 @@
---
title: Matrix rotation 90 degrees
description: Consider the algorithm for rotating a matrix 90 degrees clockwise and anticlockwise. This algorithm is similar to matrix transpose, with the difference...
sections: [Transpose,Comparing algorithms]
tags: [java,arrays,multidimensional arrays,matrices,rows,columns,loops,nested loops]
canonical_url: /en/2021/12/13/matrix-rotation-90-degrees.html
url_translated: /ru/2021/12/12/matrix-rotation-90-degrees.html
title_translated: Поворот матрицы на 90 градусов
date: 2021.12.13
lang: en
---
Consider the algorithm for rotating a matrix 90 degrees clockwise and anticlockwise. This
algorithm is similar to *matrix transpose*, with the difference that here, for each point,
one of the coordinates is mirrored.
```java
// matrix transpose rows and columns are swapped
swapped[j][i] = matrix[i][j];
// clockwise rows are mirrored
rotated[j][i] = matrix[m-i-1][j];
// anticlockwise columns are mirrored
rotated[j][i] = matrix[i][n-j-1];
```
*Similar algorithm: [Matrix rotation 180 degrees]({{ '/en/2021/12/17/matrix-rotation-180-degrees.html' | relative_url }}).*
Let's write a method in Java to rotate a matrix {`m×n`} 90 degrees. The additional parameter
sets the direction of rotation: clockwise or anticlockwise. As an example, let's take a
rectangular matrix {`4×3`}.
```java
/**
* @param m number of rows of the original matrix
* @param n number of columns of the original matrix
* @param clock direction of rotation:
* clockwise or anticlockwise
* @param matrix the original matrix
* @return the rotated matrix
*/
public static int[][] rotateMatrix(int m, int n, boolean clock, int[][] matrix) {
// new matrix, the number of rows and columns are swapped
int[][] rotated = new int[n][m];
// bypass the rows of the original matrix
for (int i = 0; i < m; i++)
// bypass the columns of the original matrix
for (int j = 0; j < n; j++)
if (clock) // clockwise rotation
rotated[j][i] = matrix[m-i-1][j];
else // anticlockwise rotation
rotated[j][i] = matrix[i][n-j-1];
return rotated;
}
```
{% raw %}
```java
// start the program and output the result
public static void main(String[] args) {
// incoming data
int m = 4, n = 3;
int[][] matrix = {{11, 12, 13}, {14, 15, 16}, {17, 18, 19}, {20, 21, 22}};
// rotate the matrix and output the result
outputMatrix("Original matrix:", matrix);
outputMatrix("Clockwise ↻:", rotateMatrix(m, n, true, matrix));
outputMatrix("Anticlockwise ↺:", rotateMatrix(m, n, false, matrix));
}
```
{% endraw %}
```java
// helper method, prints the matrix to the console row-wise
public static void outputMatrix(String title, int[][] matrix) {
System.out.println(title);
for (int[] row : matrix) {
for (int el : row)
System.out.print(" " + el);
System.out.println();
}
}
```
Output:
```
Original matrix:
11 12 13
14 15 16
17 18 19
20 21 22
Clockwise :
20 17 14 11
21 18 15 12
22 19 16 13
Anticlockwise :
13 16 19 22
12 15 18 21
11 14 17 20
```

View file

@ -0,0 +1,85 @@
---
title: Matrix rotation 180 degrees
description: Consider the algorithm for rotating a matrix 180 degrees. Unlike the transpose algorithm, here in the resulting matrix the rows and columns are not swapped...
sections: [Transpose,Comparing algorithms]
tags: [java,arrays,multidimensional arrays,matrices,rows,columns,loops,nested loops]
canonical_url: /en/2021/12/17/matrix-rotation-180-degrees.html
url_translated: /ru/2021/12/16/matrix-rotation-180-degrees.html
title_translated: Поворот матрицы на 180 градусов
date: 2021.12.17
lang: en
---
Consider the algorithm for rotating a matrix 180 degrees. Unlike the *transpose* algorithm,
here in the resulting matrix the rows and columns are not swapped, but are mirrored.
```java
// rows and columns are swapped
swapped[j][i] = matrix[i][j];
// rows and columns are mirrored
rotated[i][j] = matrix[m-i-1][n-j-1];
```
*Similar algorithm: [Matrix rotation 90 degrees]({{ '/en/2021/12/13/matrix-rotation-90-degrees.html' | relative_url }}).*
Let's write a method in Java to rotate a matrix {`m×n`} 180 degrees. As an example,
let's take a rectangular matrix {`4×3`}.
```java
/**
* @param m number of rows of the original matrix
* @param n number of columns of the original matrix
* @param matrix the original matrix
* @return the rotated matrix
*/
public static int[][] rotateMatrix(int m, int n, int[][] matrix) {
// new matrix
int[][] rotated = new int[m][n];
// bypass the rows of the original matrix
for (int i = 0; i < m; i++)
// bypass the columns of the original matrix
for (int j = 0; j < n; j++)
// rows and columns are mirrored
rotated[i][j] = matrix[m-i-1][n-j-1];
return rotated;
}
```
{% raw %}
```java
// start the program and output the result
public static void main(String[] args) {
// incoming data
int m = 4, n = 3;
int[][] matrix = {{11, 12, 13}, {14, 15, 16}, {17, 18, 19}, {20, 21, 22}};
// rotate the matrix and output the result
outputMatrix("Original matrix:", matrix);
outputMatrix("Rotation by 180°:", rotateMatrix(m, n, matrix));
}
```
{% endraw %}
```java
// helper method, prints the matrix to the console row-wise
public static void outputMatrix(String title, int[][] matrix) {
System.out.println(title);
for (int[] row : matrix) {
for (int el : row)
System.out.print(" " + el);
System.out.println();
}
}
```
Output:
```
Original matrix:
11 12 13
14 15 16
17 18 19
20 21 22
Rotation by 180°:
22 21 20
19 18 17
16 15 14
13 12 11
```

View file

@ -0,0 +1,184 @@
---
title: Matrix multiplication in parallel streams
description: Consider an algorithm for multiplying rectangular matrices using Java Streams. Let's take the optimized version of the algorithm using three nested loops...
sections: [Multithreading,Nested loops,Comparing algorithms]
tags: [java,streams,arrays,multidimensional arrays,matrices,rows,loops]
canonical_url: /en/2022/02/09/matrix-multiplication-parallel-streams.html
url_translated: /ru/2022/02/08/matrix-multiplication-parallel-streams.html
title_translated: Умножение матриц в параллельных потоках
date: 2022.02.09
lang: en
---
Consider an algorithm for multiplying rectangular matrices using Java Streams. Let's take the
*optimized version* of the algorithm using three nested loops and replace the outer loop with
a stream. Let's compare the operating time of two algorithms.
We process the rows of the resulting matrix in parallel mode, and populate each row layerwise. Due to
the parallel use of cache of the execution environment on multi-core machines, the computation time can
be reduced by more than two times. To check, let's take two rectangular matrices {`L×M`} and {`M×N`}.
*Further optimization: [Winograd Strassen algorithm]({{ '/en/2022/02/11/winograd-strassen-algorithm.html' | relative_url }}).*
## Parallel stream {#parallel-stream}
We bypass the rows of the first matrix `L` in parallel mode. In each thread there are two nested loops:
across the *common side* of two matrices `M` and across the columns of the second matrix `N`. Processing
of the rows of the resulting matrix occurs independently of each other in parallel streams.
```java
/**
* @param l rows of matrix 'a'
* @param m columns of matrix 'a'
* and rows of matrix 'b'
* @param n columns of matrix 'b'
* @param a first matrix 'l×m'
* @param b second matrix 'm×n'
* @return resulting matrix 'l×n'
*/
public static int[][] parallelMatrixMultiplication(int l, int m, int n, int[][] a, int[][] b) {
// resulting matrix
int[][] c = new int[l][n];
// bypass the indexes of the rows of matrix 'a' in parallel mode
IntStream.range(0, l).parallel().forEach(i -> {
// bypass the indexes of the common side of two matrices:
// the columns of matrix 'a' and the rows of matrix 'b'
for (int k = 0; k < m; k++)
// bypass the indexes of the columns of matrix 'b'
for (int j = 0; j < n; j++)
// the sum of the products of the elements of the i-th
// row of matrix 'a' and the j-th column of matrix 'b'
c[i][j] += a[i][k] * b[k][j];
});
return c;
}
```
## Three nested loops {#three-nested-loops}
We take the *optimized* variant of algorithm, that uses cache of the execution environment better
than others. The outer loop bypasses the rows of the first matrix `L`, then there is a loop across
the *common side* of the two matrices `M` and it is followed by a loop across the columns of the
second matrix `N`. Writing to the resulting matrix occurs row-wise, and each row is filled in layers.
```java
/**
* @param l rows of matrix 'a'
* @param m columns of matrix 'a'
* and rows of matrix 'b'
* @param n columns of matrix 'b'
* @param a first matrix 'l×m'
* @param b second matrix 'm×n'
* @return resulting matrix 'l×n'
*/
public static int[][] sequentialMatrixMultiplication(int l, int m, int n, int[][] a, int[][] b) {
// resulting matrix
int[][] c = new int[l][n];
// bypass the indexes of the rows of matrix 'a'
for (int i = 0; i < l; i++)
// bypass the indexes of the common side of two matrices:
// the columns of matrix 'a' and the rows of matrix 'b'
for (int k = 0; k < m; k++)
// bypass the indexes of the columns of matrix 'b'
for (int j = 0; j < n; j++)
// the sum of the products of the elements of the i-th
// row of matrix 'a' and the j-th column of matrix 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
## Testing {#testing}
To check, we take two matrices `A=[900×1000]` and `B=[1000×750]`, filled with random numbers.
First, we compare the correctness of the implementation of the two algorithms matrix products
must match. Then we execute each method 10 times and calculate the average execution time.
```java
// start the program and output the result
public static void main(String[] args) {
// incoming data
int l = 900, m = 1000, n = 750, steps = 10;
int[][] a = randomMatrix(l, m), b = randomMatrix(m, n);
// matrix products
int[][] c1 = parallelMatrixMultiplication(l, m, n, a, b);
int[][] c2 = sequentialMatrixMultiplication(l, m, n, a, b);
// check the correctness of the results
System.out.println("The results match: " + Arrays.deepEquals(c1, c2));
// measure the execution time of two methods
benchmark("Stream", steps, () -> {
int[][] c = parallelMatrixMultiplication(l, m, n, a, b);
if (!Arrays.deepEquals(c, c1)) System.out.print("error");
});
benchmark("Loops", steps, () -> {
int[][] c = sequentialMatrixMultiplication(l, m, n, a, b);
if (!Arrays.deepEquals(c, c2)) System.out.print("error");
});
}
```
{% capture collapsed_md %}
```java
// helper method, returns a matrix of the specified size
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// helper method for measuring the execution time of the passed code
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// execution time of one step
System.out.print(" | " + time);
avg += time;
}
// average execution time
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Helper methods" content=collapsed_md -%}
Output depends on the execution environment, time in milliseconds:
```
The results match: true
Stream | 113 | 144 | 117 | 114 | 114 | 117 | 120 | 125 | 111 | 113 || 118
Loops | 1357 | 530 | 551 | 569 | 535 | 538 | 525 | 517 | 518 | 514 || 615
```
## Comparing algorithms {#comparing-algorithms}
On an eight-core Linux x64 computer, we create a Windows x64 virtual machine for tests. All
other things being equal, in the settings, we change the number of processors. We run the
above test 100 times instead of 10 we get a summary table of results. Time in milliseconds.
```
CPU | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
-------|-----|-----|-----|-----|-----|-----|-----|-----|
Stream | 522 | 276 | 179 | 154 | 128 | 112 | 110 | 93 |
Loops | 519 | 603 | 583 | 571 | 545 | 571 | 559 | 467 |
```
Results: with a large number of processors in the system, the multithreaded algorithm works
out noticeably faster than three nested loops. The computation time can be reduced by more
than two times.
All the methods described above, including collapsed blocks, can be placed in one class.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.stream.IntStream;
```
{% endcapture %}
{%- include collapsed_block.html summary="Required imports" content=collapsed_md -%}

View file

@ -0,0 +1,298 @@
---
title: Winograd Strassen algorithm
description: Consider a modification of Strassen's algorithm for square matrix multiplication with fewer number of summations between blocks than in the ordinary...
sections: [Multithreading,Block matrices,Comparing algorithms]
tags: [java,streams,arrays,multidimensional arrays,matrices,recursion,loops,nested loops]
canonical_url: /en/2022/02/11/winograd-strassen-algorithm.html
url_translated: /ru/2022/02/10/winograd-strassen-algorithm.html
title_translated: Алгоритм Винограда Штрассена
date: 2022.02.11
lang: en
---
Consider a modification of Strassen's algorithm for square matrix multiplication with *fewer* number
of summations between blocks than in the ordinary algorithm 15 instead of 18 and the same number
of multiplications as in the ordinary algorithm 7. We will use Java Streams.
Recursive partitioning of matrices into blocks during multiplication makes sense up to a certain
limit, and then it loses its sense, since the Strassen's algorithm does not use cache of the execution
environment. Therefore, for small blocks we will use a parallel version of nested loops, and for large
blocks we will perform recursive partitioning in parallel.
We determine the boundary between the two algorithms experimentally we adjust it to the cache of the
execution environment. The benefit of Strassen's algorithm becomes more evident on sizable matrices
the difference with the algorithm using nested loops becomes larger and depends on the execution
environment. Let's compare the operating time of two algorithms.
*Algorithm using three nested loops: [Optimizing matrix multiplication]({{ '/en/2021/12/10/optimizing-matrix-multiplication.html' | relative_url }}).*
## Algorithm description {#algorithm-description}
Matrices must be the same size. We partition each matrix into 4 equally sized blocks. The blocks
must be square, therefore if this is not the case, then first we supplement the matrices with zero
rows and columns, and after that partition them into blocks. We will remove the redundant rows and
columns later from the resulting matrix.
{% include image_svg.html src="/img/block-matrices.svg" style="width:221pt; height:33pt;"
alt="{\displaystyle A={\begin{pmatrix}A_{11}&A_{12}\\A_{21}&A_{22}\end{pmatrix}},\quad B={\begin{pmatrix}B_{11}&B_{12}\\B_{21}&B_{22}\end{pmatrix}}.}" %}
Summation of blocks.
{% include image_svg.html src="/img/sums1.svg" style="width:101pt; height:148pt;"
alt="{\displaystyle{\begin{aligned}S_{1}&=(A_{21}+A_{22});\\S_{2}&=(S_{1}-A_{11});\\S_{3}&=(A_{11}-A_{21});\\S_{4}&=(A_{12}-S_{2});\\S_{5}&=(B_{12}-B_{11});\\S_{6}&=(B_{22}-S_{5});\\S_{7}&=(B_{22}-B_{12});\\S_{8}&=(S_{6}-B_{21}).\end{aligned}}}" %}
Multiplication of blocks.
{% include image_svg.html src="/img/products.svg" style="width:75pt; height:127pt;"
alt="{\displaystyle{\begin{aligned}P_{1}&=S_{2}S_{6};\\P_{2}&=A_{11}B_{11};\\P_{3}&=A_{12}B_{21};\\P_{4}&=S_{3}S_{7};\\P_{5}&=S_{1}S_{5};\\P_{6}&=S_{4}B_{22};\\P_{7}&=A_{22}S_{8}.\end{aligned}}}" %}
Summation of blocks.
{% include image_svg.html src="/img/sums2.svg" style="width:78pt; height:31pt;"
alt="{\displaystyle{\begin{aligned}T_{1}&=P_{1}+P_{2};\\T_{2}&=T_{1}+P_{4}.\end{aligned}}}" %}
Blocks of the resulting matrix.
{% include image_svg.html src="/img/sums3.svg" style="width:240pt; height:33pt;"
alt="{\displaystyle{\begin{pmatrix}C_{11}&C_{12}\\C_{21}&C_{22}\end{pmatrix}}={\begin{pmatrix}P_{2}+P_{3}&T_{1}+P_{5}+P_{6}\\T_{2}-P_{7}&T_{2}+P_{5}\end{pmatrix}}.}" %}
## Hybrid algorithm {#hybrid-algorithm}
We partition each matrix `A` and `B` into 4 equally sized blocks and, if necessary, we supplement
the missing parts with zeros. Perform 15 summations and 7 multiplications over the blocks we get
4 blocks of the matrix `C`. Remove the redundant zeros, if added, and return the resulting matrix.
We run recursive partitioning of large blocks in parallel mode, and for small blocks we call the
algorithm with nested loops.
```java
/**
* @param n matrix size
* @param brd minimum matrix size
* @param a first matrix 'n×n'
* @param b second matrix 'n×n'
* @return resulting matrix 'n×n'
*/
public static int[][] multiplyMatrices(int n, int brd, int[][] a, int[][] b) {
// multiply small blocks using algorithm with nested loops
if (n < brd) return simpleMultiplication(n, a, b);
// midpoint of the matrix, round up blocks should
// be square, if necessary add zero rows and columns
int m = n - n / 2;
// blocks of the first matrix
int[][] a11 = getQuadrant(m, n, a, true, true);
int[][] a12 = getQuadrant(m, n, a, true, false);
int[][] a21 = getQuadrant(m, n, a, false, true);
int[][] a22 = getQuadrant(m, n, a, false, false);
// blocks of the second matrix
int[][] b11 = getQuadrant(m, n, b, true, true);
int[][] b12 = getQuadrant(m, n, b, true, false);
int[][] b21 = getQuadrant(m, n, b, false, true);
int[][] b22 = getQuadrant(m, n, b, false, false);
// summation of blocks
int[][] s1 = sumMatrices(m, a21, a22, true);
int[][] s2 = sumMatrices(m, s1, a11, false);
int[][] s3 = sumMatrices(m, a11, a21, false);
int[][] s4 = sumMatrices(m, a12, s2, false);
int[][] s5 = sumMatrices(m, b12, b11, false);
int[][] s6 = sumMatrices(m, b22, s5, false);
int[][] s7 = sumMatrices(m, b22, b12, false);
int[][] s8 = sumMatrices(m, s6, b21, false);
int[][][] p = new int[7][][];
// multiplication of blocks in parallel streams
IntStream.range(0, 7).parallel().forEach(i -> {
switch (i) { // recursive calls
case 0: p[i] = multiplyMatrices(m, brd, s2, s6); break;
case 1: p[i] = multiplyMatrices(m, brd, a11, b11); break;
case 2: p[i] = multiplyMatrices(m, brd, a12, b21); break;
case 3: p[i] = multiplyMatrices(m, brd, s3, s7); break;
case 4: p[i] = multiplyMatrices(m, brd, s1, s5); break;
case 5: p[i] = multiplyMatrices(m, brd, s4, b22); break;
case 6: p[i] = multiplyMatrices(m, brd, a22, s8); break;
}
});
// summation of blocks
int[][] t1 = sumMatrices(m, p[0], p[1], true);
int[][] t2 = sumMatrices(m, t1, p[3], true);
// blocks of the resulting matrix
int[][] c11 = sumMatrices(m, p[1], p[2], true);
int[][] c12 = sumMatrices(m, t1, sumMatrices(m, p[4], p[5], true), true);
int[][] c21 = sumMatrices(m, t2, p[6], false);
int[][] c22 = sumMatrices(m, t2, p[4], true);
// assemble a matrix from blocks,
// remove zero rows and columns, if added
return putQuadrants(m, n, c11, c12, c21, c22);
}
```
{% capture collapsed_md %}
```java
// helper method for matrix summation
private static int[][] sumMatrices(int n, int[][] a, int[][] b, boolean sign) {
int[][] c = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
c[i][j] = sign ? a[i][j] + b[i][j] : a[i][j] - b[i][j];
return c;
}
```
```java
// helper method, gets a block of a matrix
private static int[][] getQuadrant(int m, int n, int[][] x,
boolean first, boolean second) {
int[][] q = new int[m][m];
if (first) for (int i = 0; i < m; i++)
if (second) System.arraycopy(x[i], 0, q[i], 0, m); // x11
else System.arraycopy(x[i], m, q[i], 0, n - m); // x12
else for (int i = m; i < n; i++)
if (second) System.arraycopy(x[i], 0, q[i - m], 0, m); // x21
else System.arraycopy(x[i], m, q[i - m], 0, n - m); // x22
return q;
}
```
```java
// helper method, assembles a matrix from blocks
private static int[][] putQuadrants(int m, int n,
int[][] x11, int[][] x12,
int[][] x21, int[][] x22) {
int[][] x = new int[n][n];
for (int i = 0; i < n; i++)
if (i < m) {
System.arraycopy(x11[i], 0, x[i], 0, m);
System.arraycopy(x12[i], 0, x[i], m, n - m);
} else {
System.arraycopy(x21[i - m], 0, x[i], 0, m);
System.arraycopy(x22[i - m], 0, x[i], m, n - m);
}
return x;
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Helper methods" content=collapsed_md -%}
## Nested loops {#nested-loops}
To supplement the previous algorithm and to compare with it, we take the *optimized* variant of nested
loops, that uses cache of the execution environment better than others processing of the rows of the
resulting matrix occurs independently of each other in parallel streams. For small matrices, we use this
algorithm large matrices we partition into small blocks and use the same algorithm.
```java
/**
* @param n matrix size
* @param a first matrix 'n×n'
* @param b second matrix 'n×n'
* @return resulting matrix 'n×n'
*/
public static int[][] simpleMultiplication(int n, int[][] a, int[][] b) {
// the resulting matrix
int[][] c = new int[n][n];
// bypass the rows of matrix 'a' in parallel mode
IntStream.range(0, n).parallel().forEach(i -> {
// bypass the indexes of the common side of two matrices:
// the columns of matrix 'a' and the rows of matrix 'b'
for (int k = 0; k < n; k++)
// bypass the indexes of the columns of matrix 'b'
for (int j = 0; j < n; j++)
// the sum of the products of the elements of the i-th
// row of matrix 'a' and the j-th column of matrix 'b'
c[i][j] += a[i][k] * b[k][j];
});
return c;
}
```
## Testing {#testing}
To check, we take two square matrices `A=[1000×1000]` and `B=[1000×1000]`, filled with random numbers.
Take the minimum block size `[200×200]` elements. First, we compare the correctness of the implementation
of the two algorithms matrix products must match. Then we execute each method 10 times and calculate
the average execution time.
```java
// start the program and output the result
public static void main(String[] args) {
// incoming data
int n = 1000, brd = 200, steps = 10;
int[][] a = randomMatrix(n, n), b = randomMatrix(n, n);
// matrix products
int[][] c1 = multiplyMatrices(n, brd, a, b);
int[][] c2 = simpleMultiplication(n, a, b);
// check the correctness of the results
System.out.println("The results match: " + Arrays.deepEquals(c1, c2));
// measure the execution time of two methods
benchmark("Hybrid algorithm", steps, () -> {
int[][] c = multiplyMatrices(n, brd, a, b);
if (!Arrays.deepEquals(c, c1)) System.out.print("error");
});
benchmark("Nested loops ", steps, () -> {
int[][] c = simpleMultiplication(n, a, b);
if (!Arrays.deepEquals(c, c2)) System.out.print("error");
});
}
```
{% capture collapsed_md %}
```java
// helper method, returns a matrix of the specified size
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// helper method for measuring the execution time of the passed code
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// execution time of one step
System.out.print(" | " + time);
avg += time;
}
// average execution time
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Helper methods" content=collapsed_md -%}
Output depends on the execution environment, time in milliseconds:
```
The results match: true
Hybrid algorithm | 196 | 177 | 156 | 205 | 154 | 165 | 133 | 118 | 132 | 134 || 157
Nested loops | 165 | 164 | 168 | 167 | 168 | 168 | 170 | 179 | 173 | 168 || 169
```
## Comparing algorithms {#comparing-algorithms}
On an eight-core Linux x64 computer, execute the above test 100 times instead of 10. Take the minimum
block size `[brd=200]` elements. Change only `n` sizes of both matrices `A=[n×n]` and `B=[n×n]`. Get
a summary table of results. Time in milliseconds.
```
n | 900 | 1000 | 1100 | 1200 | 1300 | 1400 | 1500 | 1600 | 1700 |
-----------------|-----|------|------|------|------|------|------|------|------|
Hybrid algorithm | 96 | 125 | 169 | 204 | 260 | 313 | 384 | 482 | 581 |
Nested loops | 119 | 162 | 235 | 281 | 361 | 497 | 651 | 793 | 971 |
```
Results: the benefit of the Strassen algorithm becomes more evident on large matrices, when the
size of the matrix itself is several times larger than the size of the minimal block, and depends
on the execution environment.
All the methods described above, including collapsed blocks, can be placed in one class.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.stream.IntStream;
```
{% endcapture %}
{%- include collapsed_block.html summary="Required imports" content=collapsed_md -%}

76
jekyll_site/en/index.md Normal file
View file

@ -0,0 +1,76 @@
---
title: Code with comments
description: Notes about programming with code snippets and comments. Problem solutions and solution descriptions.
sections: [Problem solutions and solution descriptions]
tags: [java,algorithms,implementation,arrays,multidimensional arrays,matrices,loops,streams]
canonical_url: /en/
url_translated: /ru/
title_translated: Код с комментариями
lang: en
---
{%- assign articles = "" | split: "" %}
{%- assign articles = articles | push: "Winograd — Strassen algorithm" %}
{%- capture article_brief %}
Consider a modification of Strassen's algorithm for square matrix multiplication with *fewer* number
of summations between blocks than in the ordinary algorithm 15 instead of 18 and the same number
of multiplications as in the ordinary algorithm 7. We will use Java Streams.
Recursive partitioning of matrices into blocks during multiplication makes sense up to a certain
limit, and then it loses its sense, since the Strassen's algorithm does not use cache of the execution
environment. Therefore, for small blocks we will use a parallel version of nested loops, and for large
blocks we will perform recursive partitioning in parallel.
We determine the boundary between the two algorithms experimentally we adjust it to the cache of the
execution environment. The benefit of Strassen's algorithm becomes more evident on sizable matrices
the difference with the algorithm using nested loops becomes larger and depends on the execution
environment. Let's compare the operating time of two algorithms.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Matrix multiplication in parallel streams" %}
{%- capture article_brief %}
Consider an algorithm for multiplying rectangular matrices using Java Streams. Let's take the
*optimized version* of the algorithm using three nested loops and replace the outer loop with
a stream. Let's compare the operating time of two algorithms.
We process the rows of the resulting matrix in parallel mode, and populate each row layerwise. Due to
the parallel use of cache of the execution environment on multi-core machines, the computation time can
be reduced by more than two times. To check, let's take two rectangular matrices {`L×M`} and {`M×N`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Matrix rotation 180 degrees" %}
{%- capture article_brief %}
Consider the algorithm for rotating a matrix 180 degrees. Unlike the *transpose* algorithm,
here in the resulting matrix the rows and columns are not swapped, but are mirrored.
Let's write a method in Java to rotate a matrix {`m×n`} 180 degrees. As an example,
let's take a rectangular matrix {`4×3`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Matrix rotation 90 degrees" %}
{%- capture article_brief %}
Consider the algorithm for rotating a matrix 90 degrees clockwise and anticlockwise. This
algorithm is similar to *matrix transpose*, with the difference that here, for each point,
one of the coordinates is mirrored.
Let's write a method in Java to rotate a matrix {`m×n`} 90 degrees. The additional parameter
sets the direction of rotation: clockwise or anticlockwise. As an example, let's take a
rectangular matrix {`4×3`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Optimizing matrix multiplication" %}
{%- capture article_brief %}
Consider an algorithm for multiplying matrices using three nested loops. The complexity of such
an algorithm by definition should be `O(n³)`, but there are particularities related to the execution
environment the speed of the algorithm depends on the sequence in which the loops are executed.
Let's compare different permutations of nested loops and the execution time of the algorithms.
Let's take two matrices: {`L×M`} and {`M×N`} &rarr; three loops &rarr; six permutations:
`LMN`, `LNM`, `MLN`, `MNL`, `NLM`, `NML`.
The algorithms that work faster than others are those that write data to the resulting matrix
*row-wise in layers*: `LMN` and `MLN`, the percentage difference to other algorithms is substantial
and depends on the execution environment.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- include main_page.html articles = articles -%}

View file

@ -0,0 +1,50 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="220.625102pt" height="32.408907pt" viewBox="-.239051 -.228392 220.625102 32.408907">
<defs>
<path id="g2-49" d="M2.502615-5.076961C2.502615-5.292154 2.486675-5.300125 2.271482-5.300125C1.944707-4.98132 1.522291-4.790037 .765131-4.790037V-4.527024C.980324-4.527024 1.41071-4.527024 1.872976-4.742217V-.653549C1.872976-.358655 1.849066-.263014 1.091905-.263014H.812951V0C1.139726-.02391 1.825156-.02391 2.183811-.02391S3.235866-.02391 3.56264 0V-.263014H3.283686C2.526526-.263014 2.502615-.358655 2.502615-.653549V-5.076961Z"/>
<path id="g2-50" d="M2.247572-1.625903C2.375093-1.745455 2.709838-2.008468 2.83736-2.12005C3.331507-2.574346 3.801743-3.012702 3.801743-3.737983C3.801743-4.686426 3.004732-5.300125 2.008468-5.300125C1.052055-5.300125 .422416-4.574844 .422416-3.865504C.422416-3.474969 .73325-3.419178 .844832-3.419178C1.012204-3.419178 1.259278-3.53873 1.259278-3.841594C1.259278-4.25604 .860772-4.25604 .765131-4.25604C.996264-4.837858 1.530262-5.037111 1.920797-5.037111C2.662017-5.037111 3.044583-4.407472 3.044583-3.737983C3.044583-2.909091 2.462765-2.303362 1.522291-1.338979L.518057-.302864C.422416-.215193 .422416-.199253 .422416 0H3.57061L3.801743-1.42665H3.55467C3.53076-1.267248 3.466999-.868742 3.371357-.71731C3.323537-.653549 2.717808-.653549 2.590286-.653549H1.171606L2.247572-1.625903Z"/>
<path id="g0-18" d="M8.368618 28.08269C8.368618 28.034869 8.344707 28.010959 8.320797 27.975093C7.878456 27.532752 7.07746 26.731756 6.276463 25.440598C4.351681 22.356164 3.478954 18.470735 3.478954 13.867995C3.478954 10.652055 3.90934 6.503611 5.881943 2.940971C6.826401 1.243337 7.806725 .263014 8.332752-.263014C8.368618-.298879 8.368618-.32279 8.368618-.358655C8.368618-.478207 8.284932-.478207 8.117559-.478207S7.926276-.478207 7.746949-.298879C3.741968 3.347447 2.486675 8.822914 2.486675 13.85604C2.486675 18.554421 3.56264 23.288667 6.599253 26.863263C6.838356 27.138232 7.292653 27.628394 7.782814 28.05878C7.926276 28.202242 7.950187 28.202242 8.117559 28.202242S8.368618 28.202242 8.368618 28.08269Z"/>
<path id="g0-19" d="M6.300374 13.867995C6.300374 9.169614 5.224408 4.435367 2.187796 .860772C1.948692 .585803 1.494396 .095641 1.004234-.334745C.860772-.478207 .836862-.478207 .669489-.478207C.526027-.478207 .418431-.478207 .418431-.358655C.418431-.310834 .466252-.263014 .490162-.239103C.908593 .191283 1.709589 .992279 2.510585 2.283437C4.435367 5.36787 5.308095 9.2533 5.308095 13.85604C5.308095 17.07198 4.877709 21.220423 2.905106 24.783064C1.960648 26.480697 .968369 27.472976 .466252 27.975093C.442341 28.010959 .418431 28.046824 .418431 28.08269C.418431 28.202242 .526027 28.202242 .669489 28.202242C.836862 28.202242 .860772 28.202242 1.0401 28.022914C5.045081 24.376588 6.300374 18.901121 6.300374 13.867995Z"/>
<path id="g3-61" d="M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z"/>
<path id="g1-58" d="M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z"/>
<path id="g1-59" d="M2.331258 .047821C2.331258-.645579 2.10411-1.159651 1.613948-1.159651C1.231382-1.159651 1.0401-.848817 1.0401-.585803S1.219427 0 1.625903 0C1.78132 0 1.912827-.047821 2.020423-.155417C2.044334-.179328 2.056289-.179328 2.068244-.179328C2.092154-.179328 2.092154-.011955 2.092154 .047821C2.092154 .442341 2.020423 1.219427 1.327024 1.996513C1.195517 2.139975 1.195517 2.163885 1.195517 2.187796C1.195517 2.247572 1.255293 2.307347 1.315068 2.307347C1.41071 2.307347 2.331258 1.422665 2.331258 .047821Z"/>
<path id="g1-65" d="M2.032379-1.327024C1.613948-.621669 1.207472-.382565 .633624-.3467C.502117-.334745 .406476-.334745 .406476-.119552C.406476-.047821 .466252 0 .549938 0C.765131 0 1.303113-.02391 1.518306-.02391C1.865006-.02391 2.247572 0 2.582316 0C2.654047 0 2.797509 0 2.797509-.227148C2.797509-.334745 2.701868-.3467 2.630137-.3467C2.355168-.37061 2.12802-.466252 2.12802-.753176C2.12802-.920548 2.199751-1.052055 2.355168-1.315068L3.263761-2.82142H6.312329C6.324284-2.713823 6.324284-2.618182 6.336239-2.510585C6.372105-2.199751 6.515567-.956413 6.515567-.729265C6.515567-.37061 5.905853-.3467 5.71457-.3467C5.583064-.3467 5.451557-.3467 5.451557-.131507C5.451557 0 5.559153 0 5.630884 0C5.834122 0 6.073225-.02391 6.276463-.02391H6.957908C7.687173-.02391 8.2132 0 8.225156 0C8.308842 0 8.440349 0 8.440349-.227148C8.440349-.3467 8.332752-.3467 8.153425-.3467C7.49589-.3467 7.483935-.454296 7.44807-.812951L6.718804-8.272976C6.694894-8.51208 6.647073-8.53599 6.515567-8.53599C6.396015-8.53599 6.324284-8.51208 6.216687-8.332752L2.032379-1.327024ZM3.466999-3.16812L5.869988-7.185056L6.276463-3.16812H3.466999Z"/>
<path id="g1-66" d="M4.375592-7.352428C4.483188-7.79477 4.531009-7.81868 4.99726-7.81868H6.551432C7.902366-7.81868 7.902366-6.670984 7.902366-6.563387C7.902366-5.595019 6.933998-4.363636 5.355915-4.363636H3.634371L4.375592-7.352428ZM6.396015-4.267995C7.699128-4.507098 8.88269-5.415691 8.88269-6.515567C8.88269-7.44807 8.057783-8.16538 6.706849-8.16538H2.86924C2.642092-8.16538 2.534496-8.16538 2.534496-7.938232C2.534496-7.81868 2.642092-7.81868 2.82142-7.81868C3.550685-7.81868 3.550685-7.723039 3.550685-7.591532C3.550685-7.567621 3.550685-7.49589 3.502864-7.316563L1.888917-.884682C1.78132-.466252 1.75741-.3467 .920548-.3467C.6934-.3467 .573848-.3467 .573848-.131507C.573848 0 .645579 0 .884682 0H4.985305C6.814446 0 8.225156-1.3868 8.225156-2.594271C8.225156-3.574595 7.364384-4.172354 6.396015-4.267995ZM4.698381-.3467H3.084433C2.917061-.3467 2.893151-.3467 2.82142-.358655C2.689913-.37061 2.677958-.394521 2.677958-.490162C2.677958-.573848 2.701868-.645579 2.725778-.753176L3.56264-4.124533H5.810212C7.220922-4.124533 7.220922-2.809465 7.220922-2.713823C7.220922-1.566127 6.180822-.3467 4.698381-.3467Z"/>
</defs>
<g fill="#222" stroke="#222" style="fill: var(--color, #222); stroke: var(--color, #222);" stroke-width="0.3" transform="matrix(1.13 0 0 1.13 -63.986043 -61.132812)">
<use x="56.413267" y="71.232862" xlink:href="#g1-65"/>
<use x="68.509443" y="71.232862" xlink:href="#g3-61"/>
<use x="80.934924" y="54.375924" xlink:href="#g0-18"/>
<use x="89.735297" y="63.910285" xlink:href="#g1-65"/>
<use x="98.510643" y="65.703548" xlink:href="#g2-49"/>
<use x="102.744826" y="65.703548" xlink:href="#g2-49"/>
<use x="117.43977" y="63.910285" xlink:href="#g1-65"/>
<use x="126.215116" y="65.703548" xlink:href="#g2-49"/>
<use x="130.449299" y="65.703548" xlink:href="#g2-50"/>
<use x="89.735297" y="78.356097" xlink:href="#g1-65"/>
<use x="98.510643" y="80.149361" xlink:href="#g2-50"/>
<use x="102.744826" y="80.149361" xlink:href="#g2-49"/>
<use x="117.43977" y="78.356097" xlink:href="#g1-65"/>
<use x="126.215116" y="80.149361" xlink:href="#g2-50"/>
<use x="130.449299" y="80.149361" xlink:href="#g2-50"/>
<use x="135.181603" y="54.375924" xlink:href="#g0-19"/>
<use x="143.981975" y="71.232862" xlink:href="#g1-59"/>
<use x="160.932115" y="71.232862" xlink:href="#g1-66"/>
<use x="173.749205" y="71.232862" xlink:href="#g3-61"/>
<use x="186.174686" y="54.375924" xlink:href="#g0-18"/>
<use x="194.975058" y="63.910285" xlink:href="#g1-66"/>
<use x="203.868021" y="65.703548" xlink:href="#g2-49"/>
<use x="208.102204" y="65.703548" xlink:href="#g2-49"/>
<use x="222.797148" y="63.910285" xlink:href="#g1-66"/>
<use x="231.690111" y="65.703548" xlink:href="#g2-49"/>
<use x="235.924293" y="65.703548" xlink:href="#g2-50"/>
<use x="194.975058" y="78.356097" xlink:href="#g1-66"/>
<use x="203.868021" y="80.149361" xlink:href="#g2-50"/>
<use x="208.102204" y="80.149361" xlink:href="#g2-49"/>
<use x="222.797148" y="78.356097" xlink:href="#g1-66"/>
<use x="231.690111" y="80.149361" xlink:href="#g2-50"/>
<use x="235.924293" y="80.149361" xlink:href="#g2-50"/>
<use x="240.656597" y="54.375924" xlink:href="#g0-19"/>
<use x="249.45697" y="71.232862" xlink:href="#g1-58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.2 KiB

View file

@ -0,0 +1,84 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="74.42272pt" height="126.555483pt" viewBox="-.299738 -.250564 74.42272 126.555483">
<defs>
<path id="g2-59" d="M2.199751-4.578829C2.199751-4.901619 1.924782-5.152677 1.625903-5.152677C1.279203-5.152677 1.0401-4.877709 1.0401-4.578829C1.0401-4.220174 1.338979-3.993026 1.613948-3.993026C1.936737-3.993026 2.199751-4.244085 2.199751-4.578829ZM1.996513-.119552C1.996513 .298879 1.996513 1.147696 1.267248 2.044334C1.195517 2.139975 1.195517 2.163885 1.195517 2.187796C1.195517 2.247572 1.255293 2.307347 1.315068 2.307347C1.398755 2.307347 2.235616 1.422665 2.235616 .02391C2.235616-.418431 2.199751-1.159651 1.613948-1.159651C1.267248-1.159651 1.0401-.896638 1.0401-.585803C1.0401-.263014 1.267248 0 1.625903 0C1.853051 0 1.936737-.071731 1.996513-.119552Z"/>
<path id="g2-61" d="M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z"/>
<path id="g1-49" d="M2.502615-5.076961C2.502615-5.292154 2.486675-5.300125 2.271482-5.300125C1.944707-4.98132 1.522291-4.790037 .765131-4.790037V-4.527024C.980324-4.527024 1.41071-4.527024 1.872976-4.742217V-.653549C1.872976-.358655 1.849066-.263014 1.091905-.263014H.812951V0C1.139726-.02391 1.825156-.02391 2.183811-.02391S3.235866-.02391 3.56264 0V-.263014H3.283686C2.526526-.263014 2.502615-.358655 2.502615-.653549V-5.076961Z"/>
<path id="g1-50" d="M2.247572-1.625903C2.375093-1.745455 2.709838-2.008468 2.83736-2.12005C3.331507-2.574346 3.801743-3.012702 3.801743-3.737983C3.801743-4.686426 3.004732-5.300125 2.008468-5.300125C1.052055-5.300125 .422416-4.574844 .422416-3.865504C.422416-3.474969 .73325-3.419178 .844832-3.419178C1.012204-3.419178 1.259278-3.53873 1.259278-3.841594C1.259278-4.25604 .860772-4.25604 .765131-4.25604C.996264-4.837858 1.530262-5.037111 1.920797-5.037111C2.662017-5.037111 3.044583-4.407472 3.044583-3.737983C3.044583-2.909091 2.462765-2.303362 1.522291-1.338979L.518057-.302864C.422416-.215193 .422416-.199253 .422416 0H3.57061L3.801743-1.42665H3.55467C3.53076-1.267248 3.466999-.868742 3.371357-.71731C3.323537-.653549 2.717808-.653549 2.590286-.653549H1.171606L2.247572-1.625903Z"/>
<path id="g1-51" d="M2.016438-2.662017C2.646077-2.662017 3.044583-2.199751 3.044583-1.362889C3.044583-.366625 2.478705-.071731 2.056289-.071731C1.617933-.071731 1.020174-.231133 .74122-.653549C1.028144-.653549 1.227397-.836862 1.227397-1.099875C1.227397-1.354919 1.044085-1.538232 .789041-1.538232C.573848-1.538232 .350685-1.40274 .350685-1.083935C.350685-.326775 1.163636 .167372 2.072229 .167372C3.132254 .167372 3.873474-.565878 3.873474-1.362889C3.873474-2.024408 3.347447-2.630137 2.534496-2.805479C3.164134-3.028643 3.634371-3.57061 3.634371-4.208219S2.917061-5.300125 2.088169-5.300125C1.235367-5.300125 .589788-4.837858 .589788-4.23213C.589788-3.937235 .789041-3.809714 .996264-3.809714C1.243337-3.809714 1.40274-3.985056 1.40274-4.216189C1.40274-4.511083 1.147696-4.622665 .972354-4.630635C1.307098-5.068991 1.920797-5.092902 2.064259-5.092902C2.271482-5.092902 2.87721-5.029141 2.87721-4.208219C2.87721-3.650311 2.646077-3.315567 2.534496-3.188045C2.295392-2.940971 2.11208-2.925031 1.625903-2.893151C1.474471-2.885181 1.41071-2.87721 1.41071-2.773599C1.41071-2.662017 1.482441-2.662017 1.617933-2.662017H2.016438Z"/>
<path id="g1-52" d="M3.140224-5.156663C3.140224-5.316065 3.140224-5.379826 2.972852-5.379826C2.86924-5.379826 2.86127-5.371856 2.781569-5.260274L.239103-1.570112V-1.307098H2.486675V-.645579C2.486675-.350685 2.462765-.263014 1.849066-.263014H1.665753V0C2.343213-.02391 2.359153-.02391 2.81345-.02391S3.283686-.02391 3.961146 0V-.263014H3.777833C3.164134-.263014 3.140224-.350685 3.140224-.645579V-1.307098H3.985056V-1.570112H3.140224V-5.156663ZM2.542466-4.511083V-1.570112H.518057L2.542466-4.511083Z"/>
<path id="g1-53" d="M1.115816-4.479203C1.219427-4.447323 1.538232-4.367621 1.872976-4.367621C2.86924-4.367621 3.474969-5.068991 3.474969-5.188543C3.474969-5.276214 3.419178-5.300125 3.379328-5.300125C3.363387-5.300125 3.347447-5.300125 3.275716-5.260274C2.964882-5.140722 2.598257-5.045081 2.16787-5.045081C1.697634-5.045081 1.307098-5.164633 1.060025-5.260274C.980324-5.300125 .964384-5.300125 .956413-5.300125C.852802-5.300125 .852802-5.212453 .852802-5.068991V-2.733748C.852802-2.590286 .852802-2.494645 .980324-2.494645C1.044085-2.494645 1.067995-2.526526 1.107846-2.590286C1.203487-2.709838 1.506351-3.116314 2.183811-3.116314C2.630137-3.116314 2.84533-2.749689 2.917061-2.598257C3.052553-2.311333 3.068493-1.944707 3.068493-1.633873C3.068493-1.338979 3.060523-.908593 2.83736-.557908C2.685928-.318804 2.367123-.071731 1.944707-.071731C1.42665-.071731 .916563-.398506 .73325-.916563C.757161-.908593 .804981-.908593 .812951-.908593C1.036115-.908593 1.211457-1.052055 1.211457-1.299128C1.211457-1.594022 .980324-1.697634 .820922-1.697634C.67746-1.697634 .422416-1.617933 .422416-1.275218C.422416-.557908 1.044085 .167372 1.960648 .167372C2.956912 .167372 3.801743-.605729 3.801743-1.594022C3.801743-2.518555 3.132254-3.339477 2.191781-3.339477C1.793275-3.339477 1.41868-3.211955 1.115816-2.940971V-4.479203Z"/>
<path id="g1-54" d="M1.099875-2.638107C1.099875-3.299626 1.155666-3.881445 1.44259-4.367621C1.681694-4.766127 2.088169-5.092902 2.590286-5.092902C2.749689-5.092902 3.116314-5.068991 3.299626-4.790037C2.940971-4.774097 2.909091-4.503113 2.909091-4.415442C2.909091-4.176339 3.092403-4.040847 3.283686-4.040847C3.427148-4.040847 3.658281-4.128518 3.658281-4.431382C3.658281-4.909589 3.299626-5.300125 2.582316-5.300125C1.474471-5.300125 .350685-4.24807 .350685-2.526526C.350685-.366625 1.354919 .167372 2.12802 .167372C2.510585 .167372 2.925031 .063761 3.283686-.278954C3.602491-.589788 3.873474-.924533 3.873474-1.617933C3.873474-2.662017 3.084433-3.395268 2.199751-3.395268C1.625903-3.395268 1.283188-3.028643 1.099875-2.638107ZM2.12802-.071731C1.705604-.071731 1.44259-.366625 1.323039-.589788C1.139726-.948443 1.123786-1.490411 1.123786-1.793275C1.123786-2.582316 1.554172-3.172105 2.16787-3.172105C2.566376-3.172105 2.805479-2.964882 2.956912-2.685928C3.124284-2.391034 3.124284-2.032379 3.124284-1.625903S3.124284-.868742 2.964882-.581818C2.757659-.215193 2.478705-.071731 2.12802-.071731Z"/>
<path id="g1-55" d="M4.032877-4.853798C4.104608-4.941469 4.104608-4.95741 4.104608-5.132752H2.080199C1.880946-5.132752 1.633873-5.140722 1.43462-5.156663C1.020174-5.188543 1.012204-5.260274 .988294-5.387796H.74122L.470237-3.706102H.71731C.73325-3.825654 .820922-4.375592 .932503-4.439352C1.020174-4.479203 1.617933-4.479203 1.737484-4.479203H3.427148L2.606227-3.379328C1.697634-2.16787 1.506351-.908593 1.506351-.278954C1.506351-.199253 1.506351 .167372 1.880946 .167372S2.255542-.191283 2.255542-.286924V-.669489C2.255542-1.817186 2.446824-2.757659 2.83736-3.275716L4.032877-4.853798Z"/>
<path id="g1-56" d="M2.646077-2.87721C3.092403-3.092403 3.634371-3.490909 3.634371-4.112578C3.634371-4.869738 2.86127-5.300125 2.12005-5.300125C1.275218-5.300125 .589788-4.718306 .589788-3.969116C.589788-3.674222 .6934-3.403238 .892653-3.172105C1.028144-3.004732 1.060025-2.988792 1.554172-2.677958C.565878-2.239601 .350685-1.657783 .350685-1.211457C.350685-.334745 1.235367 .167372 2.10411 .167372C3.084433 .167372 3.873474-.494147 3.873474-1.338979C3.873474-1.841096 3.602491-2.175841 3.474969-2.311333C3.339477-2.438854 3.331507-2.446824 2.646077-2.87721ZM1.41071-3.626401C1.179577-3.761893 .988294-3.993026 .988294-4.27198C.988294-4.774097 1.538232-5.092902 2.10411-5.092902C2.725778-5.092902 3.235866-4.670486 3.235866-4.112578C3.235866-3.650311 2.87721-3.259776 2.406974-3.028643L1.41071-3.626401ZM1.801245-2.534496C1.833126-2.518555 2.741719-1.960648 2.87721-1.872976C3.004732-1.801245 3.419178-1.546202 3.419178-1.067995C3.419178-.454296 2.773599-.071731 2.12005-.071731C1.41071-.071731 .804981-.557908 .804981-1.211457C.804981-1.809215 1.251308-2.279452 1.801245-2.534496Z"/>
<path id="g0-58" d="M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z"/>
<path id="g0-65" d="M2.032379-1.327024C1.613948-.621669 1.207472-.382565 .633624-.3467C.502117-.334745 .406476-.334745 .406476-.119552C.406476-.047821 .466252 0 .549938 0C.765131 0 1.303113-.02391 1.518306-.02391C1.865006-.02391 2.247572 0 2.582316 0C2.654047 0 2.797509 0 2.797509-.227148C2.797509-.334745 2.701868-.3467 2.630137-.3467C2.355168-.37061 2.12802-.466252 2.12802-.753176C2.12802-.920548 2.199751-1.052055 2.355168-1.315068L3.263761-2.82142H6.312329C6.324284-2.713823 6.324284-2.618182 6.336239-2.510585C6.372105-2.199751 6.515567-.956413 6.515567-.729265C6.515567-.37061 5.905853-.3467 5.71457-.3467C5.583064-.3467 5.451557-.3467 5.451557-.131507C5.451557 0 5.559153 0 5.630884 0C5.834122 0 6.073225-.02391 6.276463-.02391H6.957908C7.687173-.02391 8.2132 0 8.225156 0C8.308842 0 8.440349 0 8.440349-.227148C8.440349-.3467 8.332752-.3467 8.153425-.3467C7.49589-.3467 7.483935-.454296 7.44807-.812951L6.718804-8.272976C6.694894-8.51208 6.647073-8.53599 6.515567-8.53599C6.396015-8.53599 6.324284-8.51208 6.216687-8.332752L2.032379-1.327024ZM3.466999-3.16812L5.869988-7.185056L6.276463-3.16812H3.466999Z"/>
<path id="g0-66" d="M4.375592-7.352428C4.483188-7.79477 4.531009-7.81868 4.99726-7.81868H6.551432C7.902366-7.81868 7.902366-6.670984 7.902366-6.563387C7.902366-5.595019 6.933998-4.363636 5.355915-4.363636H3.634371L4.375592-7.352428ZM6.396015-4.267995C7.699128-4.507098 8.88269-5.415691 8.88269-6.515567C8.88269-7.44807 8.057783-8.16538 6.706849-8.16538H2.86924C2.642092-8.16538 2.534496-8.16538 2.534496-7.938232C2.534496-7.81868 2.642092-7.81868 2.82142-7.81868C3.550685-7.81868 3.550685-7.723039 3.550685-7.591532C3.550685-7.567621 3.550685-7.49589 3.502864-7.316563L1.888917-.884682C1.78132-.466252 1.75741-.3467 .920548-.3467C.6934-.3467 .573848-.3467 .573848-.131507C.573848 0 .645579 0 .884682 0H4.985305C6.814446 0 8.225156-1.3868 8.225156-2.594271C8.225156-3.574595 7.364384-4.172354 6.396015-4.267995ZM4.698381-.3467H3.084433C2.917061-.3467 2.893151-.3467 2.82142-.358655C2.689913-.37061 2.677958-.394521 2.677958-.490162C2.677958-.573848 2.701868-.645579 2.725778-.753176L3.56264-4.124533H5.810212C7.220922-4.124533 7.220922-2.809465 7.220922-2.713823C7.220922-1.566127 6.180822-.3467 4.698381-.3467Z"/>
<path id="g0-80" d="M3.53873-3.801743H5.547198C7.197011-3.801743 8.846824-5.021171 8.846824-6.38406C8.846824-7.316563 8.057783-8.16538 6.551432-8.16538H2.857285C2.630137-8.16538 2.52254-8.16538 2.52254-7.938232C2.52254-7.81868 2.630137-7.81868 2.809465-7.81868C3.53873-7.81868 3.53873-7.723039 3.53873-7.591532C3.53873-7.567621 3.53873-7.49589 3.490909-7.316563L1.876961-.884682C1.769365-.466252 1.745455-.3467 .908593-.3467C.681445-.3467 .561893-.3467 .561893-.131507C.561893 0 .669489 0 .74122 0C.968369 0 1.207472-.02391 1.43462-.02391H2.833375C3.060523-.02391 3.311582 0 3.53873 0C3.634371 0 3.765878 0 3.765878-.227148C3.765878-.3467 3.658281-.3467 3.478954-.3467C2.761644-.3467 2.749689-.430386 2.749689-.549938C2.749689-.609714 2.761644-.6934 2.773599-.753176L3.53873-3.801743ZM4.399502-7.352428C4.507098-7.79477 4.554919-7.81868 5.021171-7.81868H6.204732C7.10137-7.81868 7.84259-7.531756 7.84259-6.635118C7.84259-6.324284 7.687173-5.308095 7.137235-4.758157C6.933998-4.542964 6.360149-4.088667 5.272229-4.088667H3.58655L4.399502-7.352428Z"/>
<path id="g0-83" d="M7.591532-8.308842C7.591532-8.416438 7.507846-8.416438 7.483935-8.416438C7.436115-8.416438 7.424159-8.404483 7.280697-8.225156C7.208966-8.141469 6.718804-7.519801 6.706849-7.507846C6.312329-8.284932 5.523288-8.416438 5.021171-8.416438C3.502864-8.416438 2.12802-7.029639 2.12802-5.678705C2.12802-4.782067 2.666002-4.25604 3.251806-4.052802C3.383313-4.004981 4.088667-3.813699 4.447323-3.730012C5.057036-3.56264 5.212453-3.514819 5.463512-3.251806C5.511333-3.19203 5.750436-2.917061 5.750436-2.355168C5.750436-1.243337 4.722291-.095641 3.526775-.095641C2.546451-.095641 1.458531-.514072 1.458531-1.853051C1.458531-2.080199 1.506351-2.367123 1.542217-2.486675C1.542217-2.52254 1.554172-2.582316 1.554172-2.606227C1.554172-2.654047 1.530262-2.713823 1.43462-2.713823C1.327024-2.713823 1.315068-2.689913 1.267248-2.486675L.657534-.035866C.657534-.02391 .609714 .131507 .609714 .143462C.609714 .251059 .705355 .251059 .729265 .251059C.777086 .251059 .789041 .239103 .932503 .059776L1.482441-.657534C1.769365-.227148 2.391034 .251059 3.502864 .251059C5.045081 .251059 6.455791-1.243337 6.455791-2.737733C6.455791-3.239851 6.336239-3.682192 5.881943-4.124533C5.630884-4.375592 5.415691-4.435367 4.315816-4.722291C3.514819-4.937484 3.407223-4.97335 3.19203-5.164633C2.988792-5.36787 2.833375-5.654795 2.833375-6.06127C2.833375-7.065504 3.849564-8.093649 4.985305-8.093649C6.156912-8.093649 6.706849-7.376339 6.706849-6.240598C6.706849-5.929763 6.647073-5.606974 6.647073-5.559153C6.647073-5.451557 6.742715-5.451557 6.77858-5.451557C6.886177-5.451557 6.898132-5.487422 6.945953-5.678705L7.591532-8.308842Z"/>
</defs>
<g fill="#222" stroke="#222" style="fill: var(--color, #222); stroke: var(--color, #222);" stroke-width="0.3" transform="matrix(1.13 0 0 1.13 -80.23 -67.067712)">
<use x="70.734745" y="67.546657" xlink:href="#g0-80"/>
<use x="78.280013" y="69.33992" xlink:href="#g1-49"/>
<use x="86.333157" y="67.546657" xlink:href="#g2-61"/>
<use x="98.758638" y="67.546657" xlink:href="#g0-83"/>
<use x="105.957978" y="69.33992" xlink:href="#g1-50"/>
<use x="110.690292" y="67.546657" xlink:href="#g0-83"/>
<use x="117.889632" y="69.33992" xlink:href="#g1-54"/>
<use x="122.621947" y="67.546657" xlink:href="#g2-59"/>
<use x="70.734745" y="84.483145" xlink:href="#g0-80"/>
<use x="78.280013" y="86.276409" xlink:href="#g1-50"/>
<use x="86.333157" y="84.483145" xlink:href="#g2-61"/>
<use x="98.758638" y="84.483145" xlink:href="#g0-65"/>
<use x="107.533984" y="86.276409" xlink:href="#g1-49"/>
<use x="111.768167" y="86.276409" xlink:href="#g1-49"/>
<use x="116.500482" y="84.483145" xlink:href="#g0-66"/>
<use x="125.393445" y="86.276409" xlink:href="#g1-49"/>
<use x="129.627628" y="86.276409" xlink:href="#g1-49"/>
<use x="134.359943" y="84.483145" xlink:href="#g2-59"/>
<use x="70.734745" y="101.419634" xlink:href="#g0-80"/>
<use x="78.280013" y="103.212897" xlink:href="#g1-51"/>
<use x="86.333157" y="101.419634" xlink:href="#g2-61"/>
<use x="98.758638" y="101.419634" xlink:href="#g0-65"/>
<use x="107.533984" y="103.212897" xlink:href="#g1-49"/>
<use x="111.768167" y="103.212897" xlink:href="#g1-50"/>
<use x="116.500482" y="101.419634" xlink:href="#g0-66"/>
<use x="125.393445" y="103.212897" xlink:href="#g1-50"/>
<use x="129.627628" y="103.212897" xlink:href="#g1-49"/>
<use x="134.359943" y="101.419634" xlink:href="#g2-59"/>
<use x="70.734745" y="118.356122" xlink:href="#g0-80"/>
<use x="78.280013" y="120.149385" xlink:href="#g1-52"/>
<use x="86.333157" y="118.356122" xlink:href="#g2-61"/>
<use x="98.758638" y="118.356122" xlink:href="#g0-83"/>
<use x="105.957978" y="120.149385" xlink:href="#g1-51"/>
<use x="110.690292" y="118.356122" xlink:href="#g0-83"/>
<use x="117.889632" y="120.149385" xlink:href="#g1-55"/>
<use x="122.621947" y="118.356122" xlink:href="#g2-59"/>
<use x="70.734745" y="135.29261" xlink:href="#g0-80"/>
<use x="78.280013" y="137.085873" xlink:href="#g1-53"/>
<use x="86.333157" y="135.29261" xlink:href="#g2-61"/>
<use x="98.758638" y="135.29261" xlink:href="#g0-83"/>
<use x="105.957978" y="137.085873" xlink:href="#g1-49"/>
<use x="110.690292" y="135.29261" xlink:href="#g0-83"/>
<use x="117.889632" y="137.085873" xlink:href="#g1-53"/>
<use x="122.621947" y="135.29261" xlink:href="#g2-59"/>
<use x="70.734745" y="152.229098" xlink:href="#g0-80"/>
<use x="78.280013" y="154.022361" xlink:href="#g1-54"/>
<use x="86.333157" y="152.229098" xlink:href="#g2-61"/>
<use x="98.758638" y="152.229098" xlink:href="#g0-83"/>
<use x="105.957978" y="154.022361" xlink:href="#g1-52"/>
<use x="110.690292" y="152.229098" xlink:href="#g0-66"/>
<use x="119.583255" y="154.022361" xlink:href="#g1-50"/>
<use x="123.817438" y="154.022361" xlink:href="#g1-50"/>
<use x="128.549753" y="152.229098" xlink:href="#g2-59"/>
<use x="70.734745" y="169.165586" xlink:href="#g0-80"/>
<use x="78.280013" y="170.958849" xlink:href="#g1-55"/>
<use x="86.333157" y="169.165586" xlink:href="#g2-61"/>
<use x="98.758638" y="169.165586" xlink:href="#g0-65"/>
<use x="107.533984" y="170.958849" xlink:href="#g1-50"/>
<use x="111.768167" y="170.958849" xlink:href="#g1-50"/>
<use x="116.500482" y="169.165586" xlink:href="#g0-83"/>
<use x="123.699822" y="170.958849" xlink:href="#g1-56"/>
<use x="128.432137" y="169.165586" xlink:href="#g0-58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

125
jekyll_site/img/sums1.svg Normal file
View file

@ -0,0 +1,125 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="100.964341pt" height="147.490471pt" viewBox="-.299738 -.248234 100.964341 147.490471">
<defs>
<path id="g0-0" d="M7.878456-2.749689C8.081694-2.749689 8.296887-2.749689 8.296887-2.988792S8.081694-3.227895 7.878456-3.227895H1.41071C1.207472-3.227895 .992279-3.227895 .992279-2.988792S1.207472-2.749689 1.41071-2.749689H7.878456Z"/>
<path id="g3-40" d="M3.88543 2.905106C3.88543 2.86924 3.88543 2.84533 3.682192 2.642092C2.486675 1.43462 1.817186-.537983 1.817186-2.976837C1.817186-5.296139 2.379078-7.292653 3.765878-8.703362C3.88543-8.810959 3.88543-8.834869 3.88543-8.870735C3.88543-8.942466 3.825654-8.966376 3.777833-8.966376C3.622416-8.966376 2.642092-8.105604 2.056289-6.933998C1.446575-5.726526 1.171606-4.447323 1.171606-2.976837C1.171606-1.912827 1.338979-.490162 1.960648 .789041C2.666002 2.223661 3.646326 3.000747 3.777833 3.000747C3.825654 3.000747 3.88543 2.976837 3.88543 2.905106Z"/>
<path id="g3-41" d="M3.371357-2.976837C3.371357-3.88543 3.251806-5.36787 2.582316-6.75467C1.876961-8.18929 .896638-8.966376 .765131-8.966376C.71731-8.966376 .657534-8.942466 .657534-8.870735C.657534-8.834869 .657534-8.810959 .860772-8.607721C2.056289-7.400249 2.725778-5.427646 2.725778-2.988792C2.725778-.669489 2.163885 1.327024 .777086 2.737733C.657534 2.84533 .657534 2.86924 .657534 2.905106C.657534 2.976837 .71731 3.000747 .765131 3.000747C.920548 3.000747 1.900872 2.139975 2.486675 .968369C3.096389-.251059 3.371357-1.542217 3.371357-2.976837Z"/>
<path id="g3-43" d="M4.770112-2.761644H8.069738C8.237111-2.761644 8.452304-2.761644 8.452304-2.976837C8.452304-3.203985 8.249066-3.203985 8.069738-3.203985H4.770112V-6.503611C4.770112-6.670984 4.770112-6.886177 4.554919-6.886177C4.327771-6.886177 4.327771-6.682939 4.327771-6.503611V-3.203985H1.028144C.860772-3.203985 .645579-3.203985 .645579-2.988792C.645579-2.761644 .848817-2.761644 1.028144-2.761644H4.327771V.537983C4.327771 .705355 4.327771 .920548 4.542964 .920548C4.770112 .920548 4.770112 .71731 4.770112 .537983V-2.761644Z"/>
<path id="g3-59" d="M2.199751-4.578829C2.199751-4.901619 1.924782-5.152677 1.625903-5.152677C1.279203-5.152677 1.0401-4.877709 1.0401-4.578829C1.0401-4.220174 1.338979-3.993026 1.613948-3.993026C1.936737-3.993026 2.199751-4.244085 2.199751-4.578829ZM1.996513-.119552C1.996513 .298879 1.996513 1.147696 1.267248 2.044334C1.195517 2.139975 1.195517 2.163885 1.195517 2.187796C1.195517 2.247572 1.255293 2.307347 1.315068 2.307347C1.398755 2.307347 2.235616 1.422665 2.235616 .02391C2.235616-.418431 2.199751-1.159651 1.613948-1.159651C1.267248-1.159651 1.0401-.896638 1.0401-.585803C1.0401-.263014 1.267248 0 1.625903 0C1.853051 0 1.936737-.071731 1.996513-.119552Z"/>
<path id="g3-61" d="M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z"/>
<path id="g2-49" d="M2.502615-5.076961C2.502615-5.292154 2.486675-5.300125 2.271482-5.300125C1.944707-4.98132 1.522291-4.790037 .765131-4.790037V-4.527024C.980324-4.527024 1.41071-4.527024 1.872976-4.742217V-.653549C1.872976-.358655 1.849066-.263014 1.091905-.263014H.812951V0C1.139726-.02391 1.825156-.02391 2.183811-.02391S3.235866-.02391 3.56264 0V-.263014H3.283686C2.526526-.263014 2.502615-.358655 2.502615-.653549V-5.076961Z"/>
<path id="g2-50" d="M2.247572-1.625903C2.375093-1.745455 2.709838-2.008468 2.83736-2.12005C3.331507-2.574346 3.801743-3.012702 3.801743-3.737983C3.801743-4.686426 3.004732-5.300125 2.008468-5.300125C1.052055-5.300125 .422416-4.574844 .422416-3.865504C.422416-3.474969 .73325-3.419178 .844832-3.419178C1.012204-3.419178 1.259278-3.53873 1.259278-3.841594C1.259278-4.25604 .860772-4.25604 .765131-4.25604C.996264-4.837858 1.530262-5.037111 1.920797-5.037111C2.662017-5.037111 3.044583-4.407472 3.044583-3.737983C3.044583-2.909091 2.462765-2.303362 1.522291-1.338979L.518057-.302864C.422416-.215193 .422416-.199253 .422416 0H3.57061L3.801743-1.42665H3.55467C3.53076-1.267248 3.466999-.868742 3.371357-.71731C3.323537-.653549 2.717808-.653549 2.590286-.653549H1.171606L2.247572-1.625903Z"/>
<path id="g2-51" d="M2.016438-2.662017C2.646077-2.662017 3.044583-2.199751 3.044583-1.362889C3.044583-.366625 2.478705-.071731 2.056289-.071731C1.617933-.071731 1.020174-.231133 .74122-.653549C1.028144-.653549 1.227397-.836862 1.227397-1.099875C1.227397-1.354919 1.044085-1.538232 .789041-1.538232C.573848-1.538232 .350685-1.40274 .350685-1.083935C.350685-.326775 1.163636 .167372 2.072229 .167372C3.132254 .167372 3.873474-.565878 3.873474-1.362889C3.873474-2.024408 3.347447-2.630137 2.534496-2.805479C3.164134-3.028643 3.634371-3.57061 3.634371-4.208219S2.917061-5.300125 2.088169-5.300125C1.235367-5.300125 .589788-4.837858 .589788-4.23213C.589788-3.937235 .789041-3.809714 .996264-3.809714C1.243337-3.809714 1.40274-3.985056 1.40274-4.216189C1.40274-4.511083 1.147696-4.622665 .972354-4.630635C1.307098-5.068991 1.920797-5.092902 2.064259-5.092902C2.271482-5.092902 2.87721-5.029141 2.87721-4.208219C2.87721-3.650311 2.646077-3.315567 2.534496-3.188045C2.295392-2.940971 2.11208-2.925031 1.625903-2.893151C1.474471-2.885181 1.41071-2.87721 1.41071-2.773599C1.41071-2.662017 1.482441-2.662017 1.617933-2.662017H2.016438Z"/>
<path id="g2-52" d="M3.140224-5.156663C3.140224-5.316065 3.140224-5.379826 2.972852-5.379826C2.86924-5.379826 2.86127-5.371856 2.781569-5.260274L.239103-1.570112V-1.307098H2.486675V-.645579C2.486675-.350685 2.462765-.263014 1.849066-.263014H1.665753V0C2.343213-.02391 2.359153-.02391 2.81345-.02391S3.283686-.02391 3.961146 0V-.263014H3.777833C3.164134-.263014 3.140224-.350685 3.140224-.645579V-1.307098H3.985056V-1.570112H3.140224V-5.156663ZM2.542466-4.511083V-1.570112H.518057L2.542466-4.511083Z"/>
<path id="g2-53" d="M1.115816-4.479203C1.219427-4.447323 1.538232-4.367621 1.872976-4.367621C2.86924-4.367621 3.474969-5.068991 3.474969-5.188543C3.474969-5.276214 3.419178-5.300125 3.379328-5.300125C3.363387-5.300125 3.347447-5.300125 3.275716-5.260274C2.964882-5.140722 2.598257-5.045081 2.16787-5.045081C1.697634-5.045081 1.307098-5.164633 1.060025-5.260274C.980324-5.300125 .964384-5.300125 .956413-5.300125C.852802-5.300125 .852802-5.212453 .852802-5.068991V-2.733748C.852802-2.590286 .852802-2.494645 .980324-2.494645C1.044085-2.494645 1.067995-2.526526 1.107846-2.590286C1.203487-2.709838 1.506351-3.116314 2.183811-3.116314C2.630137-3.116314 2.84533-2.749689 2.917061-2.598257C3.052553-2.311333 3.068493-1.944707 3.068493-1.633873C3.068493-1.338979 3.060523-.908593 2.83736-.557908C2.685928-.318804 2.367123-.071731 1.944707-.071731C1.42665-.071731 .916563-.398506 .73325-.916563C.757161-.908593 .804981-.908593 .812951-.908593C1.036115-.908593 1.211457-1.052055 1.211457-1.299128C1.211457-1.594022 .980324-1.697634 .820922-1.697634C.67746-1.697634 .422416-1.617933 .422416-1.275218C.422416-.557908 1.044085 .167372 1.960648 .167372C2.956912 .167372 3.801743-.605729 3.801743-1.594022C3.801743-2.518555 3.132254-3.339477 2.191781-3.339477C1.793275-3.339477 1.41868-3.211955 1.115816-2.940971V-4.479203Z"/>
<path id="g2-54" d="M1.099875-2.638107C1.099875-3.299626 1.155666-3.881445 1.44259-4.367621C1.681694-4.766127 2.088169-5.092902 2.590286-5.092902C2.749689-5.092902 3.116314-5.068991 3.299626-4.790037C2.940971-4.774097 2.909091-4.503113 2.909091-4.415442C2.909091-4.176339 3.092403-4.040847 3.283686-4.040847C3.427148-4.040847 3.658281-4.128518 3.658281-4.431382C3.658281-4.909589 3.299626-5.300125 2.582316-5.300125C1.474471-5.300125 .350685-4.24807 .350685-2.526526C.350685-.366625 1.354919 .167372 2.12802 .167372C2.510585 .167372 2.925031 .063761 3.283686-.278954C3.602491-.589788 3.873474-.924533 3.873474-1.617933C3.873474-2.662017 3.084433-3.395268 2.199751-3.395268C1.625903-3.395268 1.283188-3.028643 1.099875-2.638107ZM2.12802-.071731C1.705604-.071731 1.44259-.366625 1.323039-.589788C1.139726-.948443 1.123786-1.490411 1.123786-1.793275C1.123786-2.582316 1.554172-3.172105 2.16787-3.172105C2.566376-3.172105 2.805479-2.964882 2.956912-2.685928C3.124284-2.391034 3.124284-2.032379 3.124284-1.625903S3.124284-.868742 2.964882-.581818C2.757659-.215193 2.478705-.071731 2.12802-.071731Z"/>
<path id="g2-55" d="M4.032877-4.853798C4.104608-4.941469 4.104608-4.95741 4.104608-5.132752H2.080199C1.880946-5.132752 1.633873-5.140722 1.43462-5.156663C1.020174-5.188543 1.012204-5.260274 .988294-5.387796H.74122L.470237-3.706102H.71731C.73325-3.825654 .820922-4.375592 .932503-4.439352C1.020174-4.479203 1.617933-4.479203 1.737484-4.479203H3.427148L2.606227-3.379328C1.697634-2.16787 1.506351-.908593 1.506351-.278954C1.506351-.199253 1.506351 .167372 1.880946 .167372S2.255542-.191283 2.255542-.286924V-.669489C2.255542-1.817186 2.446824-2.757659 2.83736-3.275716L4.032877-4.853798Z"/>
<path id="g2-56" d="M2.646077-2.87721C3.092403-3.092403 3.634371-3.490909 3.634371-4.112578C3.634371-4.869738 2.86127-5.300125 2.12005-5.300125C1.275218-5.300125 .589788-4.718306 .589788-3.969116C.589788-3.674222 .6934-3.403238 .892653-3.172105C1.028144-3.004732 1.060025-2.988792 1.554172-2.677958C.565878-2.239601 .350685-1.657783 .350685-1.211457C.350685-.334745 1.235367 .167372 2.10411 .167372C3.084433 .167372 3.873474-.494147 3.873474-1.338979C3.873474-1.841096 3.602491-2.175841 3.474969-2.311333C3.339477-2.438854 3.331507-2.446824 2.646077-2.87721ZM1.41071-3.626401C1.179577-3.761893 .988294-3.993026 .988294-4.27198C.988294-4.774097 1.538232-5.092902 2.10411-5.092902C2.725778-5.092902 3.235866-4.670486 3.235866-4.112578C3.235866-3.650311 2.87721-3.259776 2.406974-3.028643L1.41071-3.626401ZM1.801245-2.534496C1.833126-2.518555 2.741719-1.960648 2.87721-1.872976C3.004732-1.801245 3.419178-1.546202 3.419178-1.067995C3.419178-.454296 2.773599-.071731 2.12005-.071731C1.41071-.071731 .804981-.557908 .804981-1.211457C.804981-1.809215 1.251308-2.279452 1.801245-2.534496Z"/>
<path id="g1-58" d="M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z"/>
<path id="g1-65" d="M2.032379-1.327024C1.613948-.621669 1.207472-.382565 .633624-.3467C.502117-.334745 .406476-.334745 .406476-.119552C.406476-.047821 .466252 0 .549938 0C.765131 0 1.303113-.02391 1.518306-.02391C1.865006-.02391 2.247572 0 2.582316 0C2.654047 0 2.797509 0 2.797509-.227148C2.797509-.334745 2.701868-.3467 2.630137-.3467C2.355168-.37061 2.12802-.466252 2.12802-.753176C2.12802-.920548 2.199751-1.052055 2.355168-1.315068L3.263761-2.82142H6.312329C6.324284-2.713823 6.324284-2.618182 6.336239-2.510585C6.372105-2.199751 6.515567-.956413 6.515567-.729265C6.515567-.37061 5.905853-.3467 5.71457-.3467C5.583064-.3467 5.451557-.3467 5.451557-.131507C5.451557 0 5.559153 0 5.630884 0C5.834122 0 6.073225-.02391 6.276463-.02391H6.957908C7.687173-.02391 8.2132 0 8.225156 0C8.308842 0 8.440349 0 8.440349-.227148C8.440349-.3467 8.332752-.3467 8.153425-.3467C7.49589-.3467 7.483935-.454296 7.44807-.812951L6.718804-8.272976C6.694894-8.51208 6.647073-8.53599 6.515567-8.53599C6.396015-8.53599 6.324284-8.51208 6.216687-8.332752L2.032379-1.327024ZM3.466999-3.16812L5.869988-7.185056L6.276463-3.16812H3.466999Z"/>
<path id="g1-66" d="M4.375592-7.352428C4.483188-7.79477 4.531009-7.81868 4.99726-7.81868H6.551432C7.902366-7.81868 7.902366-6.670984 7.902366-6.563387C7.902366-5.595019 6.933998-4.363636 5.355915-4.363636H3.634371L4.375592-7.352428ZM6.396015-4.267995C7.699128-4.507098 8.88269-5.415691 8.88269-6.515567C8.88269-7.44807 8.057783-8.16538 6.706849-8.16538H2.86924C2.642092-8.16538 2.534496-8.16538 2.534496-7.938232C2.534496-7.81868 2.642092-7.81868 2.82142-7.81868C3.550685-7.81868 3.550685-7.723039 3.550685-7.591532C3.550685-7.567621 3.550685-7.49589 3.502864-7.316563L1.888917-.884682C1.78132-.466252 1.75741-.3467 .920548-.3467C.6934-.3467 .573848-.3467 .573848-.131507C.573848 0 .645579 0 .884682 0H4.985305C6.814446 0 8.225156-1.3868 8.225156-2.594271C8.225156-3.574595 7.364384-4.172354 6.396015-4.267995ZM4.698381-.3467H3.084433C2.917061-.3467 2.893151-.3467 2.82142-.358655C2.689913-.37061 2.677958-.394521 2.677958-.490162C2.677958-.573848 2.701868-.645579 2.725778-.753176L3.56264-4.124533H5.810212C7.220922-4.124533 7.220922-2.809465 7.220922-2.713823C7.220922-1.566127 6.180822-.3467 4.698381-.3467Z"/>
<path id="g1-83" d="M7.591532-8.308842C7.591532-8.416438 7.507846-8.416438 7.483935-8.416438C7.436115-8.416438 7.424159-8.404483 7.280697-8.225156C7.208966-8.141469 6.718804-7.519801 6.706849-7.507846C6.312329-8.284932 5.523288-8.416438 5.021171-8.416438C3.502864-8.416438 2.12802-7.029639 2.12802-5.678705C2.12802-4.782067 2.666002-4.25604 3.251806-4.052802C3.383313-4.004981 4.088667-3.813699 4.447323-3.730012C5.057036-3.56264 5.212453-3.514819 5.463512-3.251806C5.511333-3.19203 5.750436-2.917061 5.750436-2.355168C5.750436-1.243337 4.722291-.095641 3.526775-.095641C2.546451-.095641 1.458531-.514072 1.458531-1.853051C1.458531-2.080199 1.506351-2.367123 1.542217-2.486675C1.542217-2.52254 1.554172-2.582316 1.554172-2.606227C1.554172-2.654047 1.530262-2.713823 1.43462-2.713823C1.327024-2.713823 1.315068-2.689913 1.267248-2.486675L.657534-.035866C.657534-.02391 .609714 .131507 .609714 .143462C.609714 .251059 .705355 .251059 .729265 .251059C.777086 .251059 .789041 .239103 .932503 .059776L1.482441-.657534C1.769365-.227148 2.391034 .251059 3.502864 .251059C5.045081 .251059 6.455791-1.243337 6.455791-2.737733C6.455791-3.239851 6.336239-3.682192 5.881943-4.124533C5.630884-4.375592 5.415691-4.435367 4.315816-4.722291C3.514819-4.937484 3.407223-4.97335 3.19203-5.164633C2.988792-5.36787 2.833375-5.654795 2.833375-6.06127C2.833375-7.065504 3.849564-8.093649 4.985305-8.093649C6.156912-8.093649 6.706849-7.376339 6.706849-6.240598C6.706849-5.929763 6.647073-5.606974 6.647073-5.559153C6.647073-5.451557 6.742715-5.451557 6.77858-5.451557C6.886177-5.451557 6.898132-5.487422 6.945953-5.678705L7.591532-8.308842Z"/>
</defs>
<g fill="#222" stroke="#222" style="fill: var(--color, #222); stroke: var(--color, #222);" stroke-width="0.3" transform="matrix(1.13 0 0 1.13 -80.23 -66.443952)">
<use x="70.734745" y="67.546657" xlink:href="#g1-83"/>
<use x="77.934085" y="69.33992" xlink:href="#g2-49"/>
<use x="85.987225" y="67.546657" xlink:href="#g3-61"/>
<use x="98.412706" y="67.546657" xlink:href="#g3-40"/>
<use x="102.965032" y="67.546657" xlink:href="#g1-65"/>
<use x="111.740378" y="69.33992" xlink:href="#g2-50"/>
<use x="115.974561" y="69.33992" xlink:href="#g2-49"/>
<use x="123.36354" y="67.546657" xlink:href="#g3-43"/>
<use x="135.124855" y="67.546657" xlink:href="#g1-65"/>
<use x="143.900201" y="69.33992" xlink:href="#g2-50"/>
<use x="148.134384" y="69.33992" xlink:href="#g2-50"/>
<use x="152.866699" y="67.546657" xlink:href="#g3-41"/>
<use x="157.419025" y="67.546657" xlink:href="#g3-59"/>
<use x="70.734745" y="84.483145" xlink:href="#g1-83"/>
<use x="77.934085" y="86.276409" xlink:href="#g2-50"/>
<use x="85.987225" y="84.483145" xlink:href="#g3-61"/>
<use x="98.412706" y="84.483145" xlink:href="#g3-40"/>
<use x="102.965032" y="84.483145" xlink:href="#g1-83"/>
<use x="110.164372" y="86.276409" xlink:href="#g2-49"/>
<use x="117.55335" y="84.483145" xlink:href="#g0-0"/>
<use x="129.50851" y="84.483145" xlink:href="#g1-65"/>
<use x="138.283857" y="86.276409" xlink:href="#g2-49"/>
<use x="142.51804" y="86.276409" xlink:href="#g2-49"/>
<use x="147.250355" y="84.483145" xlink:href="#g3-41"/>
<use x="151.802681" y="84.483145" xlink:href="#g3-59"/>
<use x="70.734745" y="101.419634" xlink:href="#g1-83"/>
<use x="77.934085" y="103.212897" xlink:href="#g2-51"/>
<use x="85.987225" y="101.419634" xlink:href="#g3-61"/>
<use x="98.412706" y="101.419634" xlink:href="#g3-40"/>
<use x="102.965032" y="101.419634" xlink:href="#g1-65"/>
<use x="111.740378" y="103.212897" xlink:href="#g2-49"/>
<use x="115.974561" y="103.212897" xlink:href="#g2-49"/>
<use x="123.36354" y="101.419634" xlink:href="#g0-0"/>
<use x="135.3187" y="101.419634" xlink:href="#g1-65"/>
<use x="144.094047" y="103.212897" xlink:href="#g2-50"/>
<use x="148.32823" y="103.212897" xlink:href="#g2-49"/>
<use x="153.060544" y="101.419634" xlink:href="#g3-41"/>
<use x="157.61287" y="101.419634" xlink:href="#g3-59"/>
<use x="70.734745" y="118.356122" xlink:href="#g1-83"/>
<use x="77.934085" y="120.149385" xlink:href="#g2-52"/>
<use x="85.987225" y="118.356122" xlink:href="#g3-61"/>
<use x="98.412706" y="118.356122" xlink:href="#g3-40"/>
<use x="102.965032" y="118.356122" xlink:href="#g1-65"/>
<use x="111.740378" y="120.149385" xlink:href="#g2-49"/>
<use x="115.974561" y="120.149385" xlink:href="#g2-50"/>
<use x="123.36354" y="118.356122" xlink:href="#g0-0"/>
<use x="135.3187" y="118.356122" xlink:href="#g1-83"/>
<use x="142.51804" y="120.149385" xlink:href="#g2-50"/>
<use x="147.250355" y="118.356122" xlink:href="#g3-41"/>
<use x="151.802681" y="118.356122" xlink:href="#g3-59"/>
<use x="70.734745" y="135.29261" xlink:href="#g1-83"/>
<use x="77.934085" y="137.085873" xlink:href="#g2-53"/>
<use x="85.987225" y="135.29261" xlink:href="#g3-61"/>
<use x="98.412706" y="135.29261" xlink:href="#g3-40"/>
<use x="102.965032" y="135.29261" xlink:href="#g1-66"/>
<use x="111.857995" y="137.085873" xlink:href="#g2-49"/>
<use x="116.092177" y="137.085873" xlink:href="#g2-50"/>
<use x="123.481156" y="135.29261" xlink:href="#g0-0"/>
<use x="135.436316" y="135.29261" xlink:href="#g1-66"/>
<use x="144.329279" y="137.085873" xlink:href="#g2-49"/>
<use x="148.563462" y="137.085873" xlink:href="#g2-49"/>
<use x="153.295777" y="135.29261" xlink:href="#g3-41"/>
<use x="157.848103" y="135.29261" xlink:href="#g3-59"/>
<use x="70.734745" y="152.229098" xlink:href="#g1-83"/>
<use x="77.934085" y="154.022361" xlink:href="#g2-54"/>
<use x="85.987225" y="152.229098" xlink:href="#g3-61"/>
<use x="98.412706" y="152.229098" xlink:href="#g3-40"/>
<use x="102.965032" y="152.229098" xlink:href="#g1-66"/>
<use x="111.857995" y="154.022361" xlink:href="#g2-50"/>
<use x="116.092177" y="154.022361" xlink:href="#g2-50"/>
<use x="123.481156" y="152.229098" xlink:href="#g0-0"/>
<use x="135.436316" y="152.229098" xlink:href="#g1-83"/>
<use x="142.635656" y="154.022361" xlink:href="#g2-53"/>
<use x="147.367971" y="152.229098" xlink:href="#g3-41"/>
<use x="151.920297" y="152.229098" xlink:href="#g3-59"/>
<use x="70.734745" y="169.165586" xlink:href="#g1-83"/>
<use x="77.934085" y="170.958849" xlink:href="#g2-55"/>
<use x="85.987225" y="169.165586" xlink:href="#g3-61"/>
<use x="98.412706" y="169.165586" xlink:href="#g3-40"/>
<use x="102.965032" y="169.165586" xlink:href="#g1-66"/>
<use x="111.857995" y="170.958849" xlink:href="#g2-50"/>
<use x="116.092177" y="170.958849" xlink:href="#g2-50"/>
<use x="123.481156" y="169.165586" xlink:href="#g0-0"/>
<use x="135.436316" y="169.165586" xlink:href="#g1-66"/>
<use x="144.329279" y="170.958849" xlink:href="#g2-49"/>
<use x="148.563462" y="170.958849" xlink:href="#g2-50"/>
<use x="153.295777" y="169.165586" xlink:href="#g3-41"/>
<use x="157.848103" y="169.165586" xlink:href="#g3-59"/>
<use x="70.734745" y="186.102074" xlink:href="#g1-83"/>
<use x="77.934085" y="187.895338" xlink:href="#g2-56"/>
<use x="85.987225" y="186.102074" xlink:href="#g3-61"/>
<use x="98.412706" y="186.102074" xlink:href="#g3-40"/>
<use x="102.965032" y="186.102074" xlink:href="#g1-83"/>
<use x="110.164372" y="187.895338" xlink:href="#g2-54"/>
<use x="117.55335" y="186.102074" xlink:href="#g0-0"/>
<use x="129.50851" y="186.102074" xlink:href="#g1-66"/>
<use x="138.401473" y="187.895338" xlink:href="#g2-50"/>
<use x="142.635656" y="187.895338" xlink:href="#g2-49"/>
<use x="147.367971" y="186.102074" xlink:href="#g3-41"/>
<use x="151.920297" y="186.102074" xlink:href="#g1-58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

34
jekyll_site/img/sums2.svg Normal file
View file

@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="77.459681pt" height="30.391498pt" viewBox="-.299738 -.251628 77.459681 30.391498">
<defs>
<path id="g2-43" d="M4.770112-2.761644H8.069738C8.237111-2.761644 8.452304-2.761644 8.452304-2.976837C8.452304-3.203985 8.249066-3.203985 8.069738-3.203985H4.770112V-6.503611C4.770112-6.670984 4.770112-6.886177 4.554919-6.886177C4.327771-6.886177 4.327771-6.682939 4.327771-6.503611V-3.203985H1.028144C.860772-3.203985 .645579-3.203985 .645579-2.988792C.645579-2.761644 .848817-2.761644 1.028144-2.761644H4.327771V.537983C4.327771 .705355 4.327771 .920548 4.542964 .920548C4.770112 .920548 4.770112 .71731 4.770112 .537983V-2.761644Z"/>
<path id="g2-59" d="M2.199751-4.578829C2.199751-4.901619 1.924782-5.152677 1.625903-5.152677C1.279203-5.152677 1.0401-4.877709 1.0401-4.578829C1.0401-4.220174 1.338979-3.993026 1.613948-3.993026C1.936737-3.993026 2.199751-4.244085 2.199751-4.578829ZM1.996513-.119552C1.996513 .298879 1.996513 1.147696 1.267248 2.044334C1.195517 2.139975 1.195517 2.163885 1.195517 2.187796C1.195517 2.247572 1.255293 2.307347 1.315068 2.307347C1.398755 2.307347 2.235616 1.422665 2.235616 .02391C2.235616-.418431 2.199751-1.159651 1.613948-1.159651C1.267248-1.159651 1.0401-.896638 1.0401-.585803C1.0401-.263014 1.267248 0 1.625903 0C1.853051 0 1.936737-.071731 1.996513-.119552Z"/>
<path id="g2-61" d="M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z"/>
<path id="g1-49" d="M2.502615-5.076961C2.502615-5.292154 2.486675-5.300125 2.271482-5.300125C1.944707-4.98132 1.522291-4.790037 .765131-4.790037V-4.527024C.980324-4.527024 1.41071-4.527024 1.872976-4.742217V-.653549C1.872976-.358655 1.849066-.263014 1.091905-.263014H.812951V0C1.139726-.02391 1.825156-.02391 2.183811-.02391S3.235866-.02391 3.56264 0V-.263014H3.283686C2.526526-.263014 2.502615-.358655 2.502615-.653549V-5.076961Z"/>
<path id="g1-50" d="M2.247572-1.625903C2.375093-1.745455 2.709838-2.008468 2.83736-2.12005C3.331507-2.574346 3.801743-3.012702 3.801743-3.737983C3.801743-4.686426 3.004732-5.300125 2.008468-5.300125C1.052055-5.300125 .422416-4.574844 .422416-3.865504C.422416-3.474969 .73325-3.419178 .844832-3.419178C1.012204-3.419178 1.259278-3.53873 1.259278-3.841594C1.259278-4.25604 .860772-4.25604 .765131-4.25604C.996264-4.837858 1.530262-5.037111 1.920797-5.037111C2.662017-5.037111 3.044583-4.407472 3.044583-3.737983C3.044583-2.909091 2.462765-2.303362 1.522291-1.338979L.518057-.302864C.422416-.215193 .422416-.199253 .422416 0H3.57061L3.801743-1.42665H3.55467C3.53076-1.267248 3.466999-.868742 3.371357-.71731C3.323537-.653549 2.717808-.653549 2.590286-.653549H1.171606L2.247572-1.625903Z"/>
<path id="g1-52" d="M3.140224-5.156663C3.140224-5.316065 3.140224-5.379826 2.972852-5.379826C2.86924-5.379826 2.86127-5.371856 2.781569-5.260274L.239103-1.570112V-1.307098H2.486675V-.645579C2.486675-.350685 2.462765-.263014 1.849066-.263014H1.665753V0C2.343213-.02391 2.359153-.02391 2.81345-.02391S3.283686-.02391 3.961146 0V-.263014H3.777833C3.164134-.263014 3.140224-.350685 3.140224-.645579V-1.307098H3.985056V-1.570112H3.140224V-5.156663ZM2.542466-4.511083V-1.570112H.518057L2.542466-4.511083Z"/>
<path id="g0-58" d="M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z"/>
<path id="g0-80" d="M3.53873-3.801743H5.547198C7.197011-3.801743 8.846824-5.021171 8.846824-6.38406C8.846824-7.316563 8.057783-8.16538 6.551432-8.16538H2.857285C2.630137-8.16538 2.52254-8.16538 2.52254-7.938232C2.52254-7.81868 2.630137-7.81868 2.809465-7.81868C3.53873-7.81868 3.53873-7.723039 3.53873-7.591532C3.53873-7.567621 3.53873-7.49589 3.490909-7.316563L1.876961-.884682C1.769365-.466252 1.745455-.3467 .908593-.3467C.681445-.3467 .561893-.3467 .561893-.131507C.561893 0 .669489 0 .74122 0C.968369 0 1.207472-.02391 1.43462-.02391H2.833375C3.060523-.02391 3.311582 0 3.53873 0C3.634371 0 3.765878 0 3.765878-.227148C3.765878-.3467 3.658281-.3467 3.478954-.3467C2.761644-.3467 2.749689-.430386 2.749689-.549938C2.749689-.609714 2.761644-.6934 2.773599-.753176L3.53873-3.801743ZM4.399502-7.352428C4.507098-7.79477 4.554919-7.81868 5.021171-7.81868H6.204732C7.10137-7.81868 7.84259-7.531756 7.84259-6.635118C7.84259-6.324284 7.687173-5.308095 7.137235-4.758157C6.933998-4.542964 6.360149-4.088667 5.272229-4.088667H3.58655L4.399502-7.352428Z"/>
<path id="g0-84" d="M4.985305-7.292653C5.057036-7.579577 5.080946-7.687173 5.260274-7.734994C5.355915-7.758904 5.750436-7.758904 6.001494-7.758904C7.197011-7.758904 7.758904-7.711083 7.758904-6.77858C7.758904-6.599253 7.711083-6.144956 7.639352-5.702615L7.627397-5.559153C7.627397-5.511333 7.675218-5.439601 7.746949-5.439601C7.866501-5.439601 7.866501-5.499377 7.902366-5.69066L8.249066-7.806725C8.272976-7.914321 8.272976-7.938232 8.272976-7.974097C8.272976-8.105604 8.201245-8.105604 7.962142-8.105604H1.422665C1.147696-8.105604 1.135741-8.093649 1.06401-7.878456L.334745-5.726526C.32279-5.702615 .286924-5.571108 .286924-5.559153C.286924-5.499377 .334745-5.439601 .406476-5.439601C.502117-5.439601 .526027-5.487422 .573848-5.642839C1.075965-7.089415 1.327024-7.758904 2.917061-7.758904H3.718057C4.004981-7.758904 4.124533-7.758904 4.124533-7.627397C4.124533-7.591532 4.124533-7.567621 4.064757-7.352428L2.462765-.932503C2.343213-.466252 2.319303-.3467 1.052055-.3467C.753176-.3467 .669489-.3467 .669489-.119552C.669489 0 .800996 0 .860772 0C1.159651 0 1.470486-.02391 1.769365-.02391H3.634371C3.93325-.02391 4.25604 0 4.554919 0C4.686426 0 4.805978 0 4.805978-.227148C4.805978-.3467 4.722291-.3467 4.411457-.3467C3.335492-.3467 3.335492-.454296 3.335492-.633624C3.335492-.645579 3.335492-.729265 3.383313-.920548L4.985305-7.292653Z"/>
</defs>
<g fill="#222" stroke="#222" style="fill: var(--color, #222); stroke: var(--color, #222);" stroke-width="0.3" transform="matrix(1.13 0 0 1.13 -80.23 -67.352472)">
<use x="70.734745" y="67.546657" xlink:href="#g0-84"/>
<use x="77.59575" y="69.33992" xlink:href="#g1-49"/>
<use x="85.648894" y="67.546657" xlink:href="#g2-61"/>
<use x="98.074375" y="67.546657" xlink:href="#g0-80"/>
<use x="105.619643" y="69.33992" xlink:href="#g1-49"/>
<use x="113.008621" y="67.546657" xlink:href="#g2-43"/>
<use x="124.769936" y="67.546657" xlink:href="#g0-80"/>
<use x="132.315204" y="69.33992" xlink:href="#g1-50"/>
<use x="137.047519" y="67.546657" xlink:href="#g2-59"/>
<use x="70.734745" y="84.483145" xlink:href="#g0-84"/>
<use x="77.59575" y="86.276409" xlink:href="#g1-50"/>
<use x="85.648894" y="84.483145" xlink:href="#g2-61"/>
<use x="98.074375" y="84.483145" xlink:href="#g0-84"/>
<use x="104.93538" y="86.276409" xlink:href="#g1-49"/>
<use x="112.324359" y="84.483145" xlink:href="#g2-43"/>
<use x="124.085674" y="84.483145" xlink:href="#g0-80"/>
<use x="131.630941" y="86.276409" xlink:href="#g1-52"/>
<use x="136.363256" y="84.483145" xlink:href="#g0-58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

63
jekyll_site/img/sums3.svg Normal file
View file

@ -0,0 +1,63 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="239.236252pt" height="32.408907pt" viewBox="-.299738 -.244857 239.236252 32.408907">
<defs>
<path id="g1-0" d="M7.878456-2.749689C8.081694-2.749689 8.296887-2.749689 8.296887-2.988792S8.081694-3.227895 7.878456-3.227895H1.41071C1.207472-3.227895 .992279-3.227895 .992279-2.988792S1.207472-2.749689 1.41071-2.749689H7.878456Z"/>
<path id="g4-43" d="M4.770112-2.761644H8.069738C8.237111-2.761644 8.452304-2.761644 8.452304-2.976837C8.452304-3.203985 8.249066-3.203985 8.069738-3.203985H4.770112V-6.503611C4.770112-6.670984 4.770112-6.886177 4.554919-6.886177C4.327771-6.886177 4.327771-6.682939 4.327771-6.503611V-3.203985H1.028144C.860772-3.203985 .645579-3.203985 .645579-2.988792C.645579-2.761644 .848817-2.761644 1.028144-2.761644H4.327771V.537983C4.327771 .705355 4.327771 .920548 4.542964 .920548C4.770112 .920548 4.770112 .71731 4.770112 .537983V-2.761644Z"/>
<path id="g4-61" d="M8.069738-3.873474C8.237111-3.873474 8.452304-3.873474 8.452304-4.088667C8.452304-4.315816 8.249066-4.315816 8.069738-4.315816H1.028144C.860772-4.315816 .645579-4.315816 .645579-4.100623C.645579-3.873474 .848817-3.873474 1.028144-3.873474H8.069738ZM8.069738-1.649813C8.237111-1.649813 8.452304-1.649813 8.452304-1.865006C8.452304-2.092154 8.249066-2.092154 8.069738-2.092154H1.028144C.860772-2.092154 .645579-2.092154 .645579-1.876961C.645579-1.649813 .848817-1.649813 1.028144-1.649813H8.069738Z"/>
<path id="g3-49" d="M2.502615-5.076961C2.502615-5.292154 2.486675-5.300125 2.271482-5.300125C1.944707-4.98132 1.522291-4.790037 .765131-4.790037V-4.527024C.980324-4.527024 1.41071-4.527024 1.872976-4.742217V-.653549C1.872976-.358655 1.849066-.263014 1.091905-.263014H.812951V0C1.139726-.02391 1.825156-.02391 2.183811-.02391S3.235866-.02391 3.56264 0V-.263014H3.283686C2.526526-.263014 2.502615-.358655 2.502615-.653549V-5.076961Z"/>
<path id="g3-50" d="M2.247572-1.625903C2.375093-1.745455 2.709838-2.008468 2.83736-2.12005C3.331507-2.574346 3.801743-3.012702 3.801743-3.737983C3.801743-4.686426 3.004732-5.300125 2.008468-5.300125C1.052055-5.300125 .422416-4.574844 .422416-3.865504C.422416-3.474969 .73325-3.419178 .844832-3.419178C1.012204-3.419178 1.259278-3.53873 1.259278-3.841594C1.259278-4.25604 .860772-4.25604 .765131-4.25604C.996264-4.837858 1.530262-5.037111 1.920797-5.037111C2.662017-5.037111 3.044583-4.407472 3.044583-3.737983C3.044583-2.909091 2.462765-2.303362 1.522291-1.338979L.518057-.302864C.422416-.215193 .422416-.199253 .422416 0H3.57061L3.801743-1.42665H3.55467C3.53076-1.267248 3.466999-.868742 3.371357-.71731C3.323537-.653549 2.717808-.653549 2.590286-.653549H1.171606L2.247572-1.625903Z"/>
<path id="g3-51" d="M2.016438-2.662017C2.646077-2.662017 3.044583-2.199751 3.044583-1.362889C3.044583-.366625 2.478705-.071731 2.056289-.071731C1.617933-.071731 1.020174-.231133 .74122-.653549C1.028144-.653549 1.227397-.836862 1.227397-1.099875C1.227397-1.354919 1.044085-1.538232 .789041-1.538232C.573848-1.538232 .350685-1.40274 .350685-1.083935C.350685-.326775 1.163636 .167372 2.072229 .167372C3.132254 .167372 3.873474-.565878 3.873474-1.362889C3.873474-2.024408 3.347447-2.630137 2.534496-2.805479C3.164134-3.028643 3.634371-3.57061 3.634371-4.208219S2.917061-5.300125 2.088169-5.300125C1.235367-5.300125 .589788-4.837858 .589788-4.23213C.589788-3.937235 .789041-3.809714 .996264-3.809714C1.243337-3.809714 1.40274-3.985056 1.40274-4.216189C1.40274-4.511083 1.147696-4.622665 .972354-4.630635C1.307098-5.068991 1.920797-5.092902 2.064259-5.092902C2.271482-5.092902 2.87721-5.029141 2.87721-4.208219C2.87721-3.650311 2.646077-3.315567 2.534496-3.188045C2.295392-2.940971 2.11208-2.925031 1.625903-2.893151C1.474471-2.885181 1.41071-2.87721 1.41071-2.773599C1.41071-2.662017 1.482441-2.662017 1.617933-2.662017H2.016438Z"/>
<path id="g3-53" d="M1.115816-4.479203C1.219427-4.447323 1.538232-4.367621 1.872976-4.367621C2.86924-4.367621 3.474969-5.068991 3.474969-5.188543C3.474969-5.276214 3.419178-5.300125 3.379328-5.300125C3.363387-5.300125 3.347447-5.300125 3.275716-5.260274C2.964882-5.140722 2.598257-5.045081 2.16787-5.045081C1.697634-5.045081 1.307098-5.164633 1.060025-5.260274C.980324-5.300125 .964384-5.300125 .956413-5.300125C.852802-5.300125 .852802-5.212453 .852802-5.068991V-2.733748C.852802-2.590286 .852802-2.494645 .980324-2.494645C1.044085-2.494645 1.067995-2.526526 1.107846-2.590286C1.203487-2.709838 1.506351-3.116314 2.183811-3.116314C2.630137-3.116314 2.84533-2.749689 2.917061-2.598257C3.052553-2.311333 3.068493-1.944707 3.068493-1.633873C3.068493-1.338979 3.060523-.908593 2.83736-.557908C2.685928-.318804 2.367123-.071731 1.944707-.071731C1.42665-.071731 .916563-.398506 .73325-.916563C.757161-.908593 .804981-.908593 .812951-.908593C1.036115-.908593 1.211457-1.052055 1.211457-1.299128C1.211457-1.594022 .980324-1.697634 .820922-1.697634C.67746-1.697634 .422416-1.617933 .422416-1.275218C.422416-.557908 1.044085 .167372 1.960648 .167372C2.956912 .167372 3.801743-.605729 3.801743-1.594022C3.801743-2.518555 3.132254-3.339477 2.191781-3.339477C1.793275-3.339477 1.41868-3.211955 1.115816-2.940971V-4.479203Z"/>
<path id="g3-54" d="M1.099875-2.638107C1.099875-3.299626 1.155666-3.881445 1.44259-4.367621C1.681694-4.766127 2.088169-5.092902 2.590286-5.092902C2.749689-5.092902 3.116314-5.068991 3.299626-4.790037C2.940971-4.774097 2.909091-4.503113 2.909091-4.415442C2.909091-4.176339 3.092403-4.040847 3.283686-4.040847C3.427148-4.040847 3.658281-4.128518 3.658281-4.431382C3.658281-4.909589 3.299626-5.300125 2.582316-5.300125C1.474471-5.300125 .350685-4.24807 .350685-2.526526C.350685-.366625 1.354919 .167372 2.12802 .167372C2.510585 .167372 2.925031 .063761 3.283686-.278954C3.602491-.589788 3.873474-.924533 3.873474-1.617933C3.873474-2.662017 3.084433-3.395268 2.199751-3.395268C1.625903-3.395268 1.283188-3.028643 1.099875-2.638107ZM2.12802-.071731C1.705604-.071731 1.44259-.366625 1.323039-.589788C1.139726-.948443 1.123786-1.490411 1.123786-1.793275C1.123786-2.582316 1.554172-3.172105 2.16787-3.172105C2.566376-3.172105 2.805479-2.964882 2.956912-2.685928C3.124284-2.391034 3.124284-2.032379 3.124284-1.625903S3.124284-.868742 2.964882-.581818C2.757659-.215193 2.478705-.071731 2.12802-.071731Z"/>
<path id="g3-55" d="M4.032877-4.853798C4.104608-4.941469 4.104608-4.95741 4.104608-5.132752H2.080199C1.880946-5.132752 1.633873-5.140722 1.43462-5.156663C1.020174-5.188543 1.012204-5.260274 .988294-5.387796H.74122L.470237-3.706102H.71731C.73325-3.825654 .820922-4.375592 .932503-4.439352C1.020174-4.479203 1.617933-4.479203 1.737484-4.479203H3.427148L2.606227-3.379328C1.697634-2.16787 1.506351-.908593 1.506351-.278954C1.506351-.199253 1.506351 .167372 1.880946 .167372S2.255542-.191283 2.255542-.286924V-.669489C2.255542-1.817186 2.446824-2.757659 2.83736-3.275716L4.032877-4.853798Z"/>
<path id="g2-58" d="M2.199751-.573848C2.199751-.920548 1.912827-1.159651 1.625903-1.159651C1.279203-1.159651 1.0401-.872727 1.0401-.585803C1.0401-.239103 1.327024 0 1.613948 0C1.960648 0 2.199751-.286924 2.199751-.573848Z"/>
<path id="g2-67" d="M8.930511-8.308842C8.930511-8.416438 8.846824-8.416438 8.822914-8.416438S8.751183-8.416438 8.655542-8.296887L7.830635-7.292653C7.412204-8.009963 6.75467-8.416438 5.858032-8.416438C3.275716-8.416438 .597758-5.798257 .597758-2.988792C.597758-.992279 1.996513 .251059 3.741968 .251059C4.698381 .251059 5.535243-.155417 6.228643-.74122C7.268742-1.613948 7.579577-2.773599 7.579577-2.86924C7.579577-2.976837 7.483935-2.976837 7.44807-2.976837C7.340473-2.976837 7.328518-2.905106 7.304608-2.857285C6.75467-.992279 5.140722-.095641 3.945205-.095641C2.677958-.095641 1.578082-.908593 1.578082-2.606227C1.578082-2.988792 1.697634-5.068991 3.048568-6.635118C3.706102-7.400249 4.829888-8.069738 5.965629-8.069738C7.280697-8.069738 7.866501-6.981818 7.866501-5.762391C7.866501-5.451557 7.830635-5.188543 7.830635-5.140722C7.830635-5.033126 7.950187-5.033126 7.986052-5.033126C8.117559-5.033126 8.129514-5.045081 8.177335-5.260274L8.930511-8.308842Z"/>
<path id="g2-80" d="M3.53873-3.801743H5.547198C7.197011-3.801743 8.846824-5.021171 8.846824-6.38406C8.846824-7.316563 8.057783-8.16538 6.551432-8.16538H2.857285C2.630137-8.16538 2.52254-8.16538 2.52254-7.938232C2.52254-7.81868 2.630137-7.81868 2.809465-7.81868C3.53873-7.81868 3.53873-7.723039 3.53873-7.591532C3.53873-7.567621 3.53873-7.49589 3.490909-7.316563L1.876961-.884682C1.769365-.466252 1.745455-.3467 .908593-.3467C.681445-.3467 .561893-.3467 .561893-.131507C.561893 0 .669489 0 .74122 0C.968369 0 1.207472-.02391 1.43462-.02391H2.833375C3.060523-.02391 3.311582 0 3.53873 0C3.634371 0 3.765878 0 3.765878-.227148C3.765878-.3467 3.658281-.3467 3.478954-.3467C2.761644-.3467 2.749689-.430386 2.749689-.549938C2.749689-.609714 2.761644-.6934 2.773599-.753176L3.53873-3.801743ZM4.399502-7.352428C4.507098-7.79477 4.554919-7.81868 5.021171-7.81868H6.204732C7.10137-7.81868 7.84259-7.531756 7.84259-6.635118C7.84259-6.324284 7.687173-5.308095 7.137235-4.758157C6.933998-4.542964 6.360149-4.088667 5.272229-4.088667H3.58655L4.399502-7.352428Z"/>
<path id="g2-84" d="M4.985305-7.292653C5.057036-7.579577 5.080946-7.687173 5.260274-7.734994C5.355915-7.758904 5.750436-7.758904 6.001494-7.758904C7.197011-7.758904 7.758904-7.711083 7.758904-6.77858C7.758904-6.599253 7.711083-6.144956 7.639352-5.702615L7.627397-5.559153C7.627397-5.511333 7.675218-5.439601 7.746949-5.439601C7.866501-5.439601 7.866501-5.499377 7.902366-5.69066L8.249066-7.806725C8.272976-7.914321 8.272976-7.938232 8.272976-7.974097C8.272976-8.105604 8.201245-8.105604 7.962142-8.105604H1.422665C1.147696-8.105604 1.135741-8.093649 1.06401-7.878456L.334745-5.726526C.32279-5.702615 .286924-5.571108 .286924-5.559153C.286924-5.499377 .334745-5.439601 .406476-5.439601C.502117-5.439601 .526027-5.487422 .573848-5.642839C1.075965-7.089415 1.327024-7.758904 2.917061-7.758904H3.718057C4.004981-7.758904 4.124533-7.758904 4.124533-7.627397C4.124533-7.591532 4.124533-7.567621 4.064757-7.352428L2.462765-.932503C2.343213-.466252 2.319303-.3467 1.052055-.3467C.753176-.3467 .669489-.3467 .669489-.119552C.669489 0 .800996 0 .860772 0C1.159651 0 1.470486-.02391 1.769365-.02391H3.634371C3.93325-.02391 4.25604 0 4.554919 0C4.686426 0 4.805978 0 4.805978-.227148C4.805978-.3467 4.722291-.3467 4.411457-.3467C3.335492-.3467 3.335492-.454296 3.335492-.633624C3.335492-.645579 3.335492-.729265 3.383313-.920548L4.985305-7.292653Z"/>
<path id="g0-18" d="M8.368618 28.08269C8.368618 28.034869 8.344707 28.010959 8.320797 27.975093C7.878456 27.532752 7.07746 26.731756 6.276463 25.440598C4.351681 22.356164 3.478954 18.470735 3.478954 13.867995C3.478954 10.652055 3.90934 6.503611 5.881943 2.940971C6.826401 1.243337 7.806725 .263014 8.332752-.263014C8.368618-.298879 8.368618-.32279 8.368618-.358655C8.368618-.478207 8.284932-.478207 8.117559-.478207S7.926276-.478207 7.746949-.298879C3.741968 3.347447 2.486675 8.822914 2.486675 13.85604C2.486675 18.554421 3.56264 23.288667 6.599253 26.863263C6.838356 27.138232 7.292653 27.628394 7.782814 28.05878C7.926276 28.202242 7.950187 28.202242 8.117559 28.202242S8.368618 28.202242 8.368618 28.08269Z"/>
<path id="g0-19" d="M6.300374 13.867995C6.300374 9.169614 5.224408 4.435367 2.187796 .860772C1.948692 .585803 1.494396 .095641 1.004234-.334745C.860772-.478207 .836862-.478207 .669489-.478207C.526027-.478207 .418431-.478207 .418431-.358655C.418431-.310834 .466252-.263014 .490162-.239103C.908593 .191283 1.709589 .992279 2.510585 2.283437C4.435367 5.36787 5.308095 9.2533 5.308095 13.85604C5.308095 17.07198 4.877709 21.220423 2.905106 24.783064C1.960648 26.480697 .968369 27.472976 .466252 27.975093C.442341 28.010959 .418431 28.046824 .418431 28.08269C.418431 28.202242 .526027 28.202242 .669489 28.202242C.836862 28.202242 .860772 28.202242 1.0401 28.022914C5.045081 24.376588 6.300374 18.901121 6.300374 13.867995Z"/>
</defs>
<g fill="#222" stroke="#222" style="fill: var(--color, #222); stroke: var(--color, #222);" stroke-width="0.3" transform="matrix(1.13 0 0 1.13 -80.23 -65.539978)">
<use x="70.734745" y="58.2615" xlink:href="#g0-18"/>
<use x="79.535117" y="67.945294" xlink:href="#g2-67"/>
<use x="87.910163" y="69.738557" xlink:href="#g3-49"/>
<use x="92.144346" y="69.738557" xlink:href="#g3-49"/>
<use x="106.839297" y="67.945294" xlink:href="#g2-67"/>
<use x="115.214343" y="69.738557" xlink:href="#g3-49"/>
<use x="119.448526" y="69.738557" xlink:href="#g3-50"/>
<use x="79.535117" y="81.89299" xlink:href="#g2-67"/>
<use x="87.910163" y="83.686253" xlink:href="#g3-50"/>
<use x="92.144346" y="83.686253" xlink:href="#g3-49"/>
<use x="106.839297" y="81.89299" xlink:href="#g2-67"/>
<use x="115.214343" y="83.686253" xlink:href="#g3-50"/>
<use x="119.448526" y="83.686253" xlink:href="#g3-50"/>
<use x="124.180837" y="58.2615" xlink:href="#g0-19"/>
<use x="136.302039" y="75.118437" xlink:href="#g4-61"/>
<use x="148.72752" y="58.2615" xlink:href="#g0-18"/>
<use x="157.527892" y="67.945294" xlink:href="#g2-80"/>
<use x="165.07316" y="69.738557" xlink:href="#g3-50"/>
<use x="172.462138" y="67.945294" xlink:href="#g4-43"/>
<use x="184.223453" y="67.945294" xlink:href="#g2-80"/>
<use x="191.768721" y="69.738557" xlink:href="#g3-51"/>
<use x="206.463676" y="67.945294" xlink:href="#g2-84"/>
<use x="213.324681" y="69.738557" xlink:href="#g3-49"/>
<use x="220.71366" y="67.945294" xlink:href="#g4-43"/>
<use x="232.474975" y="67.945294" xlink:href="#g2-80"/>
<use x="240.020243" y="69.738557" xlink:href="#g3-53"/>
<use x="247.409221" y="67.945294" xlink:href="#g4-43"/>
<use x="259.170536" y="67.945294" xlink:href="#g2-80"/>
<use x="266.715804" y="69.738557" xlink:href="#g3-54"/>
<use x="157.773112" y="81.89299" xlink:href="#g2-84"/>
<use x="164.634117" y="83.686253" xlink:href="#g3-50"/>
<use x="172.023096" y="81.89299" xlink:href="#g1-0"/>
<use x="183.978256" y="81.89299" xlink:href="#g2-80"/>
<use x="191.523524" y="83.686253" xlink:href="#g3-55"/>
<use x="219.811464" y="81.89299" xlink:href="#g2-84"/>
<use x="226.67247" y="83.686253" xlink:href="#g3-50"/>
<use x="234.061448" y="81.89299" xlink:href="#g4-43"/>
<use x="245.822763" y="81.89299" xlink:href="#g2-80"/>
<use x="253.368031" y="83.686253" xlink:href="#g3-53"/>
<use x="271.448119" y="58.2615" xlink:href="#g0-19"/>
<use x="280.248491" y="75.118437" xlink:href="#g2-58"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

7
jekyll_site/robots.txt Normal file
View file

@ -0,0 +1,7 @@
User-agent: *
Disallow: *404*
Sitemap: https://pomodoro3.mircloud.ru/pagesmap.xml
Sitemap: https://pomodoro3.mircloud.ru/color/pagesmap.xml
Host: https://pomodoro3.mircloud.ru

View file

@ -0,0 +1,246 @@
---
title: Оптимизация умножения матриц
description: Рассмотрим алгоритм перемножения матриц с использованием трёх вложенных циклов. Сложность такого алгоритма по определению должна составлять O(n³), но есть...
sections: [Перестановки,Вложенные циклы,Сравнение алгоритмов]
tags: [java,массивы,многомерные массивы,матрицы,строки,колонки,слои,циклы]
canonical_url: /ru/2021/12/09/optimizing-matrix-multiplication.html
url_translated: /en/2021/12/10/optimizing-matrix-multiplication.html
title_translated: Optimizing matrix multiplication
date: 2021.12.09
---
Рассмотрим алгоритм перемножения матриц с использованием трёх вложенных циклов. Сложность такого
алгоритма по определению должна составлять `O(n³)`, но есть особенности, связанные со средой
выполнения скорость работы алгоритма зависит от последовательности, в которой выполняются циклы.
Сравним различные варианты перестановок вложенных циклов и время выполнения алгоритмов.
Возьмём две матрицы: {`L×M`} и {`M×N`} &rarr; три цикла &rarr; шесть перестановок:
`LMN`, `LNM`, `MLN`, `MNL`, `NLM`, `NML`.
Быстрее других отрабатывают те алгоритмы, которые пишут данные в результирующую матрицу *построчно слоями*:
`LMN` и `MLN`, разница в процентах к другим алгоритмам значительная и зависит от среды выполнения.
*Дальнейшая оптимизация: [Умножение матриц в параллельных потоках]({{ '/ru/2022/02/08/matrix-multiplication-parallel-streams.html' | relative_url }}).*
## Построчный алгоритм {#row-wise-algorithm}
Внешний цикл обходит строки первой матрицы `L`, далее идёт цикл по *общей стороне* двух матриц `M`
и за ним цикл по колонкам второй матрицы `N`. Запись в результирующую матрицу происходит построчно,
а каждая строка заполняется слоями.
```java
/**
* @param l строки матрицы 'a'
* @param m колонки матрицы 'a'
* и строки матрицы 'b'
* @param n колонки матрицы 'b'
* @param a первая матрица 'l×m'
* @param b вторая матрица 'm×n'
* @return результирующая матрица 'l×n'
*/
public static int[][] matrixMultiplicationLMN(int l, int m, int n, int[][] a, int[][] b) {
// результирующая матрица
int[][] c = new int[l][n];
// обходим индексы строк матрицы 'a'
for (int i = 0; i < l; i++)
// обходим индексы общей стороны двух матриц:
// колонок матрицы 'a' и строк матрицы 'b'
for (int k = 0; k < m; k++)
// обходим индексы колонок матрицы 'b'
for (int j = 0; j < n; j++)
// сумма произведений элементов i-ой строки
// матрицы 'a' и j-ой колонки матрицы 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
## Послойный алгоритм {#layer-wise-algorithm}
Внешний цикл обходит *общую сторону* двух матриц `M`, далее идёт цикл по строкам первой матрицы
`L` и за ним цикл по колонкам второй матрицы `N`. Запись в результирующую матрицу происходит слоями,
а каждый слой заполняется построчно.
```java
/**
* @param l строки матрицы 'a'
* @param m колонки матрицы 'a'
* и строки матрицы 'b'
* @param n колонки матрицы 'b'
* @param a первая матрица 'l×m'
* @param b вторая матрица 'm×n'
* @return результирующая матрица 'l×n'
*/
public static int[][] matrixMultiplicationMLN(int l, int m, int n, int[][] a, int[][] b) {
// результирующая матрица
int[][] c = new int[l][n];
// обходим индексы общей стороны двух матриц:
// колонок матрицы 'a' и строк матрицы 'b'
for (int k = 0; k < m; k++)
// обходим индексы строк матрицы 'a'
for (int i = 0; i < l; i++)
// обходим индексы колонок матрицы 'b'
for (int j = 0; j < n; j++)
// сумма произведений элементов i-ой строки
// матрицы 'a' и j-ой колонки матрицы 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
### Прочие алгоритмы {#other-algorithms}
Обход колонок второй матрицы `N` происходит перед обходом *общей стороны* двух матриц `M`
и/или перед обходом строк первой матрицы `L`.
{% capture collapsed_md %}
```java
public static int[][] matrixMultiplicationLNM(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int i = 0; i < l; i++)
for (int j = 0; j < n; j++)
for (int k = 0; k < m; k++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationNLM(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int j = 0; j < n; j++)
for (int i = 0; i < l; i++)
for (int k = 0; k < m; k++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationMNL(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int k = 0; k < m; k++)
for (int j = 0; j < n; j++)
for (int i = 0; i < l; i++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
```java
public static int[][] matrixMultiplicationNML(int l, int m, int n, int[][] a, int[][] b) {
int[][] c = new int[l][n];
for (int j = 0; j < n; j++)
for (int k = 0; k < m; k++)
for (int i = 0; i < l; i++)
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Код без комментариев" content=collapsed_md -%}
## Сравнение алгоритмов {#comparing-algorithms}
Для проверки возьмём две матрицы `A=[500×700]` и `B=[700×450]`, заполненные случайными числами.
Сначала сравниваем между собой корректность реализации алгоритмов все полученные результаты
должны совпадать. Затем выполняем каждый метод по 10 раз и подсчитываем среднее время выполнения.
```java
// запускаем программу и выводим результат
public static void main(String[] args) throws Exception {
// входящие данные
int l = 500, m = 700, n = 450, steps = 10;
int[][] a = randomMatrix(l, m), b = randomMatrix(m, n);
// карта методов для сравнения
var methods = new TreeMap<String, Callable<int[][]>>(Map.of(
"LMN", () -> matrixMultiplicationLMN(l, m, n, a, b),
"LNM", () -> matrixMultiplicationLNM(l, m, n, a, b),
"MLN", () -> matrixMultiplicationMLN(l, m, n, a, b),
"MNL", () -> matrixMultiplicationMNL(l, m, n, a, b),
"NLM", () -> matrixMultiplicationNLM(l, m, n, a, b),
"NML", () -> matrixMultiplicationNML(l, m, n, a, b)));
int[][] last = null;
// обходим карту методов, проверяем корректность результатов,
// все полученные результаты должны быть равны друг другу
for (var method : methods.entrySet()) {
// следующий метод для сравнения
var next = methods.higherEntry(method.getKey());
// если текущий метод не последний сравниваем результаты двух методов
if (next != null) System.out.println(method.getKey() + "=" + next.getKey() + ": "
// сравниваем результат выполнения текущего метода и следующего за ним
+ Arrays.deepEquals(method.getValue().call(), next.getValue().call()));
// результат выполнения последнего метода
else last = method.getValue().call();
}
int[][] test = last;
// обходим карту методов, замеряем время работы каждого метода
for (var method : methods.entrySet())
// параметры: заголовок, количество шагов, исполняемый код
benchmark(method.getKey(), steps, () -> {
try { // выполняем метод, получаем результат
int[][] result = method.getValue().call();
// проверяем корректность результатов на каждом шаге
if (!Arrays.deepEquals(result, test)) System.out.print("error");
} catch (Exception e) {
e.printStackTrace();
}
});
}
```
{% capture collapsed_md %}
```java
// вспомогательный метод, возвращает матрицу указанного размера
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// вспомогательный метод для замера времени работы переданного кода
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// время выполнения одного шага
System.out.print(" | " + time);
avg += time;
}
// среднее время выполнения
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Вспомогательные методы" content=collapsed_md -%}
Вывод зависит от среды выполнения, время в миллисекундах:
```
LMN=LNM: true
LNM=MLN: true
MLN=MNL: true
MNL=NLM: true
NLM=NML: true
LMN | 191 | 109 | 105 | 106 | 105 | 106 | 106 | 105 | 123 | 109 || 116
LNM | 417 | 418 | 419 | 416 | 416 | 417 | 418 | 417 | 416 | 417 || 417
MLN | 113 | 115 | 113 | 115 | 114 | 114 | 114 | 115 | 114 | 113 || 114
MNL | 857 | 864 | 857 | 859 | 860 | 863 | 862 | 860 | 858 | 860 || 860
NLM | 404 | 404 | 407 | 404 | 406 | 405 | 405 | 404 | 403 | 404 || 404
NML | 866 | 872 | 867 | 868 | 867 | 868 | 867 | 873 | 869 | 863 || 868
```
Все описанные выше методы, включая свёрнутые блоки, можно поместить в одном классе.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Callable;
```
{% endcapture %}
{%- include collapsed_block.html summary="Необходимые импорты" content=collapsed_md -%}

View file

@ -0,0 +1,96 @@
---
title: Поворот матрицы на 90 градусов
description: Рассмотрим алгоритм поворота матрицы на 90 градусов по часовой стрелке и против часовой стрелки. Этот алгоритм похож на транспонирование матрицы с тем...
sections: [Транспонирование,Сравнение алгоритмов]
tags: [java,массивы,многомерные массивы,матрицы,строки,колонки,циклы,вложенные циклы]
canonical_url: /ru/2021/12/12/matrix-rotation-90-degrees.html
url_translated: /en/2021/12/13/matrix-rotation-90-degrees.html
title_translated: Matrix rotation 90 degrees
date: 2021.12.12
---
Рассмотрим алгоритм поворота матрицы на 90 градусов по часовой стрелке и против часовой стрелки.
Этот алгоритм похож на *транспонирование матрицы* с тем отличием, что здесь для каждой точки
одна из координат отображается зеркально.
```java
// транспонирование матрицы строки и колонки меняются местами
swapped[j][i] = matrix[i][j];
// по часовой стрелке строки отображаются зеркально
rotated[j][i] = matrix[m-i-1][j];
// против часовой стрелки колонки отображаются зеркально
rotated[j][i] = matrix[i][n-j-1];
```
*Похожий алгоритм: [Поворот матрицы на 180 градусов]({{ '/ru/2021/12/16/matrix-rotation-180-degrees.html' | relative_url }}).*
Напишем метод на Java для поворота матрицы {`m×n`} на 90 градусов. Дополнительный параметр задаёт
направление поворота: по часовой стрелке или против часовой стрелки. Для примера возьмём
прямоугольную матрицу {`4×3`}.
```java
/**
* @param m количество строк исходной матрицы
* @param n количество колонок исходной матрицы
* @param clock направление поворота: по часовой
* стрелке или против часовой стрелки
* @param matrix исходная матрица
* @return повёрнутая матрица
*/
public static int[][] rotateMatrix(int m, int n, boolean clock, int[][] matrix) {
// новая матрица, количества строк и колонок меняются местами
int[][] rotated = new int[n][m];
// обходим строки исходной матрицы
for (int i = 0; i < m; i++)
// обходим колонки исходной матрицы
for (int j = 0; j < n; j++)
if (clock) // поворот по часовой стрелке
rotated[j][i] = matrix[m-i-1][j];
else // поворот против часовой стрелки
rotated[j][i] = matrix[i][n-j-1];
return rotated;
}
```
{% raw %}
```java
// запускаем программу и выводим результат
public static void main(String[] args) {
// исходные данные
int m = 4, n = 3;
int[][] matrix = {{11, 12, 13}, {14, 15, 16}, {17, 18, 19}, {20, 21, 22}};
// поворачиваем матрицу и выводим результат
outputMatrix("Исходная матрица:", matrix);
outputMatrix("По часовой стрелке ↻:", rotateMatrix(m, n, true, matrix));
outputMatrix("Против часовой стрелки ↺:", rotateMatrix(m, n, false, matrix));
}
```
{% endraw %}
```java
// вспомогательный метод, выводит матрицу в консоль построчно
public static void outputMatrix(String title, int[][] matrix) {
System.out.println(title);
for (int[] row : matrix) {
for (int el : row)
System.out.print(" " + el);
System.out.println();
}
}
```
Вывод:
```
Исходная матрица:
11 12 13
14 15 16
17 18 19
20 21 22
По часовой стрелке :
20 17 14 11
21 18 15 12
22 19 16 13
Против часовой стрелки :
13 16 19 22
12 15 18 21
11 14 17 20
```

View file

@ -0,0 +1,84 @@
---
title: Поворот матрицы на 180 градусов
description: Рассмотрим алгоритм разворота матрицы на 180 градусов. В отличие от алгоритма транспонирования, здесь в результирующей матрице строки и колонки не меняются...
sections: [Транспонирование,Сравнение алгоритмов]
tags: [java,массивы,многомерные массивы,матрицы,строки,колонки,циклы,вложенные циклы]
canonical_url: /ru/2021/12/16/matrix-rotation-180-degrees.html
url_translated: /en/2021/12/17/matrix-rotation-180-degrees.html
title_translated: Matrix rotation 180 degrees
date: 2021.12.16
---
Рассмотрим алгоритм разворота матрицы на 180 градусов. В отличие от алгоритма *транспонирования*,
здесь в результирующей матрице строки и колонки не меняются местами, но отображаются зеркально.
```java
// строки и колонки меняются местами
swapped[j][i] = matrix[i][j];
// строки и колонки отображаются зеркально
rotated[i][j] = matrix[m-i-1][n-j-1];
```
*Похожий алгоритм: [Поворот матрицы на 90 градусов]({{ '/ru/2021/12/12/matrix-rotation-90-degrees.html' | relative_url }}).*
Напишем метод на Java для поворота матрицы {`m×n`} на 180 градусов. Для примера возьмём
прямоугольную матрицу {`4×3`}.
```java
/**
* @param m количество строк исходной матрицы
* @param n количество колонок исходной матрицы
* @param matrix исходная матрица
* @return повёрнутая матрица
*/
public static int[][] rotateMatrix(int m, int n, int[][] matrix) {
// новая матрица
int[][] rotated = new int[m][n];
// обходим строки исходной матрицы
for (int i = 0; i < m; i++)
// обходим колонки исходной матрицы
for (int j = 0; j < n; j++)
// строки и колонки отображаются зеркально
rotated[i][j] = matrix[m-i-1][n-j-1];
return rotated;
}
```
{% raw %}
```java
// запускаем программу и выводим результат
public static void main(String[] args) {
// исходные данные
int m = 4, n = 3;
int[][] matrix = {{11, 12, 13}, {14, 15, 16}, {17, 18, 19}, {20, 21, 22}};
// поворачиваем матрицу и выводим результат
outputMatrix("Исходная матрица:", matrix);
outputMatrix("Поворот на 180°:", rotateMatrix(m, n, matrix));
}
```
{% endraw %}
```java
// вспомогательный метод, выводит матрицу в консоль построчно
public static void outputMatrix(String title, int[][] matrix) {
System.out.println(title);
for (int[] row : matrix) {
for (int el : row)
System.out.print(" " + el);
System.out.println();
}
}
```
Вывод:
```
Исходная матрица:
11 12 13
14 15 16
17 18 19
20 21 22
Поворот на 180°:
22 21 20
19 18 17
16 15 14
13 12 11
```

View file

@ -0,0 +1,182 @@
---
title: Умножение матриц в параллельных потоках
description: Рассмотрим алгоритм перемножения прямоугольных матриц с использованием потоков Java Stream. Возьмём оптимизированный вариант алгоритма на трёх вложенных...
sections: [Многопоточность,Вложенные циклы,Сравнение алгоритмов]
tags: [java,потоки,массивы,многомерные массивы,матрицы,строки,циклы]
canonical_url: /ru/2022/02/08/matrix-multiplication-parallel-streams.html
url_translated: /en/2022/02/09/matrix-multiplication-parallel-streams.html
title_translated: Matrix multiplication in parallel streams
date: 2022.02.08
---
Рассмотрим алгоритм перемножения прямоугольных матриц с использованием потоков Java Stream. Возьмём
*оптимизированный вариант* алгоритма на трёх вложенных циклах и заменим внешний цикл на поток. Сравним
время работы двух алгоритмов.
Строки результирующей матрицы обрабатываем в параллельном режиме, а каждую строку заполняем слоями.
За счёт параллельного использования кеша среды выполнения на многоядерных машинах время вычислений
можно сократить более чем в два раза. Для проверки возьмём две прямоугольные матрицы: {`L×M`} и {`M×N`}.
*Дальнейшая оптимизация: [Алгоритм Винограда Штрассена]({{ '/ru/2022/02/10/winograd-strassen-algorithm.html' | relative_url }}).*
## Параллельный поток {#parallel-stream}
Строки первой матрицы `L` обходим в параллельном режиме. Внутри каждого потока два вложенных цикла:
по *общей стороне* двух матриц `M` и по колонкам второй матрицы `N`. Обработка строк результирующей
матрицы происходит независимо друг от друга в параллельных потоках.
```java
/**
* @param l строки матрицы 'a'
* @param m колонки матрицы 'a'
* и строки матрицы 'b'
* @param n колонки матрицы 'b'
* @param a первая матрица 'l×m'
* @param b вторая матрица 'm×n'
* @return результирующая матрица 'l×n'
*/
public static int[][] parallelMatrixMultiplication(int l, int m, int n, int[][] a, int[][] b) {
// результирующая матрица
int[][] c = new int[l][n];
// обходим индексы строк матрицы 'a' в параллельном режиме
IntStream.range(0, l).parallel().forEach(i -> {
// обходим индексы общей стороны двух матриц:
// колонок матрицы 'a' и строк матрицы 'b'
for (int k = 0; k < m; k++)
// обходим индексы колонок матрицы 'b'
for (int j = 0; j < n; j++)
// сумма произведений элементов i-ой строки
// матрицы 'a' и j-ой колонки матрицы 'b'
c[i][j] += a[i][k] * b[k][j];
});
return c;
}
```
## Три вложенных цикла {#three-nested-loops}
Возьмём оптимизированный вариант алгоритма, который лучше прочих использует кеш среды выполнения.
Внешний цикл обходит строки первой матрицы `L`, далее идёт цикл по *общей стороне* двух матриц `M`
и за ним цикл по колонкам второй матрицы `N`. Запись в результирующую матрицу происходит построчно,
а каждая строка заполняется слоями.
```java
/**
* @param l строки матрицы 'a'
* @param m колонки матрицы 'a'
* и строки матрицы 'b'
* @param n колонки матрицы 'b'
* @param a первая матрица 'l×m'
* @param b вторая матрица 'm×n'
* @return результирующая матрица 'l×n'
*/
public static int[][] sequentialMatrixMultiplication(int l, int m, int n, int[][] a, int[][] b) {
// результирующая матрица
int[][] c = new int[l][n];
// обходим индексы строк матрицы 'a'
for (int i = 0; i < l; i++)
// обходим индексы общей стороны двух матриц:
// колонок матрицы 'a' и строк матрицы 'b'
for (int k = 0; k < m; k++)
// обходим индексы колонок матрицы 'b'
for (int j = 0; j < n; j++)
// сумма произведений элементов i-ой строки
// матрицы 'a' и j-ой колонки матрицы 'b'
c[i][j] += a[i][k] * b[k][j];
return c;
}
```
## Тестирование {#testing}
Для проверки возьмём две матрицы: `A=[900×1000]` и `B=[1000×750]`, заполненные случайными числами.
Сначала сравниваем между собой корректность реализации двух алгоритмов произведения матриц
должны совпадать. Затем выполняем каждый метод по 10 раз и подсчитываем среднее время выполнения.
```java
// запускаем программу и выводим результат
public static void main(String[] args) {
// входящие данные
int l = 900, m = 1000, n = 750, steps = 10;
int[][] a = randomMatrix(l, m), b = randomMatrix(m, n);
// произведения матриц
int[][] c1 = parallelMatrixMultiplication(l, m, n, a, b);
int[][] c2 = sequentialMatrixMultiplication(l, m, n, a, b);
// проверяем корректность результатов
System.out.println("Результаты совпадают: " + Arrays.deepEquals(c1, c2));
// замеряем время работы двух методов
benchmark("Потоки", steps, () -> {
int[][] c = parallelMatrixMultiplication(l, m, n, a, b);
if (!Arrays.deepEquals(c, c1)) System.out.print("ошибка");
});
benchmark("Циклы", steps, () -> {
int[][] c = sequentialMatrixMultiplication(l, m, n, a, b);
if (!Arrays.deepEquals(c, c2)) System.out.print("ошибка");
});
}
```
{% capture collapsed_md %}
```java
// вспомогательный метод, возвращает матрицу указанного размера
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// вспомогательный метод для замера времени работы переданного кода
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// время выполнения одного шага
System.out.print(" | " + time);
avg += time;
}
// среднее время выполнения
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Вспомогательные методы" content=collapsed_md -%}
Вывод зависит от среды выполнения, время в миллисекундах:
```
Результаты совпадают: true
Потоки | 113 | 144 | 117 | 114 | 114 | 117 | 120 | 125 | 111 | 113 || 118
Циклы | 1357 | 530 | 551 | 569 | 535 | 538 | 525 | 517 | 518 | 514 || 615
```
## Сравнение алгоритмов {#comparing-algorithms}
На восьмиядерном компьютере Linux x64 создаём для тестов виртуальную машину Windows x64. При
прочих равных условиях в настройках меняем количество процессоров. Запускаем вышеописанный
тест 100 раз вместо 10 получаем сводную таблицу результатов. Время в миллисекундах.
```
ЦПУ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
-------|-----|-----|-----|-----|-----|-----|-----|-----|
Потоки | 522 | 276 | 179 | 154 | 128 | 112 | 110 | 93 |
Циклы | 519 | 603 | 583 | 571 | 545 | 571 | 559 | 467 |
```
Результаты: при большом количестве процессоров в системе многопоточный алгоритм отрабатывает
заметно быстрее, чем три вложенных цикла. Время вычислений можно сократить более чем в два раза.
Все описанные выше методы, включая свёрнутые блоки, можно поместить в одном классе.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.stream.IntStream;
```
{% endcapture %}
{%- include collapsed_block.html summary="Необходимые импорты" content=collapsed_md -%}

View file

@ -0,0 +1,293 @@
---
title: Алгоритм Винограда Штрассена
description: Рассмотрим модификацию алгоритма Штрассена для умножения квадратных матриц с меньшим количеством сложений между блоками, чем в обычном алгоритме 15 вместо...
sections: [Многопоточность,Блочные матрицы,Сравнение алгоритмов]
tags: [java,потоки,массивы,многомерные массивы,матрицы,рекурсия,циклы,вложенные циклы]
canonical_url: /ru/2022/02/10/winograd-strassen-algorithm.html
url_translated: /en/2022/02/11/winograd-strassen-algorithm.html
title_translated: Winograd Strassen algorithm
date: 2022.02.10
---
Рассмотрим модификацию алгоритма Штрассена для умножения квадратных матриц с *меньшим* количеством
сложений между блоками, чем в обычном алгоритме 15 вместо 18 и таким же количеством умножений как
в обычном алгоритме 7. Будем использовать потоки Java Stream.
Рекурсивное дробление матриц на блоки при перемножении имеет смысл до определенного предела, а дальше
теряет смысл, так как алгоритм Штрассена не использует кеш среды выполнения. Поэтому для малых блоков
будем использовать параллельный вариант вложенных циклов, а для больших блоков параллельно будем
выполнять рекурсивное дробление.
Границу между двумя алгоритмами определяем экспериментально подстраиваем под кеш среды выполнения.
Выгода алгоритма Штрассена становится заметнее на больших матрицах отличие от алгоритма на вложенных
циклах становится больше и зависит от среды выполнения. Сравним время работы двух алгоритмов.
*Алгоритм на трёх вложенных циклах: [Оптимизация умножения матриц]({{ '/ru/2021/12/09/optimizing-matrix-multiplication.html' | relative_url }}).*
## Описание алгоритма {#algorithm-description}
Матрицы должны быть одинакового размера. Разделяем каждую матрицу на 4 равных блока. Блоки должны быть
квадратными, поэтому если это не так, тогда сначала дополняем матрицы нулевыми строками и столбцами,
а после этого разделяем на блоки. Лишние строки и столбцы потом уберём из результирующей матрицы.
{% include image_svg.html src="/img/block-matrices.svg" style="width:221pt; height:33pt;"
alt="{\displaystyle A={\begin{pmatrix}A_{11}&A_{12}\\A_{21}&A_{22}\end{pmatrix}},\quad B={\begin{pmatrix}B_{11}&B_{12}\\B_{21}&B_{22}\end{pmatrix}}.}" %}
Суммируем блоки.
{% include image_svg.html src="/img/sums1.svg" style="width:101pt; height:148pt;"
alt="{\displaystyle{\begin{aligned}S_{1}&=(A_{21}+A_{22});\\S_{2}&=(S_{1}-A_{11});\\S_{3}&=(A_{11}-A_{21});\\S_{4}&=(A_{12}-S_{2});\\S_{5}&=(B_{12}-B_{11});\\S_{6}&=(B_{22}-S_{5});\\S_{7}&=(B_{22}-B_{12});\\S_{8}&=(S_{6}-B_{21}).\end{aligned}}}" %}
Умножаем блоки.
{% include image_svg.html src="/img/products.svg" style="width:75pt; height:127pt;"
alt="{\displaystyle{\begin{aligned}P_{1}&=S_{2}S_{6};\\P_{2}&=A_{11}B_{11};\\P_{3}&=A_{12}B_{21};\\P_{4}&=S_{3}S_{7};\\P_{5}&=S_{1}S_{5};\\P_{6}&=S_{4}B_{22};\\P_{7}&=A_{22}S_{8}.\end{aligned}}}" %}
Суммируем блоки.
{% include image_svg.html src="/img/sums2.svg" style="width:78pt; height:31pt;"
alt="{\displaystyle{\begin{aligned}T_{1}&=P_{1}+P_{2};\\T_{2}&=T_{1}+P_{4}.\end{aligned}}}" %}
Блоки результирующей матрицы.
{% include image_svg.html src="/img/sums3.svg" style="width:240pt; height:33pt;"
alt="{\displaystyle{\begin{pmatrix}C_{11}&C_{12}\\C_{21}&C_{22}\end{pmatrix}}={\begin{pmatrix}P_{2}+P_{3}&T_{1}+P_{5}+P_{6}\\T_{2}-P_{7}&T_{2}+P_{5}\end{pmatrix}}.}" %}
## Гибридный алгоритм {#hybrid-algorithm}
Каждую матрицу `A` и `B` делим на 4 *равных* блока, при необходимости дополняем недостающие части
нулями. Выполняем 15 сложений и 7 умножений над блоками получаем 4 блока матрицы `C`. Убираем
лишние нули, если добавляли, и возвращаем результирующую матрицу. Рекурсивное дробление больших
блоков запускаем в параллельном режиме, а для малых блоков вызываем алгоритм на вложенных циклах.
```java
/**
* @param n размер матрицы
* @param brd минимальный размер матрицы
* @param a первая матрица 'n×n'
* @param b вторая матрица 'n×n'
* @return результирующая матрица 'n×n'
*/
public static int[][] multiplyMatrices(int n, int brd, int[][] a, int[][] b) {
// малые блоки перемножаем с помощью алгоритма на вложенных циклах
if (n < brd) return simpleMultiplication(n, a, b);
// серединная точка матрицы, округляем в большую сторону блоки должны
// быть квадратными, при необходимости добавляем нулевые строки и столбцы
int m = n - n / 2;
// блоки первой матрицы
int[][] a11 = getQuadrant(m, n, a, true, true);
int[][] a12 = getQuadrant(m, n, a, true, false);
int[][] a21 = getQuadrant(m, n, a, false, true);
int[][] a22 = getQuadrant(m, n, a, false, false);
// блоки второй матрицы
int[][] b11 = getQuadrant(m, n, b, true, true);
int[][] b12 = getQuadrant(m, n, b, true, false);
int[][] b21 = getQuadrant(m, n, b, false, true);
int[][] b22 = getQuadrant(m, n, b, false, false);
// суммируем блоки
int[][] s1 = sumMatrices(m, a21, a22, true);
int[][] s2 = sumMatrices(m, s1, a11, false);
int[][] s3 = sumMatrices(m, a11, a21, false);
int[][] s4 = sumMatrices(m, a12, s2, false);
int[][] s5 = sumMatrices(m, b12, b11, false);
int[][] s6 = sumMatrices(m, b22, s5, false);
int[][] s7 = sumMatrices(m, b22, b12, false);
int[][] s8 = sumMatrices(m, s6, b21, false);
int[][][] p = new int[7][][];
// перемножаем блоки в параллельных потоках
IntStream.range(0, 7).parallel().forEach(i -> {
switch (i) { // рекурсивные вызовы
case 0: p[i] = multiplyMatrices(m, brd, s2, s6); break;
case 1: p[i] = multiplyMatrices(m, brd, a11, b11); break;
case 2: p[i] = multiplyMatrices(m, brd, a12, b21); break;
case 3: p[i] = multiplyMatrices(m, brd, s3, s7); break;
case 4: p[i] = multiplyMatrices(m, brd, s1, s5); break;
case 5: p[i] = multiplyMatrices(m, brd, s4, b22); break;
case 6: p[i] = multiplyMatrices(m, brd, a22, s8); break;
}
});
// суммируем блоки
int[][] t1 = sumMatrices(m, p[0], p[1], true);
int[][] t2 = sumMatrices(m, t1, p[3], true);
// блоки результирующей матрицы
int[][] c11 = sumMatrices(m, p[1], p[2], true);
int[][] c12 = sumMatrices(m, t1, sumMatrices(m, p[4], p[5], true), true);
int[][] c21 = sumMatrices(m, t2, p[6], false);
int[][] c22 = sumMatrices(m, t2, p[4], true);
// собираем результирующую матрицу из блоков,
// убираем нулевые строки и столбцы, если добавляли
return putQuadrants(m, n, c11, c12, c21, c22);
}
```
{% capture collapsed_md %}
```java
// вспомогательный метод для суммирования матриц
private static int[][] sumMatrices(int n, int[][] a, int[][] b, boolean sign) {
int[][] c = new int[n][n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
c[i][j] = sign ? a[i][j] + b[i][j] : a[i][j] - b[i][j];
return c;
}
```
```java
// вспомогательный метод, получает блок матрицы
private static int[][] getQuadrant(int m, int n, int[][] x,
boolean first, boolean second) {
int[][] q = new int[m][m];
if (first) for (int i = 0; i < m; i++)
if (second) System.arraycopy(x[i], 0, q[i], 0, m); // x11
else System.arraycopy(x[i], m, q[i], 0, n - m); // x12
else for (int i = m; i < n; i++)
if (second) System.arraycopy(x[i], 0, q[i - m], 0, m); // x21
else System.arraycopy(x[i], m, q[i - m], 0, n - m); // x22
return q;
}
```
```java
// вспомогательный метод, собирает матрицу из блоков
private static int[][] putQuadrants(int m, int n,
int[][] x11, int[][] x12,
int[][] x21, int[][] x22) {
int[][] x = new int[n][n];
for (int i = 0; i < n; i++)
if (i < m) {
System.arraycopy(x11[i], 0, x[i], 0, m);
System.arraycopy(x12[i], 0, x[i], m, n - m);
} else {
System.arraycopy(x21[i - m], 0, x[i], 0, m);
System.arraycopy(x22[i - m], 0, x[i], m, n - m);
}
return x;
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Вспомогательные методы" content=collapsed_md -%}
## Вложенные циклы {#nested-loops}
Для дополнения предыдущего алгоритма и для сравнения с ним же будем использовать *оптимизированный*
вариант вложенных циклов, который лучше прочих использует кеш среды выполнения обработка строк
результирующей матрицы выполняется независимо друг от друга в параллельных потоках. Для малых матриц
будем использовать этот алгоритм большие матрицы делим на малые блоки и используем этот же алгоритм.
```java
/**
* @param n размер матрицы
* @param a первая матрица 'n×n'
* @param b вторая матрица 'n×n'
* @return результирующая матрица 'n×n'
*/
public static int[][] simpleMultiplication(int n, int[][] a, int[][] b) {
// результирующая матрица
int[][] c = new int[n][n];
// обходим индексы строк матрицы 'a' в параллельном режиме
IntStream.range(0, n).parallel().forEach(i -> {
// обходим индексы общей стороны двух матриц:
// колонок матрицы 'a' и строк матрицы 'b'
for (int k = 0; k < n; k++)
// обходим индексы колонок матрицы 'b'
for (int j = 0; j < n; j++)
// сумма произведений элементов i-ой строки
// матрицы 'a' и j-ой колонки матрицы 'b'
c[i][j] += a[i][k] * b[k][j];
});
return c;
}
```
## Тестирование {#testing}
Для проверки возьмём две квадратные матрицы `A=[1000×1000]` и `B=[1000×1000]`, заполненные случайными
числами. Минимальный размер блока возьмём `[200×200]` элементов. Сначала сравниваем между собой
корректность реализации двух алгоритмов произведения матриц должны совпадать. Затем выполняем каждый
метод по 10 раз и подсчитываем среднее время выполнения.
```java
// запускаем программу и выводим результат
public static void main(String[] args) {
// входящие данные
int n = 1000, brd = 200, steps = 10;
int[][] a = randomMatrix(n, n), b = randomMatrix(n, n);
// произведения матриц
int[][] c1 = multiplyMatrices(n, brd, a, b);
int[][] c2 = simpleMultiplication(n, a, b);
// проверяем корректность результатов
System.out.println("Результаты совпадают: " + Arrays.deepEquals(c1, c2));
// замеряем время работы двух методов
benchmark("Гибридный алгоритм", steps, () -> {
int[][] c = multiplyMatrices(n, brd, a, b);
if (!Arrays.deepEquals(c, c1)) System.out.print("ошибка");
});
benchmark("Вложенные циклы ", steps, () -> {
int[][] c = simpleMultiplication(n, a, b);
if (!Arrays.deepEquals(c, c2)) System.out.print("ошибка");
});
}
```
{% capture collapsed_md %}
```java
// вспомогательный метод, возвращает матрицу указанного размера
private static int[][] randomMatrix(int row, int col) {
int[][] matrix = new int[row][col];
for (int i = 0; i < row; i++)
for (int j = 0; j < col; j++)
matrix[i][j] = (int) (Math.random() * row * col);
return matrix;
}
```
```java
// вспомогательный метод для замера времени работы переданного кода
private static void benchmark(String title, int steps, Runnable runnable) {
long time, avg = 0;
System.out.print(title);
for (int i = 0; i < steps; i++) {
time = System.currentTimeMillis();
runnable.run();
time = System.currentTimeMillis() - time;
// время выполнения одного шага
System.out.print(" | " + time);
avg += time;
}
// среднее время выполнения
System.out.println(" || " + (avg / steps));
}
```
{% endcapture %}
{%- include collapsed_block.html summary="Вспомогательные методы" content=collapsed_md -%}
Вывод зависит от среды выполнения, время в миллисекундах:
```
Результаты совпадают: true
Гибридный алгоритм | 196 | 177 | 156 | 205 | 154 | 165 | 133 | 118 | 132 | 134 || 157
Вложенные циклы | 165 | 164 | 168 | 167 | 168 | 168 | 170 | 179 | 173 | 168 || 169
```
## Сравнение алгоритмов {#comparing-algorithms}
На восьмиядерном компьютере Linux x64 запускаем вышеописанный тест 100 раз вместо 10. Минимальный
размер блока берём `[brd=200]`. Изменяем только `n` размеры обеих матриц `A=[n×n]` и `B=[n×n]`.
Получаем сводную таблицу результатов. Время в миллисекундах.
```
n | 900 | 1000 | 1100 | 1200 | 1300 | 1400 | 1500 | 1600 | 1700 |
-------------------|-----|------|------|------|------|------|------|------|------|
Гибридный алгоритм | 96 | 125 | 169 | 204 | 260 | 313 | 384 | 482 | 581 |
Вложенные циклы | 119 | 162 | 235 | 281 | 361 | 497 | 651 | 793 | 971 |
```
Результаты: выгода алгоритма Штрассена становится заметнее на больших матрицах, когда размер
самой матрицы в несколько раз превышает размер минимального блока, и зависит от среды выполнения.
Все описанные выше методы, включая свёрнутые блоки, можно поместить в одном классе.
{% capture collapsed_md %}
```java
import java.util.Arrays;
import java.util.stream.IntStream;
```
{% endcapture %}
{%- include collapsed_block.html summary="Необходимые импорты" content=collapsed_md -%}

73
jekyll_site/ru/index.md Normal file
View file

@ -0,0 +1,73 @@
---
title: Код с комментариями
description: Заметки на тему программирования с примерами кода и комментариями. Решения задач и описания решений.
sections: [Решения задач и описания решений]
tags: [java,алгоритмы,реализация,массивы,многомерные массивы,матрицы,циклы,потоки]
canonical_url: /
url_translated: /en/
title_translated: Code with comments
---
{%- assign articles = "" | split: "" %}
{%- assign articles = articles | push: "Алгоритм Винограда — Штрассена" %}
{%- capture article_brief %}
Рассмотрим модификацию алгоритма Штрассена для умножения квадратных матриц с *меньшим* количеством
сложений между блоками, чем в обычном алгоритме 15 вместо 18 и таким же количеством умножений как
в обычном алгоритме 7. Будем использовать потоки Java Stream.
Рекурсивное дробление матриц на блоки при перемножении имеет смысл до определенного предела, а дальше
теряет смысл, так как алгоритм Штрассена не использует кеш среды выполнения. Поэтому для малых блоков
будем использовать параллельный вариант вложенных циклов, а для больших блоков параллельно будем
выполнять рекурсивное дробление.
Границу между двумя алгоритмами определяем экспериментально подстраиваем под кеш среды выполнения.
Выгода алгоритма Штрассена становится заметнее на больших матрицах отличие от алгоритма на вложенных
циклах становится больше и зависит от среды выполнения. Сравним время работы двух алгоритмов.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Умножение матриц в параллельных потоках" %}
{%- capture article_brief %}
Рассмотрим алгоритм перемножения прямоугольных матриц с использованием потоков Java Stream. Возьмём
*оптимизированный вариант* алгоритма на трёх вложенных циклах и заменим внешний цикл на поток. Сравним
время работы двух алгоритмов.
Строки результирующей матрицы обрабатываем в параллельном режиме, а каждую строку заполняем слоями.
За счёт параллельного использования кеша среды выполнения на многоядерных машинах время вычислений
можно сократить более чем в два раза. Для проверки возьмём две прямоугольные матрицы: {`L×M`} и {`M×N`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Поворот матрицы на 180 градусов" %}
{%- capture article_brief %}
Рассмотрим алгоритм разворота матрицы на 180 градусов. В отличие от алгоритма *транспонирования*,
здесь в результирующей матрице строки и колонки не меняются местами, но отображаются зеркально.
Напишем метод на Java для поворота матрицы {`m×n`} на 180 градусов. Для примера возьмём
прямоугольную матрицу {`4×3`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Поворот матрицы на 90 градусов" %}
{%- capture article_brief %}
Рассмотрим алгоритм поворота матрицы на 90 градусов по часовой стрелке и против часовой стрелки.
Этот алгоритм похож на *транспонирование матрицы* с тем отличием, что здесь для каждой точки
одна из координат отображается зеркально.
Напишем метод на Java для поворота матрицы {`m×n`} на 90 градусов. Дополнительный параметр задаёт
направление поворота: по часовой стрелке или против часовой стрелки. Для примера возьмём
прямоугольную матрицу {`4×3`}.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- assign articles = articles | push: "Оптимизация умножения матриц" %}
{%- capture article_brief %}
Рассмотрим алгоритм перемножения матриц с использованием трёх вложенных циклов. Сложность такого
алгоритма по определению должна составлять `O(n³)`, но есть особенности, связанные со средой
выполнения скорость работы алгоритма зависит от последовательности, в которой выполняются циклы.
Сравним различные варианты перестановок вложенных циклов и время выполнения алгоритмов.
Возьмём две матрицы: {`L×M`} и {`M×N`} &rarr; три цикла &rarr; шесть перестановок:
`LMN`, `LNM`, `MLN`, `MNL`, `NLM`, `NML`.
Быстрее других отрабатывают те алгоритмы, которые пишут данные в результирующую матрицу *построчно слоями*:
`LMN` и `MLN`, разница в процентах к другим алгоритмам значительная и зависит от среды выполнения.
{%- endcapture %}
{%- assign articles = articles | push: article_brief %}
{%- include main_page.html articles = articles -%}

5
package.sh Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
echo "Подготовка архива для последующего развёртывания."
cd _site || exit
rm -rf ../pomodoro3.zip
7z a ../pomodoro3.zip ./*

4
serve.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
echo "Локальное развёртывание для проверки корректности сборки."
jekyll serve --skip-initial-build --disable-disk-cache --host localhost
echo "Адрес сервера: http://localhost:4000"