diff --git a/.gitattributes b/.gitattributes index e69de29..4414986 100644 --- a/.gitattributes +++ b/.gitattributes @@ -0,0 +1,2 @@ +jekyll_site/ru/** linguist-language=Java +jekyll_site/en/** linguist-language=Java diff --git a/.gitignore b/.gitignore index c38fa4e..42e9bb7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .idea *.iml +*.zip +_site* +.repo_*.sh diff --git a/DIRECTORY-TREE.md b/DIRECTORY-TREE.md new file mode 100644 index 0000000..45e61ec --- /dev/null +++ b/DIRECTORY-TREE.md @@ -0,0 +1,57 @@ +## Дерево каталогов + +
+. +├─ jekyll_site +│ ├─ _includes +│ │ ├─ counters_body.html +│ │ └─ counters_head.html +│ ├─ en +│ │ ├─ 2021 +│ │ │ ├─ 09 +│ │ │ │ ├─ 07 +│ │ │ │ │ └─ cartesian-product-of-sets.md +│ │ │ │ ├─ 10 +│ │ │ │ │ └─ pascals-triangle-in-java.md +│ │ │ │ ├─ 14 +│ │ │ │ │ └─ combinations-by-columns.md +│ │ │ │ └─ 22 +│ │ │ │ └─ combinations-of-sequence-elements.md +│ │ │ └─ 10 +│ │ │ └─ 05 +│ │ │ └─ cartesian-product-parallel-streams.md +│ │ └─ index.md +│ ├─ img +│ │ ├─ arrangement-formula.svg +│ │ ├─ arrangements-quantity.gif +│ │ └─ three-elements-sequence.svg +│ ├─ ru +│ │ ├─ 2021 +│ │ │ ├─ 09 +│ │ │ │ ├─ 06 +│ │ │ │ │ └─ cartesian-product-of-sets.md +│ │ │ │ ├─ 09 +│ │ │ │ │ └─ pascals-triangle-in-java.md +│ │ │ │ ├─ 13 +│ │ │ │ │ └─ combinations-by-columns.md +│ │ │ │ └─ 20 +│ │ │ │ └─ combinations-of-sequence-elements.md +│ │ │ └─ 10 +│ │ │ └─ 04 +│ │ │ └─ cartesian-product-parallel-streams.md +│ │ └─ index.md +│ ├─ Gemfile_color +│ ├─ Gemfile_older +│ ├─ _config_color.yml +│ ├─ _config_older.yml +│ └─ robots.txt +├─ CONTRIBUTING.md +├─ DIRECTORY-TREE.md +├─ LICENSE.md +├─ OPEN_LICENSE.txt +├─ README.en.md +├─ README.md +├─ build.sh +├─ package.sh +└─ serve.sh +diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..aef0be5 --- /dev/null +++ b/README.en.md @@ -0,0 +1,17 @@ +## Website pages + +- [Cartesian product in parallel streams](https://pomodoro2.mircloud.ru/en/2021/10/05/cartesian-product-parallel-streams.html) — 05.10.2021. +- [Combinations of sequence elements](https://pomodoro2.mircloud.ru/en/2021/09/22/combinations-of-sequence-elements.html) — 22.09.2021. +- [Combinations of elements by columns](https://pomodoro2.mircloud.ru/en/2021/09/14/combinations-by-columns.html) — 14.09.2021. +- [Pascal's Triangle in Java](https://pomodoro2.mircloud.ru/en/2021/09/10/pascals-triangle-in-java.html) — 10.09.2021. +- [Cartesian product of sets](https://pomodoro2.mircloud.ru/en/2021/09/07/cartesian-product-of-sets.html) — 07.09.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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0efd66f --- /dev/null +++ b/README.md @@ -0,0 +1,17 @@ +## Страницы вёб-сайта + +- [Декартово произведение в параллельных потоках](https://pomodoro2.mircloud.ru/ru/2021/10/04/cartesian-product-parallel-streams.html) — 04.10.2021. +- [Комбинации элементов последовательности](https://pomodoro2.mircloud.ru/ru/2021/09/20/combinations-of-sequence-elements.html) — 20.09.2021. +- [Комбинации элементов по столбцам](https://pomodoro2.mircloud.ru/ru/2021/09/13/combinations-by-columns.html) — 13.09.2021. +- [Треугольник Паскаля на Java](https://pomodoro2.mircloud.ru/ru/2021/09/09/pascals-triangle-in-java.html) — 09.09.2021. +- [Декартово произведение множеств](https://pomodoro2.mircloud.ru/ru/2021/09/06/cartesian-product-of-sets.html) — 06.09.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) — Подготовка архива для последующего развёртывания. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..265203a --- /dev/null +++ b/build.sh @@ -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/
//g' "$file"
+ sed -i 's/<\/code><\/pre><\/div><\/div>/<\/code><\/pre><\/div>/g' "$file"
+ sed -i 's/
/
/g' "$file"
+ sed -i -r 's/
/
/g' "$file"
+done
+echo "Время выполнения сборки: $(("$(date '+%s%3N')" - "$milliseconds")) мс."
diff --git a/jekyll_site/Gemfile_color b/jekyll_site/Gemfile_color
new file mode 100644
index 0000000..136ccb7
--- /dev/null
+++ b/jekyll_site/Gemfile_color
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+gem "jekyll"
+gem "color-tomato-theme"
diff --git a/jekyll_site/Gemfile_older b/jekyll_site/Gemfile_older
new file mode 100644
index 0000000..5e19e6f
--- /dev/null
+++ b/jekyll_site/Gemfile_older
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+gem "jekyll"
+gem "older-tomato-theme"
diff --git a/jekyll_site/_config_color.yml b/jekyll_site/_config_color.yml
new file mode 100644
index 0000000..074fc98
--- /dev/null
+++ b/jekyll_site/_config_color.yml
@@ -0,0 +1,20 @@
+# site parameters
+name: "Код с комментариями"
+name_translated: "Code with comments"
+url: "https://pomodoro2.mircloud.ru"
+baseurl: "/color"
+homepage_url: "https://git.org.ru/pomodoro/2"
+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
diff --git a/jekyll_site/_config_older.yml b/jekyll_site/_config_older.yml
new file mode 100644
index 0000000..0e84dd4
--- /dev/null
+++ b/jekyll_site/_config_older.yml
@@ -0,0 +1,20 @@
+# site parameters
+name: "Код с комментариями"
+name_translated: "Code with comments"
+url: "https://pomodoro2.mircloud.ru"
+baseurl: ""
+homepage_url: "https://git.org.ru/pomodoro/2"
+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
diff --git a/jekyll_site/_includes/counters_body.html b/jekyll_site/_includes/counters_body.html
new file mode 100644
index 0000000..5c9093d
--- /dev/null
+++ b/jekyll_site/_includes/counters_body.html
@@ -0,0 +1,2 @@
+
+
diff --git a/jekyll_site/_includes/counters_head.html b/jekyll_site/_includes/counters_head.html
new file mode 100644
index 0000000..2607f89
--- /dev/null
+++ b/jekyll_site/_includes/counters_head.html
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/jekyll_site/en/2021/09/07/cartesian-product-of-sets.md b/jekyll_site/en/2021/09/07/cartesian-product-of-sets.md
new file mode 100644
index 0000000..b0f181d
--- /dev/null
+++ b/jekyll_site/en/2021/09/07/cartesian-product-of-sets.md
@@ -0,0 +1,213 @@
+---
+title: Cartesian product of sets
+description: Consider an algorithm for obtaining a Cartesian product of multiple sets using three nested loops. The number of sets and their elements can be arbitrary.
+sections: [Combinatorics,Direct product]
+tags: [java,combinatorics,algorithms,cartesian product,combinations,set,list,array,loops,nested loops]
+canonical_url: /en/2021/09/07/cartesian-product-of-sets.html
+url_translated: /ru/2021/09/06/cartesian-product-of-sets.html
+title_translated: Декартово произведение множеств
+date: 2021.09.07
+lang: en
+---
+
+Consider an algorithm for obtaining a *Cartesian product* of multiple sets using three nested
+loops. The number of sets and their elements can be arbitrary. We multiply the sets sequentially
+and accumulate the result. The order does not matter, since the product does not change due to the
+permutation of the multipliers. As a result, the order will be different, but the combinations will
+be the same.
+
+For clarity, let's take several *ordered* sets:
+
+```
+a = [A,B,C,D]
+b = [E,F,G]
+c = [H,I]
+d = [J]
+```
+
+The number of possible combinations is the product of the number of elements of all sets:
+
+```
+4 * 3 * 2 * 1 = 24
+```
+
+Let's write a method in Java to solve such issues, we will use three nested `for` loops.
+
+First, we prepare a *list of lists* `List>`, filled with one empty value.
+We will use it as an intermediate result and as a final result.
+
+Then we pass through the received lists and sequentially supplement the intermediate
+result with their elements. At each step, we get a new intermediate result and move on.
+Thus, we consistently multiply pairs of lists and step-by-step accumulate the result.
+
+Schematically, this process looks like this:
+
+```
+result0: [[]]
+ list1: [A,B,C,D]
+--------
+result1: [[A],[B],[C],[D]]
+ list2: [E,F,G]
+--------
+result2: [[A,E],[A,F],[A,G],[B,E],[B,F],...[D,G]]
+ list3: [H,I]
+--------
+result3: [[A,E,H],[A,E,I],[A,F,H],[A,F,I],[A,G,H],...[D,G,I]]
+ list4: [J]
+--------
+result4: [[A,E,H,J],[A,E,I,J],[A,F,H,J],[A,F,I,J],[A,G,H,J],...[D,G,I,J]]
+```
+
+### Combinations by columns {#combinations-by-columns}
+
+```
+Number of combinations: 24
+ [A, E, H, J] [B, E, H, J] [C, E, H, J] [D, E, H, J]
+ [A, E, I, J] [B, E, I, J] [C, E, I, J] [D, E, I, J]
+ [A, F, H, J] [B, F, H, J] [C, F, H, J] [D, F, H, J]
+ [A, F, I, J] [B, F, I, J] [C, F, I, J] [D, F, I, J]
+ [A, G, H, J] [B, G, H, J] [C, G, H, J] [D, G, H, J]
+ [A, G, I, J] [B, G, I, J] [C, G, I, J] [D, G, I, J]
+```
+
+## Cartesian product of lists {#cartesian-product-of-lists}
+
+The list can contain a *modifiable number* of elements. This simplifies the code.
+
+```java
+/**
+ * @param lists an arbitrary number of lists
+ * @param the type of elements in lists
+ * @return the Cartesian product of the passed lists
+ */
+public static List> cartesianProduct(List> lists) {
+ // incoming data is not null
+ if (lists == null) return Collections.emptyList();
+ // intermediate result, contains one empty value
+ List> cp = Collections.singletonList(Collections.emptyList());
+ // pass through the received lists
+ for (List list : lists) {
+ // non-null and non-empty lists
+ if (list == null || list.size() == 0) continue;
+ // next intermediate result
+ List> next = new ArrayList<>();
+ // rows of the current intermediate result
+ for (List row : cp) {
+ // elements of the current list
+ for (T el : list) {
+ // a new row for the next intermediate
+ // result, copy of the current row
+ List nRow = new ArrayList<>(row);
+ // add the current element
+ nRow.add(el);
+ // add to the next intermediate result
+ next.add(nRow);
+ }
+ }
+ // update the intermediate result
+ cp = next;
+ }
+ // the final result
+ return cp;
+}
+```
+
+```java
+// start the program and output the result
+public static void main(String[] args) {
+ // an arbitrary number of lists and their elements
+ List a = Arrays.asList("A", "B", "C", "D");
+ List b = Arrays.asList("E", "F", "G");
+ List c = Arrays.asList("H", "I");
+ List d = Arrays.asList("J");
+ // cartesian product
+ List> cp = cartesianProduct(Arrays.asList(a, b, c, d));
+ // output
+ System.out.println("Number of combinations: " + cp.size());
+ // combinations by columns
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.size(); j++)
+ if (j % rows == i)
+ System.out.print(" " + cp.get(j));
+ System.out.println();
+ }
+}
+```
+
+The output for this and the following code is the same, see above: [combinations by columns](#combinations-by-columns).
+
+## Cartesian product of arrays {#cartesian-product-of-arrays}
+
+The array contains a *fixed number* of elements. The code looks a little more complicated.
+
+```java
+/**
+ * @param arrays an arbitrary number of arrays
+ * @param clazz the class of elements in arrays
+ * @param the type of elements in arrays
+ * @return the Cartesian product of the passed arrays
+ */
+@SuppressWarnings("unchecked")
+public static T[][] cartesianProduct(Class clazz, T[]... arrays) {
+ // incoming data is not null
+ if (clazz == null || arrays == null)
+ return (T[][]) Array.newInstance(clazz, 0, 0);
+ // intermediate result, contains one empty value
+ T[][] cp = (T[][]) Array.newInstance(clazz, 1, 0);
+ // pass through the received arrays
+ for (int a = 0; a < arrays.length; a++) {
+ // current array
+ T[] arr = arrays[a];
+ // non-null and non-empty array
+ if (arr == null || arr.length == 0) continue;
+ // next intermediate result, specify the number of rows
+ T[][] next = (T[][]) Array.newInstance(clazz,cp.length*arr.length,0);
+ // rows of the current intermediate result
+ for (int r = 0; r < cp.length; r++) {
+ // current row
+ T[] row = cp[r];
+ // elements of the current array
+ for (int e = 0; e < arr.length; e++) {
+ // current element
+ T el = arr[e];
+ // copy the current row into the new array [length + 1]
+ T[] nRow = Arrays.copyOf(row, row.length + 1);
+ // add the current element
+ nRow[row.length] = el;
+ // add to the next intermediate result
+ next[r * arr.length + e] = nRow;
+ }
+ }
+ // update the intermediate result
+ cp = next;
+ }
+ // the final result
+ return cp;
+}
+```
+
+```java
+// start the program and output the result
+public static void main(String[] args) {
+ // an arbitrary number of arrays and their elements
+ String[] a = {"A", "B", "C", "D"};
+ String[] b = {"E", "F", "G"};
+ String[] c = {"H", "I"};
+ String[] d = {"J"};
+ // cartesian product
+ String[][] cp = cartesianProduct(String.class, a, b, c, d);
+ // output
+ System.out.println("Number of combinations: " + cp.length);
+ // combinations by columns
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.length; j++)
+ if (j % rows == i)
+ System.out.print(" " + Arrays.toString(cp[j]));
+ System.out.println();
+ }
+}
+```
+
+The output for this and the previous code is the same, see above: [combinations by columns](#combinations-by-columns).
diff --git a/jekyll_site/en/2021/09/10/pascals-triangle-in-java.md b/jekyll_site/en/2021/09/10/pascals-triangle-in-java.md
new file mode 100644
index 0000000..02f5bf0
--- /dev/null
+++ b/jekyll_site/en/2021/09/10/pascals-triangle-in-java.md
@@ -0,0 +1,72 @@
+---
+title: Pascal's Triangle in Java
+description: Consider a variant of the implementation of Pascal's triangle in Java. For the simplicity of storing and processing data, we represent the triangle as...
+sections: [Two-dimensional array,Binomial coefficients]
+tags: [java,algorithms,array,two-dimensional array,loops,nested loops]
+canonical_url: /en/2021/09/10/pascals-triangle-in-java.html
+url_translated: /ru/2021/09/09/pascals-triangle-in-java.html
+title_translated: Треугольник Паскаля на Java
+date: 2021.09.10
+lang: en
+---
+
+Consider a variant of the implementation of *Pascal's triangle* in Java. For the simplicity
+of storing and processing data, we represent the triangle as a two-dimensional array, in which
+the elements of the first row and column are equal to one, and all other elements are the sum
+of the two previous elements in the row and column.
+
+```
+A[i][j] = A[i][j-1] + A[i-1][j];
+```
+
+For example, if {`n = 8`} then the array will look like this:
+
+```
+ 1 1 1 1 1 1 1 1
+ 1 2 3 4 5 6 7
+ 1 3 6 10 15 21
+ 1 4 10 20 35
+ 1 5 15 35
+ 1 6 21
+ 1 7
+ 1
+```
+
+Create and fill a two-dimensional array with decreasing row length:
+
+```java
+int n = 8;
+// array of 'n' rows
+int[][] arr = new int[n][];
+// iterate through the rows of the array
+for (int i = 0; i < n; i++) {
+ // row of 'n-i' elements
+ arr[i] = new int[n - i];
+ // iterate through the elements of the row
+ for (int j = 0; j < n - i; j++) {
+ if (i == 0 || j == 0) {
+ // elements of the first row
+ // and column are equal to one
+ arr[i][j] = 1;
+ } else {
+ // all other elements are the sum of the two
+ // previous elements in the row and column
+ arr[i][j] = arr[i][j - 1] + arr[i - 1][j];
+ }
+ }
+}
+```
+
+Output the array row-wise:
+
+```java
+// iterate through the array rows
+for (int[] row : arr) {
+ // iterate through the row elements
+ for (int el : row)
+ // space and two-digit number
+ System.out.printf(" %2d", el);
+ // line separator
+ System.out.println();
+}
+```
diff --git a/jekyll_site/en/2021/09/14/combinations-by-columns.md b/jekyll_site/en/2021/09/14/combinations-by-columns.md
new file mode 100644
index 0000000..11c6ba0
--- /dev/null
+++ b/jekyll_site/en/2021/09/14/combinations-by-columns.md
@@ -0,0 +1,155 @@
+---
+title: Combinations of elements by columns
+description: In a two-dimensional array, data is stored row-wise. Consider an algorithm for obtaining a Cartesian product by columns using three nested loops. The...
+sections: [Combinatorics,Direct product]
+tags: [java,combinatorics,algorithms,cartesian product,combinations,columns,set,list,array,loops,nested loops]
+canonical_url: /en/2021/09/14/combinations-by-columns.html
+url_translated: /ru/2021/09/13/combinations-by-columns.html
+title_translated: Комбинации элементов по столбцам
+date: 2021.09.14
+lang: en
+---
+
+In a two-dimensional array, data is stored row-wise. Consider an algorithm for obtaining
+a *Cartesian product* by columns using three nested loops. The number of rows and columns
+of the table can be arbitrary. We multiply the columns sequentially and accumulate the
+result. The values do not necessarily have to be populated — we discard the null elements.
+
+For example, let's take a partially filled *jagged two-dimensional array*:
+
+```java
+ | col1 | col2 | col3 | col4
+-----|------|------|------|------
+row1 | "A1" | "B1" | "C1" | "D1"
+row2 | "A2" | "B2" | null | "D2"
+row3 | "A3" | null | null | "D3"
+row4 | "A4" | null |
+```
+
+The number of possible combinations is the product of the number of elements of all columns:
+
+```
+4 * 2 * 1 * 3 = 24
+```
+
+Let's write a method in Java to solve such issues, we will use three nested `for` loops.
+
+First, we prepare a *list of lists* `List>`, filled with one empty value. We will
+use it as an intermediate result and as a final result.
+
+Then we pass through the table columns, while they are present, — we consider the last
+column is the one after which all the elements are null. At each step, we supplement the
+intermediate result with the elements of the current column and get a new intermediate
+result. Thus, we consistently multiply columns and step-by-step accumulate the result.
+
+Schematically, this process looks like this:
+
+```
+res0 * col1 = res1 * col2 = res2 * col3 = res3 * col4 = res4
+-----|------|------|------|-------|------|----------|------|------------
+[] * A1 = A1 * B1 = A1,B1 * C1 = A1,B1,C1 * D1 = A1,B1,C1,D1
+ * A2 = A2 * B2 = A1,B2 * = A1,B2,C1 * D2 = A1,B1,C1,D2
+ * A3 = A3 * = A2,B1 * = A2,B1,C1 * D3 = A1,B1,C1,D3
+ * A4 = A4 * = A2,B2 * = A2,B2,C1 * = A1,B2,C1,D1
+ * = * = A3,B1 * = A3,B1,C1 * = A1,B2,C1,D2
+ * = * = A3,B2 * = A3,B2,C1 * = A1,B2,C1,D3
+ * = * = A4,B1 * = A4,B1,C1 * = A2,B1,C1,D1
+ * = * = A4,B2 * = A4,B2,C1 * = A2,B1,C1,D2
+ * = * = * = * = A2,B1,C1,D3
+ * = * = * = * = A2,B2,C1,D1
+ * = * = * = * = ...........
+ * = * = * = * = ...........
+ * = * = * = * = A4,B2,C1,D3
+-----|------|------|------|-------|------|----------|------|------------
+1 * 4 = 4 * 2 = 8 * 1 = 8 * 3 = 24
+```
+
+### Combinations by columns {#combinations-by-columns}
+
+```
+Number of combinations: 24
+ [A1, B1, C1, D1] [A2, B1, C1, D1] [A3, B1, C1, D1] [A4, B1, C1, D1]
+ [A1, B1, C1, D2] [A2, B1, C1, D2] [A3, B1, C1, D2] [A4, B1, C1, D2]
+ [A1, B1, C1, D3] [A2, B1, C1, D3] [A3, B1, C1, D3] [A4, B1, C1, D3]
+ [A1, B2, C1, D1] [A2, B2, C1, D1] [A3, B2, C1, D1] [A4, B2, C1, D1]
+ [A1, B2, C1, D2] [A2, B2, C1, D2] [A3, B2, C1, D2] [A4, B2, C1, D2]
+ [A1, B2, C1, D3] [A2, B2, C1, D3] [A3, B2, C1, D3] [A4, B2, C1, D3]
+```
+
+## Cartesian product by columns {#cartesian-product-by-columns}
+
+The code will look simpler if you first *transpose* an array of arrays, but if
+this cannot be done, then in the outer loop, pass through the table columns as
+long as they are still exist.
+
+```java
+/**
+ * @param table the two-dimensional list
+ * @param the type of elements in list
+ * @return the Cartesian product of the elements by columns
+ */
+public static List> cartesianProduct(List> table) {
+ // incoming data is not null
+ if (table == null) return Collections.emptyList();
+ // intermediate result, contains one empty value
+ List> cp = Collections.singletonList(Collections.emptyList());
+ // columns are still present
+ boolean notLast = true;
+ // pass through the table columns, while they are present
+ for (int i = 0; notLast; i++) {
+ // there are no more columns
+ notLast = false;
+ // next intermediate result
+ List> next = new ArrayList<>();
+ // pass through the combinations of the current intermediate result
+ for (List comb : cp) {
+ // pass through the rows of the table
+ for (List row : table) {
+ // if the column is still present and its value is also present
+ if (row != null && i < row.size() && row.get(i) != null) {
+ // a new combination, copy the current combination
+ List nComb = new ArrayList<>(comb);
+ // add the value from the column
+ nComb.add(row.get(i));
+ // add to the next intermediate result
+ next.add(nComb);
+ // columns are still present
+ notLast = true;
+ }
+ }
+ }
+ // if the columns are still present, then we update the intermediate
+ // result and go to the next iteration, otherwise we exit the loop
+ if (notLast) cp = next;
+ }
+ // the final result
+ return cp;
+}
+```
+
+```java
+// start the program and output the result
+public static void main(String[] args) {
+ // an arbitrary number of rows and their elements
+ List row1 = Arrays.asList("A1", "B1", "C1", "D1");
+ List row2 = Arrays.asList("A2", "B2", null, "D2");
+ List row3 = Arrays.asList("A3", null, null, "D3");
+ List row4 = Arrays.asList("A4", null);
+ // jagged two-dimensional list
+ List> table = Arrays.asList(row1, row2, row3, row4);
+ // cartesian product
+ List> cp = cartesianProduct(table);
+ // output
+ System.out.println("Number of combinations: " + cp.size());
+ // combinations by columns
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.size(); j++)
+ if (j % rows == i)
+ System.out.print(" " + cp.get(j));
+ System.out.println();
+ }
+}
+```
+
+The output for this code see above: [combinations by columns](#combinations-by-columns).
diff --git a/jekyll_site/en/2021/09/22/combinations-of-sequence-elements.md b/jekyll_site/en/2021/09/22/combinations-of-sequence-elements.md
new file mode 100644
index 0000000..ef86f5b
--- /dev/null
+++ b/jekyll_site/en/2021/09/22/combinations-of-sequence-elements.md
@@ -0,0 +1,240 @@
+---
+title: Combinations of sequence elements
+description: Consider a problem where you need to get all possible combinations of sequence elements, in which the number of elements in the combination does not exceed...
+sections: [Combinatorics,Arrangements,Permutations]
+tags: [java,combinatorics,algorithms,arrangement,permutation,combinations,sequence,set,list,loops,nested loops]
+canonical_url: /en/2021/09/22/combinations-of-sequence-elements.html
+url_translated: /ru/2021/09/20/combinations-of-sequence-elements.html
+title_translated: Комбинации элементов последовательности
+date: 2021.09.22
+lang: en
+---
+
+Consider a problem where you need to get all possible combinations of sequence elements, in which
+the number of elements in the combination does not exceed a given maximum, and let's write a method
+in Java with the appropriate filter for the minimum and maximum number of elements.
+
+*[Table setting problem](#table-setting-problem) • [Method for solving in Java](#combinations-of-length)*
+
+An *arrangement* is an ordered set of {`k`} distinct elements from a set of distinct {`n`} elements,
+where {k ≤ n
}. If {k = n
}, then this ordered set
+is a *permutation*. For the universality of the solution, we'll also consider *permutations* as
+a special case of *arrangement*.
+
+## Number of possible combinations {#number-of-possible-combinations}
+
+For clarity, we'll take a sequence of three elements {`XYZ`}, draw all possible subsets
+of this set, add permutations to them and calculate the number of combinations.
+
+{% include picture.html src="/img/arrangements-quantity.gif" size100=true background=true
+alt="We get the number of all possible arrangements of the sequence elements"
+caption="We get the number of all possible arrangements of the sequence elements" %}
+
+Formula for the number of arrangements:
+
+{% include image_svg.html src="/img/arrangement-formula.svg" style="width: 83pt; height: 46pt;"
+alt="\sum_{k=0}^{n} {n! \over (n-k)!}" %}
+
+Sequence of three elements:
+
+{% include image_svg.html src="/img/three-elements-sequence.svg" style="width: 375pt; height: 48pt;"
+alt="\sum_{k=0}^{3} {3! \over (3-k)!} = {3! \over 3!} + {3! \over 2!} + {3! \over 1!} + {3! \over 0!} = 1+3+6+6 = 16." %}
+
+Implementation in Java:
+
+```java
+public static void main(String[] args) {
+ // number of sequence elements
+ int n = 3;
+ // number of possible combinations
+ int sum = 0;
+ // bypass the possible numbers of elements
+ for (int k = 0; k <= n; k++)
+ // add up the numbers of arrangements
+ sum += factorial(n) / factorial(n - k);
+ // output
+ System.out.println(sum); // 16
+}
+// factorial of a number
+static int factorial(int n) {
+ int fact = 1;
+ for (int i = 2; i <= n; i++)
+ fact *= i;
+ return fact;
+}
+```
+
+## Combinations of three elements {#combinations-of-elements}
+
+Let's compare two sequences of three elements: digits {`123`} and letters {`XYZ`}.
+The type of elements is different — combinations are the same, because the sequence
+numbers of the elements are the same.
+
+```
+number of variants: 16
+[][1][2][3][12][13][21][23][31][32][123][132][213][231][312][321]
+[][X][Y][Z][XY][XZ][YX][YZ][ZX][ZY][XYZ][XZY][YXZ][YZX][ZXY][ZYX]
+```
+
+Let's compare sequences of [`1..12`] elements: the number of variants grows rapidly and
+soon goes beyond the `Integer`, then for {n > 12
} you will need
+to use `Long`, and when {n > 20
} — already `BigInteger`.
+
+```
+n number of variants
+1 2
+2 5
+3 16
+4 65
+5 326
+6 1957
+7 13700
+8 109601
+9 986410
+10 9864101
+11 108505112
+12 1302061345
+```
+
+Consider a problem where you need to limit the possible variants
+by the maximum number of elements contained in them.
+
+## Table setting problem {#table-setting-problem}
+
+Four guests are invited to dinner {n = 4
}, of whom it is known
+that no more than two people will arrive {k = 2
}, and the order
+of their appearance is important, since the table setting depends on it. It is also
+known that when the first one arrives, this person will say who will be the second and
+whether this person will arrive. Calculate the possible variants for table setting.
+
+### Solution approach {#solution-approach}
+
+Let's write a method in Java for solving such problems, which will take three
+parameters as input: sequence size, minimum and maximum number of elements in
+a combination. The method will return a list of combinations of specified length.
+
+```java
+static List> combinationsOfElements(int size, int min, int max)
+```
+
+We call the method with the selection {min=0; max=2
}
+and get a list of combinations of specified length.
+
+```java
+// table setting problem
+public static void main(String[] args) {
+ // four guests are invited
+ int[] arr = {1, 2, 3, 4};
+ // n - number of elements in a sequence
+ // k - maximum number of elements in a combination
+ int n = 4, k = 2;
+ // combinations of elements of specified length [0..2]
+ List> list = combinationsOfElements(n, 0, k);
+ // output
+ System.out.println("number of variants: " + list.size());
+ for (Set set : list) {
+ System.out.print("[");
+ for (int i : set)
+ System.out.print(arr[i]);
+ System.out.print("]");
+ }
+}
+```
+
+Output:
+
+```
+number of variants: 17
+[][1][2][3][4][12][13][14][21][23][24][31][32][34][41][42][43]
+```
+
+## Combinations of elements of specified length {#combinations-of-length}
+
+We write a method in Java using three nested `for` loops. Next, to check, we
+call this method without selection {min=0; max=size
} and get
+all possible combinations. For example, let's take two sequences of three
+elements: digits {`123`} and letters {`XYZ`}.
+
+### Method description {#method-description}
+
+We prepare two lists of combinations: the resulting list and the current list.
+In the current list, the number of elements in all combinations will be the same.
+The maximum number of elements in a combination is the size of the sequence.
+We start with one empty combination and gradually increase the number of elements.
+
+We bypass the possible number of elements and supplement the current combinations
+with those indices that are not yet included. At each step, we increase the current
+number of elements in the combinations by one and, if it gets into selection, then
+we add these combinations to the result.
+
+```java
+/**
+ * @param size the sequence size (0 ≤ min ≤ max ≤ size)
+ * @param min minimum number of elements in a combination
+ * @param max maximum number of elements in a combination
+ * @return combinations of elements of specified length
+ */
+static List> combinationsOfElements(int size, int min, int max) {
+ // invalid incoming data, return an empty list of combinations
+ if (0 > min || min > max || max > size) return Collections.emptyList();
+ // resulting list of combinations
+ List> result = new ArrayList<>();
+ // current list of combinations, number of elements in all
+ // combinations is the same, start with one empty combination
+ List> sublist = Arrays.asList(Collections.emptySet());
+ // bypass the possible number of elements in a combination
+ for (int l = 0; l < Math.min(size, max); l++) {
+ // if current number of elements gets into selection,
+ // then add these combinations to the result
+ if (l >= min) result.addAll(sublist);
+ // next list of combinations
+ List> nSublist = new ArrayList<>();
+ // bypass the current list of combinations
+ for (Set comb : sublist) {
+ // bypass the indexes of the sequence elements
+ for (int i = 0; i < size; i++) {
+ // skip those indexes that already present
+ if (comb.contains(i)) continue;
+ // new combination, copy the current one
+ Set nComb = new LinkedHashSet<>(comb);
+ // adding the current index
+ nComb.add(i);
+ // add in a new list of combinations
+ nSublist.add(nComb);
+ }
+ }
+ // update the current list of combinations
+ sublist = nSublist;
+ }
+ // add the current list of combinations to the result
+ result.addAll(sublist);
+ // return the final result
+ return result;
+}
+```
+
+```java
+// start the program and output the result
+public static void main(String[] args) {
+ // two sequences of three elements
+ Integer[] arr1 = {1, 2, 3};
+ String[] arr2 = {"X", "Y", "Z"};
+ // number of sequence elements
+ int n = 3;
+ // all possible combinations of elements [0..n]
+ List> list = combinationsOfElements(n, 0, n);
+ // output
+ System.out.println("number of variants: " + list.size());
+ for (Object[] arr : Arrays.asList(arr1, arr2)) {
+ StringBuilder sb = new StringBuilder();
+ for (Set set : list) {
+ sb.append("[");
+ for (int i : set) sb.append(arr[i]);
+ sb.append("]");
+ }
+ System.out.println(sb);
+ }
+}
+```
+
+The output for this code see above: [combinations of three elements](#combinations-of-elements).
diff --git a/jekyll_site/en/2021/10/05/cartesian-product-parallel-streams.md b/jekyll_site/en/2021/10/05/cartesian-product-parallel-streams.md
new file mode 100644
index 0000000..e982e76
--- /dev/null
+++ b/jekyll_site/en/2021/10/05/cartesian-product-parallel-streams.md
@@ -0,0 +1,173 @@
+---
+title: Cartesian product in parallel streams
+description: Consider an algorithm for obtaining a Cartesian product using Java Streams. This solution is similar to three nested for loops, with the difference that...
+sections: [Multithreading,Combinatorics,Direct product]
+tags: [java,multithreading,combinatorics,algorithms,cartesian product]
+canonical_url: /en/2021/10/05/cartesian-product-parallel-streams.html
+url_translated: /ru/2021/10/04/cartesian-product-parallel-streams.html
+title_translated: Декартово произведение в параллельных потоках
+date: 2021.10.05
+lang: en
+---
+
+Consider an algorithm for obtaining a *Cartesian product* using Java Streams. This solution is similar
+to three nested `for` loops, with the difference that the outer loop is replaced with a stream for the
+convenience of subsequent parallelization. We’ll use the `reduce` method with three parameters:
+`identity`, `accumulator` and `combiner`.
+
+*Algorithm with three nested loops:
+[Cartesian product of sets]({{ '/en/2021/09/07/cartesian-product-of-sets.html' | relative_url }}).*
+
+The incoming data is an arbitrary number of lists and their elements.
+
+```
+a = [A1,B1,C1,D1]
+b = [A2,B2,C2]
+c = [A3,B3]
+d = [A4]
+```
+
+The number of possible combinations is the product of the number of elements in all lists:
+
+```
+4 * 3 * 2 * 1 = 24
+```
+
+We get a *stream of lists* `Stream>` from the input data and call the `reduce` method:
+
+{% capture md_list_into_div %}
+- `identity` — in our case it is a *list of lists* `List>`, filled with
+one empty value. We will use it as an intermediate result and as a final result.
+
+- `accumulator` — for each step of *reduction*, we define a method with two parameters:
+intermediate result and current list. We supplement the intermediate result with elements
+from the current list.
+
+- `combiner` — is used in parallel mode, combines the results of the work of streams,
+obtains the *Cartesian product* of the results.
+{% endcapture %}
+
+{{- md_list_into_div | markdownify -}}
+
+
+The step-by-step process of accumulating a *Cartesian product* looks like this:
+
+```
+result0: [[]]
+ list1: [A1,B1,C1,D1]
+--------
+result1: [[A1],[B1],[C1],[D1]]
+ list2: [A2,B2,C2]
+--------
+result2: [[A1,A2],[A1,B2],[A1,C2],[B1,A2],[B1,B2],...[D1,C2]]
+ list3: [A3,B3]
+--------
+result3: [[A1,A2,A3],[A1,A2,B3],[A1,B2,A3],[A1,B2,B3],[A1,C2,A3],...[D1,C2,B3]]
+ list4: [A4]
+--------
+result4: [[A1,A2,A3,A4],[A1,A2,B3,A4],[A1,B2,A3,A4],[A1,B2,B3,A4],...[D1,C2,B3,A4]]
+```
+
+### Combinations by columns {#combinations-by-columns}
+
+```
+Number of combinations: 24
+[A1, A2, A3, A4] [B1, A2, A3, A4] [C1, A2, A3, A4] [D1, A2, A3, A4]
+[A1, A2, B3, A4] [B1, A2, B3, A4] [C1, A2, B3, A4] [D1, A2, B3, A4]
+[A1, B2, A3, A4] [B1, B2, A3, A4] [C1, B2, A3, A4] [D1, B2, A3, A4]
+[A1, B2, B3, A4] [B1, B2, B3, A4] [C1, B2, B3, A4] [D1, B2, B3, A4]
+[A1, C2, A3, A4] [B1, C2, A3, A4] [C1, C2, A3, A4] [D1, C2, A3, A4]
+[A1, C2, B3, A4] [B1, C2, B3, A4] [C1, C2, B3, A4] [D1, C2, B3, A4]
+```
+
+## Combinations of elements in parallel streams {#combinations-of-elements-in-parallel-streams}
+
+In parallel mode, the speed of the algorithm increases when multiplying a *large number of small lists*,
+for example, 20 lists of 2 elements or 15 lists of 3 elements. The computation time reduces by *one and
+a half to two* times. In other cases, the computation time is about the same as for three nested `for`
+loops.
+
+```java
+/**
+ * @param lists an arbitrary number of lists
+ * @param the type of elements in lists
+ * @return the Cartesian product of the passed lists
+ */
+public static List> cartesianProduct(List> lists) {
+ // incoming data is not null
+ if (lists == null) return Collections.emptyList();
+ // stream of lists Stream>
+ return lists.stream()
+ // enable parallel mode
+ .parallel()
+ // discard null and empty lists
+ .filter(list -> list != null && list.size() > 0)
+ // reduce the stream of lists into one list, get a Cartesian product
+ // reduce(identity, accumulator, combiner)
+ .reduce( // intermediate result, contains one empty value
+ Collections.singletonList(Collections.emptyList()),
+ // bypass the received lists and supplement the intermediate result
+ // with their elements, at each step obtain a new intermediate result
+ (result, list) -> {
+ // next intermediate result
+ List> nResult = new ArrayList<>(result.size() * list.size());
+ // rows of the current intermediate result
+ for (List row : result) {
+ // elements of the current list
+ for (T el : list) {
+ // a new row for the next intermediate result
+ List nRow = new ArrayList<>(row.size() + 1);
+ // add the current row
+ nRow.addAll(row);
+ // add the current element
+ nRow.add(el);
+ // add to the next intermediate result
+ nResult.add(nRow);
+ }
+ }
+ // pass to the next iteration
+ return nResult;
+ },
+ // is used in parallel mode, combines the results of the work
+ // of streams, obtains the Cartesian product of the results
+ (result1, result2) -> {
+ // combined result
+ List> result = new ArrayList<>(result1.size() * result2.size());
+ // bypass the results
+ for (List comb1 : result1) {
+ for (List comb2 : result2) {
+ // add up the combinations
+ List comb = new ArrayList<>(comb1.size() + comb2.size());
+ comb.addAll(comb1);
+ comb.addAll(comb2);
+ result.add(comb);
+ }
+ }
+ return result;
+ });
+}
+```
+
+```java
+// start the program and output the result
+public static void main(String[] args) {
+ // an arbitrary number of lists and their elements
+ List a = Arrays.asList("A1", "B1", "C1", "D1");
+ List b = Arrays.asList("A2", "B2", "C2");
+ List c = Arrays.asList("A3", "B3");
+ List d = Collections.singletonList("A4");
+ // cartesian product
+ List> cp = cartesianProduct(Arrays.asList(a, b, c, d));
+ // output
+ System.out.println("Number of combinations: " + cp.size());
+ // combinations by columns
+ int rows = 6;
+ IntStream.range(0, rows).forEach(i -> System.out.println(
+ IntStream.range(0, cp.size())
+ .filter(j -> j % rows == i)
+ .mapToObj(j -> cp.get(j).toString())
+ .collect(Collectors.joining(" "))));
+}
+```
+
+The output for this code see above: [combinations by columns](#combinations-by-columns).
diff --git a/jekyll_site/en/index.md b/jekyll_site/en/index.md
new file mode 100644
index 0000000..745c0a2
--- /dev/null
+++ b/jekyll_site/en/index.md
@@ -0,0 +1,63 @@
+---
+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,combinatorics,algorithms,implementation,sets,subsets,lists,arrays,loops,nested loops]
+canonical_url: /en/
+url_translated: /ru/
+title_translated: Код с комментариями
+lang: en
+---
+
+{%- assign articles = "" | split: "" %}
+{%- assign articles = articles | push: "Cartesian product in parallel streams" %}
+{%- capture article_brief %}
+Consider an algorithm for obtaining a *Cartesian product* using Java Streams. This solution is similar
+to three nested `for` loops, with the difference that the outer loop is replaced with a stream for the
+convenience of subsequent parallelization. We’ll use the `reduce` method with three parameters:
+`identity`, `accumulator` and `combiner`.
+
+In parallel mode, the speed of the algorithm increases when multiplying a *large number of small lists*,
+for example, 20 lists of 2 elements or 15 lists of 3 elements. The computation time reduces by *one and
+a half to two* times. In other cases, the computation time is about the same as for three nested `for`
+loops.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Combinations of sequence elements" %}
+{%- capture article_brief %}
+Consider a problem where you need to get all possible combinations of sequence elements, in which
+the number of elements in the combination does not exceed a given maximum, and let's write a method
+in Java with the appropriate filter for the minimum and maximum number of elements.
+
+An *arrangement* is an ordered set of {`k`} distinct elements from a set of distinct {`n`} elements,
+where {k ≤ n
}. If {k = n
}, then this ordered set
+is a *permutation*. For the universality of the solution, we'll also consider *permutations* as
+a special case of *arrangement*.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Combinations of elements by columns" %}
+{%- capture article_brief %}
+In a two-dimensional array, data is stored row-wise. Consider an algorithm for obtaining
+a *Cartesian product* by columns using three nested loops. The number of rows and columns
+of the table can be arbitrary. We multiply the columns sequentially and accumulate the
+result. The values do not necessarily have to be populated — we discard the null elements.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Pascal's Triangle in Java" %}
+{%- capture article_brief %}
+Consider a variant of the implementation of *Pascal's triangle* in Java. For the simplicity
+of storing and processing data, we represent the triangle as a two-dimensional array, in which
+the elements of the first row and column are equal to one, and all other elements are the sum
+of the two previous elements in the row and column.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Cartesian product of sets" %}
+{%- capture article_brief %}
+Consider an algorithm for obtaining a *Cartesian product* of multiple sets using three nested
+loops. The number of sets and their elements can be arbitrary. We multiply the sets sequentially
+and accumulate the result. The order does not matter, since the product does not change due to the
+permutation of the multipliers. As a result, the order will be different, but the combinations will
+be the same.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- include main_page.html articles = articles -%}
diff --git a/jekyll_site/img/arrangement-formula.svg b/jekyll_site/img/arrangement-formula.svg
new file mode 100644
index 0000000..2320929
--- /dev/null
+++ b/jekyll_site/img/arrangement-formula.svg
@@ -0,0 +1,32 @@
+
diff --git a/jekyll_site/img/arrangements-quantity.gif b/jekyll_site/img/arrangements-quantity.gif
new file mode 100644
index 0000000..96de878
Binary files /dev/null and b/jekyll_site/img/arrangements-quantity.gif differ
diff --git a/jekyll_site/img/three-elements-sequence.svg b/jekyll_site/img/three-elements-sequence.svg
new file mode 100644
index 0000000..704a566
--- /dev/null
+++ b/jekyll_site/img/three-elements-sequence.svg
@@ -0,0 +1,75 @@
+
diff --git a/jekyll_site/robots.txt b/jekyll_site/robots.txt
new file mode 100644
index 0000000..062a3cc
--- /dev/null
+++ b/jekyll_site/robots.txt
@@ -0,0 +1,7 @@
+User-agent: *
+Disallow: *404*
+
+Sitemap: https://pomodoro2.mircloud.ru/pagesmap.xml
+Sitemap: https://pomodoro2.mircloud.ru/color/pagesmap.xml
+
+Host: https://pomodoro2.mircloud.ru
diff --git a/jekyll_site/ru/2021/09/06/cartesian-product-of-sets.md b/jekyll_site/ru/2021/09/06/cartesian-product-of-sets.md
new file mode 100644
index 0000000..751e595
--- /dev/null
+++ b/jekyll_site/ru/2021/09/06/cartesian-product-of-sets.md
@@ -0,0 +1,212 @@
+---
+title: Декартово произведение множеств
+description: Рассмотрим алгоритм получения декартова произведения нескольких множеств с использованием трёх вложенных циклов. Количество множеств и их элементов может...
+sections: [Комбинаторика,Прямое произведение]
+tags: [java,комбинаторика,алгоритмы,декартово произведение,комбинации,множество,список,массив,циклы,вложенные циклы]
+canonical_url: /ru/2021/09/06/cartesian-product-of-sets.html
+url_translated: /en/2021/09/07/cartesian-product-of-sets.html
+title_translated: Cartesian product of sets
+date: 2021.09.06
+---
+
+Рассмотрим алгоритм получения *декартова произведения* нескольких множеств с использованием
+трёх вложенных циклов. Количество множеств и их элементов может быть произвольным. Последовательно
+перемножаем множества и накапливаем результат. Порядок значения не имеет, так как от перестановки
+множителей произведение не меняется. В результате порядок будет отличаться, но комбинации будут
+те же самые.
+
+Для наглядности возьмём несколько *упорядоченных* множеств:
+
+```
+a = [A,B,C,D]
+b = [E,F,G]
+c = [H,I]
+d = [J]
+```
+
+Количество возможных комбинаций — это произведение количеств элементов всех множеств:
+
+```
+4 * 3 * 2 * 1 = 24
+```
+
+Напишем метод на Java для решения подобных задач, будем использовать три вложенных цикла `for`.
+
+Сначала подготавливаем *список списков* `List>`, заполненный одним пустым значением.
+Будем использовать его как промежуточный результат и как финальный результат.
+
+Затем обходим переданные списки и последовательно дополняем промежуточный результат
+их элементами. На каждом шаге получаем новый промежуточный результат и двигаемся дальше.
+Таким образом последовательно перемножаем пары списков и постепенно накапливаем результат.
+
+Схематически этот процесс выглядит следующим образом:
+
+```
+result0: [[]]
+ list1: [A,B,C,D]
+--------
+result1: [[A],[B],[C],[D]]
+ list2: [E,F,G]
+--------
+result2: [[A,E],[A,F],[A,G],[B,E],[B,F],...[D,G]]
+ list3: [H,I]
+--------
+result3: [[A,E,H],[A,E,I],[A,F,H],[A,F,I],[A,G,H],...[D,G,I]]
+ list4: [J]
+--------
+result4: [[A,E,H,J],[A,E,I,J],[A,F,H,J],[A,F,I,J],[A,G,H,J],...[D,G,I,J]]
+```
+
+### Комбинации по столбцам {#combinations-by-columns}
+
+```
+Количество комбинаций: 24
+ [A, E, H, J] [B, E, H, J] [C, E, H, J] [D, E, H, J]
+ [A, E, I, J] [B, E, I, J] [C, E, I, J] [D, E, I, J]
+ [A, F, H, J] [B, F, H, J] [C, F, H, J] [D, F, H, J]
+ [A, F, I, J] [B, F, I, J] [C, F, I, J] [D, F, I, J]
+ [A, G, H, J] [B, G, H, J] [C, G, H, J] [D, G, H, J]
+ [A, G, I, J] [B, G, I, J] [C, G, I, J] [D, G, I, J]
+```
+
+## Декартово произведение списков {#cartesian-product-of-lists}
+
+Список может содержать *изменяемое количество* элементов. Это упрощает код.
+
+```java
+/**
+ * @param lists произвольное количество списков
+ * @param тип элементов списков
+ * @return декартово произведение переданных списков
+ */
+public static List> cartesianProduct(List> lists) {
+ // входящие данные не равны null
+ if (lists == null) return Collections.emptyList();
+ // промежуточный результат, содержит одно пустое значение
+ List> cp = Collections.singletonList(Collections.emptyList());
+ // обходим переданные списки
+ for (List list : lists) {
+ // ненулевые и непустые списки
+ if (list == null || list.size() == 0) continue;
+ // следующий промежуточный результат
+ List> next = new ArrayList<>();
+ // строки текущего промежуточного результата
+ for (List row : cp) {
+ // элементы текущего списка
+ for (T el : list) {
+ // новая строка для следующего промежуточного
+ // результата, копируем текущую строку
+ List nRow = new ArrayList<>(row);
+ // добавляем текущий элемент
+ nRow.add(el);
+ // помещаем в следующий промежуточный результат
+ next.add(nRow);
+ }
+ }
+ // обновляем промежуточный результат
+ cp = next;
+ }
+ // возвращаем итоговый результат
+ return cp;
+}
+```
+
+```java
+// запускаем программу и выводим результат
+public static void main(String[] args) {
+ // произвольное количество списков и их элементов
+ List a = Arrays.asList("A", "B", "C", "D");
+ List b = Arrays.asList("E", "F", "G");
+ List c = Arrays.asList("H", "I");
+ List d = Arrays.asList("J");
+ // декартово произведение
+ List> cp = cartesianProduct(Arrays.asList(a, b, c, d));
+ // вывод
+ System.out.println("Количество комбинаций: " + cp.size());
+ // комбинации по столбцам
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.size(); j++)
+ if (j % rows == i)
+ System.out.print(" " + cp.get(j));
+ System.out.println();
+ }
+}
+```
+
+Вывод для этого и следующего кода одинаковый, см. выше: [комбинации по столбцам](#combinations-by-columns).
+
+## Декартово произведение массивов {#cartesian-product-of-arrays}
+
+Массив содержит *фиксированное количество* элементов. Код выглядит немного сложнее.
+
+```java
+/**
+ * @param arrays произвольное количество массивов
+ * @param clazz класс элементов массивов
+ * @param тип элементов массивов
+ * @return декартово произведение переданных массивов
+ */
+@SuppressWarnings("unchecked")
+public static T[][] cartesianProduct(Class clazz, T[]... arrays) {
+ // входящие данные не равны null
+ if (clazz == null || arrays == null)
+ return (T[][]) Array.newInstance(clazz, 0, 0);
+ // промежуточный результат, содержит одну пустую строку
+ T[][] cp = (T[][]) Array.newInstance(clazz, 1, 0);
+ // обходим переданные массивы
+ for (int a = 0; a < arrays.length; a++) {
+ // текущий массив
+ T[] arr = arrays[a];
+ // ненулевой и непустой массив
+ if (arr == null || arr.length == 0) continue;
+ // следующий промежуточный результат, указываем количество строк
+ T[][] next = (T[][]) Array.newInstance(clazz,cp.length*arr.length,0);
+ // строки текущего промежуточного результата
+ for (int r = 0; r < cp.length; r++) {
+ // текущая строка
+ T[] row = cp[r];
+ // элементы текущего массива
+ for (int e = 0; e < arr.length; e++) {
+ // текущий элемент
+ T el = arr[e];
+ // копируем текущую строку в новый массив [длина + 1]
+ T[] nRow = Arrays.copyOf(row, row.length + 1);
+ // добавляем текущий элемент
+ nRow[row.length] = el;
+ // помещаем в следующий промежуточный результат
+ next[r * arr.length + e] = nRow;
+ }
+ }
+ // обновляем промежуточный результат
+ cp = next;
+ }
+ // возвращаем итоговый результат
+ return cp;
+}
+```
+
+```java
+// запускаем программу и выводим результат
+public static void main(String[] args) {
+ // произвольное количество массивов и их элементов
+ String[] a = {"A", "B", "C", "D"};
+ String[] b = {"E", "F", "G"};
+ String[] c = {"H", "I"};
+ String[] d = {"J"};
+ // декартово произведение
+ String[][] cp = cartesianProduct(String.class, a, b, c, d);
+ // вывод
+ System.out.println("Количество комбинаций: " + cp.length);
+ // комбинации по столбцам
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.length; j++)
+ if (j % rows == i)
+ System.out.print(" " + Arrays.toString(cp[j]));
+ System.out.println();
+ }
+}
+```
+
+Вывод для этого и предыдущего кода одинаковый, см. выше: [комбинации по столбцам](#combinations-by-columns).
diff --git a/jekyll_site/ru/2021/09/09/pascals-triangle-in-java.md b/jekyll_site/ru/2021/09/09/pascals-triangle-in-java.md
new file mode 100644
index 0000000..caf5b2c
--- /dev/null
+++ b/jekyll_site/ru/2021/09/09/pascals-triangle-in-java.md
@@ -0,0 +1,70 @@
+---
+title: Треугольник Паскаля на Java
+description: Рассмотрим вариант реализации треугольника Паскаля на Java. Для простоты хранения и обработки данных представим треугольник в виде двухмерного массива...
+sections: [Двухмерный массив,Биномиальные коэффициенты]
+tags: [java,алгоритмы,массив,двухмерный массив,циклы,вложенные циклы]
+canonical_url: /ru/2021/09/09/pascals-triangle-in-java.html
+url_translated: /en/2021/09/10/pascals-triangle-in-java.html
+title_translated: Pascal's Triangle in Java
+date: 2021.09.09
+---
+
+Рассмотрим вариант реализации *треугольника Паскаля* на Java. Для простоты хранения и обработки
+данных представим треугольник в виде двухмерного массива, в котором элементы первой строки и колонки
+равны единице, а все остальные элементы — есть сумма двух предыдущих элементов в строке и в колонке.
+
+```
+A[i][j] = A[i][j-1] + A[i-1][j];
+```
+
+Например, при {`n = 8`} массив будет выглядеть следующим образом:
+
+```
+ 1 1 1 1 1 1 1 1
+ 1 2 3 4 5 6 7
+ 1 3 6 10 15 21
+ 1 4 10 20 35
+ 1 5 15 35
+ 1 6 21
+ 1 7
+ 1
+```
+
+Создаём и заполняем двухмерный массив с убывающей длиной строки:
+
+```java
+int n = 8;
+// массив из 'n' строк
+int[][] arr = new int[n][];
+// обходим строки массива
+for (int i = 0; i < n; i++) {
+ // строка из 'n-i' элементов
+ arr[i] = new int[n - i];
+ // обходим элементы строки
+ for (int j = 0; j < n - i; j++) {
+ if (i == 0 || j == 0) {
+ // элементы первой строки
+ // и колонки равны единице
+ arr[i][j] = 1;
+ } else {
+ // все остальные элементы — есть сумма двух
+ // предыдущих элементов в строке и в колонке
+ arr[i][j] = arr[i][j - 1] + arr[i - 1][j];
+ }
+ }
+}
+```
+
+Выводим массив построчно:
+
+```java
+// обходим строки массива
+for (int[] row : arr) {
+ // обходим элементы строки
+ for (int el : row)
+ // пробел и двузначное число
+ System.out.printf(" %2d", el);
+ // переход на новую строку
+ System.out.println();
+}
+```
diff --git a/jekyll_site/ru/2021/09/13/combinations-by-columns.md b/jekyll_site/ru/2021/09/13/combinations-by-columns.md
new file mode 100644
index 0000000..2ad8fb3
--- /dev/null
+++ b/jekyll_site/ru/2021/09/13/combinations-by-columns.md
@@ -0,0 +1,153 @@
+---
+title: Комбинации элементов по столбцам
+description: В двухмерном массиве данные хранятся построчно. Рассмотрим алгоритм получения декартова произведения по столбцам с использованием трёх вложенных циклов.
+sections: [Комбинаторика,Прямое произведение]
+tags: [java,комбинаторика,алгоритмы,декартово произведение,комбинации,колонки,множество,список,массив,циклы,вложенные циклы]
+canonical_url: /ru/2021/09/13/combinations-by-columns.html
+url_translated: /en/2021/09/14/combinations-by-columns.html
+title_translated: Combinations of elements by columns
+date: 2021.09.13
+---
+
+В двухмерном массиве данные хранятся построчно. Рассмотрим алгоритм получения *декартова
+произведения* по столбцам с использованием трёх вложенных циклов. Количество строк и
+колонок таблицы может быть произвольным. Последовательно перемножаем колонки и накапливаем
+результат. Значения необязательно должны быть заполнены — нулевые элементы отбрасываем.
+
+Для примера возьмём частично заполненный *зубчатый двухмерный массив*:
+
+```java
+ | col1 | col2 | col3 | col4
+-----|------|------|------|------
+row1 | "A1" | "B1" | "C1" | "D1"
+row2 | "A2" | "B2" | null | "D2"
+row3 | "A3" | null | null | "D3"
+row4 | "A4" | null |
+```
+
+Количество возможных комбинаций — это произведение количеств элементов во всех колонках:
+
+```
+4 * 2 * 1 * 3 = 24
+```
+
+Напишем метод на Java для решения подобных задач, будем использовать три вложенных цикла `for`.
+
+Сначала подготавливаем *список списков* `List>`, заполненный одним пустым значением.
+Будем использовать его как промежуточный результат и как финальный результат.
+
+Затем обходим колонки таблицы, пока они ещё есть, — последней считаем ту колонку, за которой
+все элементы нулевые. На каждом шаге дополняем промежуточный результат элементами текущей
+колонки и получаем новый промежуточный результат. Таким образом последовательно перемножаем
+колонки и постепенно накапливаем результат.
+
+Схематически этот процесс выглядит следующим образом:
+
+```
+res0 * col1 = res1 * col2 = res2 * col3 = res3 * col4 = res4
+-----|------|------|------|-------|------|----------|------|------------
+[] * A1 = A1 * B1 = A1,B1 * C1 = A1,B1,C1 * D1 = A1,B1,C1,D1
+ * A2 = A2 * B2 = A1,B2 * = A1,B2,C1 * D2 = A1,B1,C1,D2
+ * A3 = A3 * = A2,B1 * = A2,B1,C1 * D3 = A1,B1,C1,D3
+ * A4 = A4 * = A2,B2 * = A2,B2,C1 * = A1,B2,C1,D1
+ * = * = A3,B1 * = A3,B1,C1 * = A1,B2,C1,D2
+ * = * = A3,B2 * = A3,B2,C1 * = A1,B2,C1,D3
+ * = * = A4,B1 * = A4,B1,C1 * = A2,B1,C1,D1
+ * = * = A4,B2 * = A4,B2,C1 * = A2,B1,C1,D2
+ * = * = * = * = A2,B1,C1,D3
+ * = * = * = * = A2,B2,C1,D1
+ * = * = * = * = ...........
+ * = * = * = * = ...........
+ * = * = * = * = A4,B2,C1,D3
+-----|------|------|------|-------|------|----------|------|------------
+1 * 4 = 4 * 2 = 8 * 1 = 8 * 3 = 24
+```
+
+### Комбинации по столбцам {#combinations-by-columns}
+
+```
+Количество комбинаций: 24
+ [A1, B1, C1, D1] [A2, B1, C1, D1] [A3, B1, C1, D1] [A4, B1, C1, D1]
+ [A1, B1, C1, D2] [A2, B1, C1, D2] [A3, B1, C1, D2] [A4, B1, C1, D2]
+ [A1, B1, C1, D3] [A2, B1, C1, D3] [A3, B1, C1, D3] [A4, B1, C1, D3]
+ [A1, B2, C1, D1] [A2, B2, C1, D1] [A3, B2, C1, D1] [A4, B2, C1, D1]
+ [A1, B2, C1, D2] [A2, B2, C1, D2] [A3, B2, C1, D2] [A4, B2, C1, D2]
+ [A1, B2, C1, D3] [A2, B2, C1, D3] [A3, B2, C1, D3] [A4, B2, C1, D3]
+```
+
+## Декартово произведение по столбцам {#cartesian-product-by-columns}
+
+Код будет выглядеть проще, если предварительно *транспонировать* массив массивов, но если этого
+нельзя сделать, тогда во внешнем цикле обходим колонки массива до тех пор, пока они ещё есть.
+
+```java
+/**
+ * @param table двухмерный список
+ * @param тип элементов списка
+ * @return декартово произведение элементов по столбцам
+ */
+public static List> cartesianProduct(List> table) {
+ // входящие данные не равны null
+ if (table == null) return Collections.emptyList();
+ // промежуточный результат, содержит одно пустое значение
+ List> cp = Collections.singletonList(Collections.emptyList());
+ // колонки ещё есть
+ boolean notLast = true;
+ // обходим колонки таблицы, пока они ещё есть
+ for (int i = 0; notLast; i++) {
+ // колонок больше нет
+ notLast = false;
+ // следующий промежуточный результат
+ List> next = new ArrayList<>();
+ // обходим комбинации текущего промежуточного результата
+ for (List comb : cp) {
+ // обходим строки таблицы
+ for (List row : table) {
+ // если колонка ещё есть и значение в ней тоже есть
+ if (row != null && i < row.size() && row.get(i) != null) {
+ // новая комбинация, копируем текущую комбинацию
+ List nComb = new ArrayList<>(comb);
+ // добавляем значение из колонки
+ nComb.add(row.get(i));
+ // помещаем в новый промежуточный результат
+ next.add(nComb);
+ // колонки ещё есть
+ notLast = true;
+ }
+ }
+ }
+ // если колонки ещё есть, то обновляем промежуточный результат
+ // и переходим на следующую итерацию, иначе выходим из цикла
+ if (notLast) cp = next;
+ }
+ // возвращаем итоговый результат
+ return cp;
+}
+```
+
+```java
+// запускаем программу и выводим результат
+public static void main(String[] args) {
+ // произвольное количество строк и их элементов
+ List row1 = Arrays.asList("A1", "B1", "C1", "D1");
+ List row2 = Arrays.asList("A2", "B2", null, "D2");
+ List row3 = Arrays.asList("A3", null, null, "D3");
+ List row4 = Arrays.asList("A4", null);
+ // зубчатый двухмерный список
+ List> table = Arrays.asList(row1, row2, row3, row4);
+ // декартово произведение
+ List> cp = cartesianProduct(table);
+ // вывод
+ System.out.println("Количество комбинаций: " + cp.size());
+ // комбинации по столбцам
+ int rows = 6;
+ for (int i = 0; i < rows; i++) {
+ for (int j = 0; j < cp.size(); j++)
+ if (j % rows == i)
+ System.out.print(" " + cp.get(j));
+ System.out.println();
+ }
+}
+```
+
+Вывод для этого кода см. выше: [комбинации по столбцам](#combinations-by-columns).
diff --git a/jekyll_site/ru/2021/09/20/combinations-of-sequence-elements.md b/jekyll_site/ru/2021/09/20/combinations-of-sequence-elements.md
new file mode 100644
index 0000000..9d5fcaa
--- /dev/null
+++ b/jekyll_site/ru/2021/09/20/combinations-of-sequence-elements.md
@@ -0,0 +1,235 @@
+---
+title: Комбинации элементов последовательности
+description: Рассмотрим задачу, в которой нужно получить все возможные комбинации элементов последовательности, где количество элементов в комбинации не превышает...
+sections: [Комбинаторика,Размещения,Перестановки]
+tags: [java,комбинаторика,алгоритмы,размещения,перестановки,комбинации,последовательность,множество,список,циклы,вложенные циклы]
+canonical_url: /ru/2021/09/20/combinations-of-sequence-elements.html
+url_translated: /en/2021/09/22/combinations-of-sequence-elements.html
+title_translated: Combinations of sequence elements
+date: 2021.09.20
+---
+
+Рассмотрим задачу, в которой нужно получить все возможные комбинации элементов последовательности,
+где количество элементов в комбинации не превышает заданного максимума, и напишем метод на Java
+с соответствующим отбором по минимальному и максимальному количеству элементов.
+
+*[Задача о сервировке стола](#table-setting-problem) • [Метод для решения на Java](#combinations-of-length)*
+
+*Размещением* называется упорядоченный набор {`k`} различных элементов из множества различных
+{`n`} элементов, где {k ≤ n
}. Если {k = n
}, то
+такой упорядоченный набор называется *перестановкой*. Для универсальности решения *перестановки*
+тоже будем учитывать как частный случай *размещения*.
+
+## Количество возможных комбинаций {#number-of-possible-combinations}
+
+Для наглядности возьмём последовательность из трёх элементов {`XYZ`}, нарисуем все возможные
+подмножества этого множества, добавим к ним перестановки и подсчитаем количество комбинаций.
+
+{% include picture.html src="/img/arrangements-quantity.gif" size100=true background=true
+alt="Получаем количество всех возможных размещений элементов последовательности"
+caption="Получаем количество всех возможных размещений элементов последовательности" %}
+
+Формула количества размещений:
+
+{% include image_svg.html src="/img/arrangement-formula.svg" style="width: 83pt; height: 46pt;"
+alt="\sum_{k=0}^{n} {n! \over (n-k)!}" %}
+
+Последовательность из трёх элементов:
+
+{% include image_svg.html src="/img/three-elements-sequence.svg" style="width: 375pt; height: 48pt;"
+alt="\sum_{k=0}^{3} {3! \over (3-k)!} = {3! \over 3!} + {3! \over 2!} + {3! \over 1!} + {3! \over 0!} = 1+3+6+6 = 16." %}
+
+Способ реализации на Java:
+
+```java
+public static void main(String[] args) {
+ // количество элементов последовательности
+ int n = 3;
+ // количество возможных комбинаций
+ int sum = 0;
+ // обходим возможные количества элементов
+ for (int k = 0; k <= n; k++)
+ // складываем количества размещений
+ sum += factorial(n) / factorial(n - k);
+ // вывод
+ System.out.println(sum); // 16
+}
+// получаем факториал числа
+static int factorial(int n) {
+ int fact = 1;
+ for (int i = 2; i <= n; i++)
+ fact *= i;
+ return fact;
+}
+```
+
+## Комбинации из трёх элементов {#combinations-of-elements}
+
+Сравним две последовательности из трёх элементов: цифр {`123`} и букв {`XYZ`}. Тип элементов
+разный — комбинации одинаковые, потому что порядковые номера у элементов те же самые.
+
+```
+Количество вариантов: 16
+[][1][2][3][12][13][21][23][31][32][123][132][213][231][312][321]
+[][X][Y][Z][XY][XZ][YX][YZ][ZX][ZY][XYZ][XZY][YXZ][YZX][ZXY][ZYX]
+```
+
+Сравним последовательности из [`1..12`] элементов: количество вариантов быстро растёт
+и вскоре выходит за пределы `Integer`, далее при {n > 12
} нужно
+будет использовать `Long`, а при {n > 20
} — уже `BigInteger`.
+
+```
+n Кол-во вариантов
+1 2
+2 5
+3 16
+4 65
+5 326
+6 1957
+7 13700
+8 109601
+9 986410
+10 9864101
+11 108505112
+12 1302061345
+```
+
+Рассмотрим задачу, где нужно ограничить возможные варианты
+по максимальному количеству входящих в них элементов.
+
+## Задача о сервировке стола {#table-setting-problem}
+
+На ужин приглашено четверо гостей {n = 4
}, из которых известно, что
+приедут не более двух человек {k = 2
}, причём порядок их появления
+важен, поскольку от этого зависит сервировка стола. Известно также, что когда приедет первый,
+то он скажет, кто будет второй и приедет ли он. Рассчитать возможные варианты сервировки стола.
+
+### Способ решения {#solution-approach}
+
+Напишем метод на Java для решения подобных задач, который будет принимать на вход три параметра:
+размер последовательности, минимальное и максимальное количество элементов в комбинации.
+Возвращать метод будет список комбинаций указанной длины.
+
+```java
+static List> combinationsOfElements(int size, int min, int max)
+```
+
+Вызываем метод с отбором {min=0; max=2
}
+и получаем список комбинаций указанной длины.
+
+```java
+// задача о сервировке стола
+public static void main(String[] args) {
+ // приглашено четверо гостей
+ int[] arr = {1, 2, 3, 4};
+ // n - количество элементов последовательности
+ // k - максимальное количество элементов в комбинации
+ int n = 4, k = 2;
+ // комбинации элементов указанной длины [0..2]
+ List> list = combinationsOfElements(n, 0, k);
+ // вывод
+ System.out.println("Количество вариантов: " + list.size());
+ for (Set set : list) {
+ System.out.print("[");
+ for (int i : set)
+ System.out.print(arr[i]);
+ System.out.print("]");
+ }
+}
+```
+
+Вывод:
+
+```
+Количество вариантов: 17
+[][1][2][3][4][12][13][14][21][23][24][31][32][34][41][42][43]
+```
+
+## Комбинации элементов указанной длины {#combinations-of-length}
+
+Пишем метод на Java с использованием трёх вложенных циклов `for`. Далее для проверки вызываем
+этот метод без отбора {min=0; max=size
} и получаем все возможные комбинации.
+Для примера возьмём две последовательности из трёх элементов: цифр {`123`} и букв {`XYZ`}.
+
+### Описание метода {#method-description}
+
+Подготавливаем два списка комбинаций: результирующий список и текущий список. В текущем списке
+количество элементов во всех комбинациях будет одинаковым. Максимальное количество элементов
+в комбинации — это размер последовательности. Начинаем с одной пустой комбинации и постепенно
+увеличиваем количество элементов.
+
+Обходим возможные количества элементов и дополняем текущие комбинации теми индексами, которых
+в них ещё нет. На каждом шаге увеличиваем текущее количество элементов в комбинациях на единицу
+и, если оно попадает в отбор, тогда добавляем эти комбинации к результату.
+
+```java
+/**
+ * @param size размер последовательности (0 ≤ min ≤ max ≤ size)
+ * @param min минимальное количество элементов в комбинации
+ * @param max максимальное количество элементов в комбинации
+ * @return комбинации элементов указанной длины
+ */
+static List> combinationsOfElements(int size, int min, int max) {
+ // некорректные входящие данные, возвращаем пустой список комбинаций
+ if (0 > min || min > max || max > size) return Collections.emptyList();
+ // результирующий список комбинаций
+ List> result = new ArrayList<>();
+ // текущий список комбинаций, количество элементов во всех
+ // комбинациях одинаковое, начинаем с одной пустой комбинации
+ List> sublist = Arrays.asList(Collections.emptySet());
+ // обходим возможные количества элементов в комбинации
+ for (int l = 0; l < Math.min(size, max); l++) {
+ // если текущее количество элементов входит в отбор,
+ // тогда добавляем эти комбинации к результату
+ if (l >= min) result.addAll(sublist);
+ // следующий список комбинаций
+ List> nSublist = new ArrayList<>();
+ // обходим текущий список комбинаций
+ for (Set comb : sublist) {
+ // обходим индексы элементов последовательности
+ for (int i = 0; i < size; i++) {
+ // пропускаем те индексы, которые уже есть
+ if (comb.contains(i)) continue;
+ // новая комбинация, копируем текущую
+ Set nComb = new LinkedHashSet<>(comb);
+ // добавляем текущий индекс
+ nComb.add(i);
+ // помещаем в новый список комбинаций
+ nSublist.add(nComb);
+ }
+ }
+ // обновляем текущий список комбинаций
+ sublist = nSublist;
+ }
+ // добавляем текущий список комбинаций к результату
+ result.addAll(sublist);
+ // возвращаем результат
+ return result;
+}
+```
+
+```java
+// запускаем программу и выводим результат
+public static void main(String[] args) {
+ // две последовательности из трёх элементов
+ Integer[] arr1 = {1, 2, 3};
+ String[] arr2 = {"X", "Y", "Z"};
+ // количество элементов последовательности
+ int n = 3;
+ // все возможные комбинации элементов [0..n]
+ List> list = combinationsOfElements(n, 0, n);
+ // вывод
+ System.out.println("Количество вариантов: " + list.size());
+ for (Object[] arr : Arrays.asList(arr1, arr2)) {
+ StringBuilder sb = new StringBuilder();
+ for (Set set : list) {
+ sb.append("[");
+ for (int i : set) sb.append(arr[i]);
+ sb.append("]");
+ }
+ System.out.println(sb);
+ }
+}
+```
+
+Вывод для этого кода см. выше: [комбинации из трёх элементов](#combinations-of-elements).
diff --git a/jekyll_site/ru/2021/10/04/cartesian-product-parallel-streams.md b/jekyll_site/ru/2021/10/04/cartesian-product-parallel-streams.md
new file mode 100644
index 0000000..ffa6ebc
--- /dev/null
+++ b/jekyll_site/ru/2021/10/04/cartesian-product-parallel-streams.md
@@ -0,0 +1,173 @@
+---
+title: Декартово произведение в параллельных потоках
+description: Рассмотрим алгоритм получения декартова произведения с помощью потоков Java Stream. Это решение похоже на три вложенных цикла for с тем отличием, что...
+sections: [Многопоточность,Комбинаторика,Прямое произведение]
+tags: [java,многопоточность,комбинаторика,алгоритмы,декартово произведение]
+canonical_url: /ru/2021/10/04/cartesian-product-parallel-streams.html
+url_translated: /en/2021/10/05/cartesian-product-parallel-streams.html
+title_translated: Cartesian product in parallel streams
+date: 2021.10.04
+---
+
+Рассмотрим алгоритм получения *декартова произведения* с помощью потоков Java Stream. Это решение
+похоже на три вложенных цикла `for` с тем отличием, что здесь внешний цикл заменён на поток для
+удобства последующего распараллеливания. Будем использовать метод `reduce` с тремя параметрами:
+`identity`, `accumulator` и `combiner`.
+
+*Алгоритм с тремя вложенными циклами:
+[Декартово произведение множеств]({{ '/ru/2021/09/06/cartesian-product-of-sets.html' | relative_url }}).*
+
+Входящие данные — это произвольное количество списков и их элементов.
+
+```
+a = [A1,B1,C1,D1]
+b = [A2,B2,C2]
+c = [A3,B3]
+d = [A4]
+```
+
+Количество возможных комбинаций — это произведение количеств элементов всех списков:
+
+```
+4 * 3 * 2 * 1 = 24
+```
+
+Получаем *поток списков* `Stream>` из входящих данных и вызываем метод `reduce`:
+
+{% capture md_list_into_div %}
+- `identity` — сущность. В нашем случае это *список списков* `List>`,
+заполненный одним пустым значением. Будем использовать его как промежуточный
+результат и как финальный результат.
+
+- `accumulator` — накопитель. Для каждого шага *редукции* описываем метод с двумя
+параметрами: промежуточный результат и текущий список. Дополняем промежуточный
+результат значениями из текущего списка.
+
+- `combiner` — объединитель. Используется в многопоточном режиме, объединяем
+результаты работы потоков, получаем *декартово произведение* результатов.
+{% endcapture %}
+
+{{- md_list_into_div | markdownify -}}
+
+
+Пошагово процесс накопления *декартова произведения* выглядит следующим образом:
+
+```
+result0: [[]]
+ list1: [A1,B1,C1,D1]
+--------
+result1: [[A1],[B1],[C1],[D1]]
+ list2: [A2,B2,C2]
+--------
+result2: [[A1,A2],[A1,B2],[A1,C2],[B1,A2],[B1,B2],...[D1,C2]]
+ list3: [A3,B3]
+--------
+result3: [[A1,A2,A3],[A1,A2,B3],[A1,B2,A3],[A1,B2,B3],[A1,C2,A3],...[D1,C2,B3]]
+ list4: [A4]
+--------
+result4: [[A1,A2,A3,A4],[A1,A2,B3,A4],[A1,B2,A3,A4],[A1,B2,B3,A4],...[D1,C2,B3,A4]]
+```
+
+### Комбинации по столбцам {#combinations-by-columns}
+
+```
+Количество комбинаций: 24
+[A1, A2, A3, A4] [B1, A2, A3, A4] [C1, A2, A3, A4] [D1, A2, A3, A4]
+[A1, A2, B3, A4] [B1, A2, B3, A4] [C1, A2, B3, A4] [D1, A2, B3, A4]
+[A1, B2, A3, A4] [B1, B2, A3, A4] [C1, B2, A3, A4] [D1, B2, A3, A4]
+[A1, B2, B3, A4] [B1, B2, B3, A4] [C1, B2, B3, A4] [D1, B2, B3, A4]
+[A1, C2, A3, A4] [B1, C2, A3, A4] [C1, C2, A3, A4] [D1, C2, A3, A4]
+[A1, C2, B3, A4] [B1, C2, B3, A4] [C1, C2, B3, A4] [D1, C2, B3, A4]
+```
+
+## Комбинации элементов в параллельных потоках {#combinations-of-elements-in-parallel-streams}
+
+В параллельном режиме скорость работы алгоритма увеличивается при перемножении *большого количества
+маленьких списков*, например 20 списков по 2 элемента или 15 списков по 3 элемента. Время вычислений
+уменьшается в *полтора-два* раза. В остальных случаях время работы примерно такое же, как у трёх
+вложенных циклов `for`.
+
+```java
+/**
+ * @param lists произвольное количество списков
+ * @param тип элементов списков
+ * @return декартово произведение переданных списков
+ */
+public static List> cartesianProduct(List> lists) {
+ // входящие данные не равны null
+ if (lists == null) return Collections.emptyList();
+ // поток списков Stream>
+ return lists.stream()
+ // включаем параллельный режим
+ .parallel()
+ // отбрасываем нулевые и пустые списки
+ .filter(list -> list != null && list.size() > 0)
+ // сводим поток списков в один список, получаем декартово произведение
+ // reduce(identity, accumulator, combiner)
+ .reduce( // промежуточный результат, содержит одно пустое значение
+ Collections.singletonList(Collections.emptyList()),
+ // обходим переданные списки и дополняем промежуточный результат их
+ // элементами, на каждом шаге получаем новый промежуточный результат
+ (result, list) -> {
+ // следующий промежуточный результат
+ List> nResult = new ArrayList<>(result.size() * list.size());
+ // строки текущего промежуточного результата
+ for (List row : result) {
+ // элементы текущего списка
+ for (T el : list) {
+ // новая строка для следующего промежуточного результата
+ List nRow = new ArrayList<>(row.size() + 1);
+ // добавляем текущую строку
+ nRow.addAll(row);
+ // добавляем текущий элемент
+ nRow.add(el);
+ // помещаем в следующий промежуточный результат
+ nResult.add(nRow);
+ }
+ }
+ // передаём на следующую итерацию
+ return nResult;
+ },
+ // используется в многопоточном режиме, объединяем результаты
+ // работы потоков, получаем декартово произведение результатов
+ (result1, result2) -> {
+ // объединённый результат
+ List> result = new ArrayList<>(result1.size() * result2.size());
+ // обходим результаты
+ for (List comb1 : result1) {
+ for (List comb2 : result2) {
+ // складываем комбинации
+ List comb = new ArrayList<>(comb1.size() + comb2.size());
+ comb.addAll(comb1);
+ comb.addAll(comb2);
+ result.add(comb);
+ }
+ }
+ return result;
+ });
+}
+```
+
+```java
+// запускаем программу и выводим результат
+public static void main(String[] args) {
+ // произвольное количество списков и их элементов
+ List a = Arrays.asList("A1", "B1", "C1", "D1");
+ List b = Arrays.asList("A2", "B2", "C2");
+ List c = Arrays.asList("A3", "B3");
+ List d = Collections.singletonList("A4");
+ // декартово произведение
+ List> cp = cartesianProduct(Arrays.asList(a, b, c, d));
+ // вывод
+ System.out.println("Количество комбинаций: " + cp.size());
+ // комбинации по столбцам
+ int rows = 6;
+ IntStream.range(0, rows).forEach(i -> System.out.println(
+ IntStream.range(0, cp.size())
+ .filter(j -> j % rows == i)
+ .mapToObj(j -> cp.get(j).toString())
+ .collect(Collectors.joining(" "))));
+}
+```
+
+Вывод для этого кода см. выше: [комбинации по столбцам](#combinations-by-columns).
diff --git a/jekyll_site/ru/index.md b/jekyll_site/ru/index.md
new file mode 100644
index 0000000..4cd844a
--- /dev/null
+++ b/jekyll_site/ru/index.md
@@ -0,0 +1,61 @@
+---
+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 %}
+Рассмотрим алгоритм получения *декартова произведения* с помощью потоков Java Stream. Это решение
+похоже на три вложенных цикла `for` с тем отличием, что здесь внешний цикл заменён на поток для
+удобства последующего распараллеливания. Будем использовать метод `reduce` с тремя параметрами:
+`identity`, `accumulator` и `combiner`.
+
+В параллельном режиме скорость работы алгоритма увеличивается при перемножении *большого количества
+маленьких списков*, например 20 списков по 2 элемента или 15 списков по 3 элемента. Время вычислений
+уменьшается в *полтора-два* раза. В остальных случаях время работы примерно такое же, как у трёх
+вложенных циклов `for`.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Комбинации элементов последовательности" %}
+{%- capture article_brief %}
+Рассмотрим задачу, в которой нужно получить все возможные комбинации элементов последовательности,
+где количество элементов в комбинации не превышает заданного максимума, и напишем метод на Java
+с соответствующим отбором по минимальному и максимальному количеству элементов.
+
+*Размещением* называется упорядоченный набор {`k`} различных элементов из множества различных
+{`n`} элементов, где {k ≤ n
}. Если {k = n
}, то
+такой упорядоченный набор называется *перестановкой*. Для универсальности решения *перестановки*
+тоже будем учитывать как частный случай *размещения*.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Комбинации элементов по столбцам" %}
+{%- capture article_brief %}
+В двухмерном массиве данные хранятся построчно. Рассмотрим алгоритм получения *декартова
+произведения* по столбцам с использованием трёх вложенных циклов. Количество строк и
+колонок таблицы может быть произвольным. Последовательно перемножаем колонки и накапливаем
+результат. Значения необязательно должны быть заполнены — нулевые элементы отбрасываем.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Треугольник Паскаля на Java" %}
+{%- capture article_brief %}
+Рассмотрим вариант реализации *треугольника Паскаля* на Java. Для простоты хранения и обработки
+данных представим треугольник в виде двухмерного массива, в котором элементы первой строки и колонки
+равны единице, а все остальные элементы — есть сумма двух предыдущих элементов в строке и в колонке.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- assign articles = articles | push: "Декартово произведение множеств" %}
+{%- capture article_brief %}
+Рассмотрим алгоритм получения *декартова произведения* нескольких множеств с использованием
+трёх вложенных циклов. Количество множеств и их элементов может быть произвольным. Последовательно
+перемножаем множества и накапливаем результат. Порядок значения не имеет, так как от перестановки
+множителей произведение не меняется. В результате порядок будет отличаться, но комбинации будут
+те же самые.
+{%- endcapture %}
+{%- assign articles = articles | push: article_brief %}
+{%- include main_page.html articles = articles -%}
diff --git a/package.sh b/package.sh
new file mode 100755
index 0000000..f03a493
--- /dev/null
+++ b/package.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+echo "Подготовка архива для последующего развёртывания."
+cd _site || exit
+rm -rf ../pomodoro2.zip
+7z a ../pomodoro2.zip ./*
diff --git a/serve.sh b/serve.sh
new file mode 100755
index 0000000..55b689c
--- /dev/null
+++ b/serve.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+echo "Локальное развёртывание для проверки корректности сборки."
+jekyll serve --skip-initial-build --disable-disk-cache --host localhost
+echo "Адрес сервера: http://localhost:4000"