From 8ed24b365e1affb97e34d3f13c6b3a9f960c64ec Mon Sep 17 00:00:00 2001 From: golovin Date: Sun, 17 Dec 2023 07:55:45 +0300 Subject: [PATCH] 2023-06-30 --- .gitattributes | 2 + .gitignore | 3 + DIRECTORY-TREE.md | 57 +++++ README.en.md | 17 ++ README.md | 17 ++ build.sh | 51 ++++ jekyll_site/Gemfile_color | 3 + jekyll_site/Gemfile_older | 3 + jekyll_site/_config_color.yml | 20 ++ jekyll_site/_config_older.yml | 20 ++ jekyll_site/_includes/counters_body.html | 2 + jekyll_site/_includes/counters_head.html | 16 ++ .../2021/09/07/cartesian-product-of-sets.md | 213 ++++++++++++++++ .../en/2021/09/10/pascals-triangle-in-java.md | 72 ++++++ .../en/2021/09/14/combinations-by-columns.md | 155 +++++++++++ .../22/combinations-of-sequence-elements.md | 240 ++++++++++++++++++ .../05/cartesian-product-parallel-streams.md | 173 +++++++++++++ jekyll_site/en/index.md | 63 +++++ jekyll_site/img/arrangement-formula.svg | 32 +++ jekyll_site/img/arrangements-quantity.gif | Bin 0 -> 16235 bytes jekyll_site/img/three-elements-sequence.svg | 75 ++++++ jekyll_site/robots.txt | 7 + .../2021/09/06/cartesian-product-of-sets.md | 212 ++++++++++++++++ .../ru/2021/09/09/pascals-triangle-in-java.md | 70 +++++ .../ru/2021/09/13/combinations-by-columns.md | 153 +++++++++++ .../20/combinations-of-sequence-elements.md | 235 +++++++++++++++++ .../04/cartesian-product-parallel-streams.md | 173 +++++++++++++ jekyll_site/ru/index.md | 61 +++++ package.sh | 5 + serve.sh | 4 + 30 files changed, 2154 insertions(+) create mode 100644 DIRECTORY-TREE.md create mode 100644 README.en.md create mode 100644 README.md create mode 100755 build.sh create mode 100644 jekyll_site/Gemfile_color create mode 100644 jekyll_site/Gemfile_older create mode 100644 jekyll_site/_config_color.yml create mode 100644 jekyll_site/_config_older.yml create mode 100644 jekyll_site/_includes/counters_body.html create mode 100644 jekyll_site/_includes/counters_head.html create mode 100644 jekyll_site/en/2021/09/07/cartesian-product-of-sets.md create mode 100644 jekyll_site/en/2021/09/10/pascals-triangle-in-java.md create mode 100644 jekyll_site/en/2021/09/14/combinations-by-columns.md create mode 100644 jekyll_site/en/2021/09/22/combinations-of-sequence-elements.md create mode 100644 jekyll_site/en/2021/10/05/cartesian-product-parallel-streams.md create mode 100644 jekyll_site/en/index.md create mode 100644 jekyll_site/img/arrangement-formula.svg create mode 100644 jekyll_site/img/arrangements-quantity.gif create mode 100644 jekyll_site/img/three-elements-sequence.svg create mode 100644 jekyll_site/robots.txt create mode 100644 jekyll_site/ru/2021/09/06/cartesian-product-of-sets.md create mode 100644 jekyll_site/ru/2021/09/09/pascals-triangle-in-java.md create mode 100644 jekyll_site/ru/2021/09/13/combinations-by-columns.md create mode 100644 jekyll_site/ru/2021/09/20/combinations-of-sequence-elements.md create mode 100644 jekyll_site/ru/2021/10/04/cartesian-product-parallel-streams.md create mode 100644 jekyll_site/ru/index.md create mode 100755 package.sh create mode 100755 serve.sh 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 0000000000000000000000000000000000000000..96de878ed49c9969c0aed361d37848db757d0a2e GIT binary patch literal 16235 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 z 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 V&o_P7w|(C?e&@G-i$DPY06Rt1Q=I?+ literal 0 HcmV?d00001 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"