openGPMP
Open Source Mathematics Package
comb.cpp
Go to the documentation of this file.
1 /*************************************************************************
2  *
3  * Project
4  * _____ _____ __ __ _____
5  * / ____| __ \| \/ | __ \
6  * ___ _ __ ___ _ __ | | __| |__) | \ / | |__) |
7  * / _ \| '_ \ / _ \ '_ \| | |_ | ___/| |\/| | ___/
8  *| (_) | |_) | __/ | | | |__| | | | | | | |
9  * \___/| .__/ \___|_| |_|\_____|_| |_| |_|_|
10  * | |
11  * |_|
12  *
13  * Copyright (C) Akiel Aries, <akiel@akiel.org>, et al.
14  *
15  * This software is licensed as described in the file LICENSE, which
16  * you should have received as part of this distribution. The terms
17  * among other details are referenced in the official documentation
18  * seen here : https://akielaries.github.io/openGPMP/ along with
19  * important files seen in this project.
20  *
21  * You may opt to use, copy, modify, merge, publish, distribute
22  * and/or sell copies of the Software, and permit persons to whom
23  * the Software is furnished to do so, under the terms of the
24  * LICENSE file.
25  *
26  *
27  *
28  * This software is distributed on an AS IS basis, WITHOUT
29  * WARRANTY OF ANY KIND, either express or implied.
30  *
31  ************************************************************************/
32 #include <algorithm>
33 #include <cmath>
34 #include <cstdint>
35 #include <numeric>
36 #include <openGPMP/disct/comb.hpp>
37 #include <string>
38 #include <vector>
39 
40 // Function to calculate permutations (n P r)
41 int64_t gpmp::Comb::permutation(int n, int r) {
42  if (n < r)
43  return 0; // Invalid input
44  int64_t result = 1;
45  for (int i = 0; i < r; ++i) {
46  result *= (n - i);
47  }
48  return result;
49 }
50 
51 // Function to calculate combinations (n C r)
52 int64_t gpmp::Comb::combination(int n, int r) {
53  if (n < r)
54  return 0; // Invalid input
55  r = std::min(r, n - r); // Optimization to reduce calculations
56  int64_t result = 1;
57  for (int i = 0; i < r; ++i) {
58  result *= (n - i);
59  result /= (i + 1);
60  }
61  return result;
62 }
63 
64 // Function to calculate binomial coefficient (n choose r)
65 int64_t gpmp::Comb::binom_coeff(int n, int r) {
66  if (n < r)
67  return 0; // Invalid input
68  r = std::min(r, n - r); // Optimization to reduce calculations
69  std::vector<int64_t> dp(r + 1, 0);
70  dp[0] = 1;
71  for (int i = 1; i <= n; ++i) {
72  for (int j = std::min(i, r); j > 0; --j) {
73  dp[j] = dp[j] + dp[j - 1];
74  }
75  }
76  return dp[r];
77 }
78 
79 // Function to calculate factorial (n!)
80 int64_t gpmp::Comb::factorial(int n) {
81  if (n < 0)
82  return 0; // Invalid input
83  int64_t result = 1;
84  for (int i = 1; i <= n; ++i) {
85  result *= i;
86  }
87  return result;
88 }
89 
90 // Function to generate all partitions of an integer n
91 std::vector<std::vector<int>> gpmp::Comb::partitions(int n) {
92  std::vector<std::vector<int>> result;
93  std::vector<int> partition;
94  generatePartitions(n, n, partition, result);
95  return result;
96 }
97 
98 // Function to generate Bell numbers up to n
99 std::vector<int64_t> gpmp::Comb::bell_num(int n) {
100  std::vector<int64_t> bell(n + 1, 0);
101  bell[0] = 1;
102  for (int i = 1; i <= n; ++i) {
103  for (int j = 0; j < i; ++j) {
104  bell[i] += binom_coeff(i - 1, j) * bell[j];
105  }
106  }
107  return bell;
108 }
109 
110 // Function to calculate Stirling numbers of the second kind (S(n, k))
111 int64_t gpmp::Comb::stirling_num(int n, int k) {
112  if (n < k)
113  return 0;
114  if (n == 0 && k == 0)
115  return 1;
116  if (k == 0 || k == n)
117  return 1;
118  return k * stirling_num(n - 1, k) + stirling_num(n - 1, k - 1);
119 }
120 
121 // Function to generate Gray codes of length n
122 std::vector<int> gpmp::Comb::gray_code(int n) {
123  std::vector<int> gray;
124  for (int i = 0; i < (1 << n); ++i) {
125  gray.push_back(i ^ (i >> 1));
126  }
127  return gray;
128 }
129 
130 // Function to calculate the number of derangements (subfactorial)
131 int64_t gpmp::Comb::subfactorial(int n) {
132  if (n == 0)
133  return 1;
134  if (n == 1)
135  return 0;
136  return (n - 1) * (subfactorial(n - 1) + subfactorial(n - 2));
137 }
138 
139 // Function to calculate the number of increasing subsequences of length k in a
140 // permutation of length n
141 int64_t gpmp::Comb::subsequences(int n, int k) {
142  if (k > n)
143  return 0;
144  std::vector<int64_t> dp(n + 1, 0);
145  dp[0] = 1;
146  for (int i = 1; i <= n; ++i) {
147  for (int j = i - 1; j >= 0; --j) {
148  dp[i] += dp[j];
149  }
150  }
151  int64_t result = 0;
152  for (int i = n - k; i <= n; ++i) {
153  result += dp[i];
154  }
155  return result;
156 }
157 
158 // Function to generate all partitions of an integer n using distinct parts
159 std::vector<std::vector<int>> gpmp::Comb::partition_distinct(int n) {
160  std::vector<std::vector<int>> result;
161  std::vector<int> partition;
162  gen_partition_distinct(n, n, partition, result);
163  return result;
164 }
165 
166 // Function to generate all compositions of an integer n (ordered partitions)
167 std::vector<std::vector<int>> gpmp::Comb::compositions(int n) {
168  std::vector<std::vector<int>> result;
169  std::vector<int> composition;
170  gen_compositions(n, n, composition, result);
171  return result;
172 }
173 
174 // Function to calculate the number of Dyck words of length 2n
175 int64_t gpmp::Comb::dyck_words(int n) {
176  return binom_coeff(2 * n, n) / (n + 1);
177 }
178 
179 // Function to generate all Dyck words of length 2n
180 std::vector<std::string> gpmp::Comb::gen_dyck_words_until(int n) {
181  std::vector<std::string> dyck_words;
182  gen_dyck_words("", n, n, dyck_words);
183  return dyck_words;
184 }
185 
186 // Function to calculate the number of necklaces with n beads of k different
187 // colors
188 int64_t gpmp::Comb::necklaces(int n, int k) {
189  if (n == 0)
190  return 0;
191  int64_t result = 0;
192  for (int d : divisors(n)) {
193  result += phi(n / d) * pow(k, d);
194  }
195  return result / n;
196 }
197 
198 // Function to calculate Euler's totient function (phi)
199 int gpmp::Comb::phi(int n) {
200  int result = n;
201  for (int p = 2; p * p <= n; ++p) {
202  if (n % p == 0) {
203  while (n % p == 0) {
204  n /= p;
205  }
206  result -= result / p;
207  }
208  }
209  if (n > 1) {
210  result -= result / n;
211  }
212  return result;
213 }
214 
215 // Function to generate all necklaces with n beads of k different colors
216 std::vector<std::vector<int>> gpmp::Comb::generateNecklaces(int n, int k) {
217  std::vector<std::vector<int>> necklaces;
218  std::vector<int> necklace(n, 0);
219  gen_necklaces(necklace, 0, n, k, necklaces);
220  return necklaces;
221 }
222 
223 // Function to calculate the number of Lyndon words of length n
224 int64_t gpmp::Comb::lyndonWords(int n) {
225  int64_t result = 0;
226  for (int d : divisors(n)) {
227  result += phi(n / d);
228  }
229  return result;
230 }
231 
232 // Function to generate all Lyndon words of length n
233 std::vector<std::string> gpmp::Comb::generateLyndonWords(int n) {
234  std::vector<std::string> lyndonWords;
235  gen_lyndon_words("", n, n, lyndonWords);
236  return lyndonWords;
237 }
238 
239 // Helper function for generating partitions recursively
241  int max,
242  std::vector<int> &partition,
243  std::vector<std::vector<int>> &result) {
244  if (n == 0) {
245  result.push_back(partition);
246  return;
247  }
248 
249  for (int i = std::min(n, max); i >= 1; --i) {
250  partition.push_back(i);
251  generatePartitions(n - i, i, partition, result);
252  partition.pop_back();
253  }
254 }
255 
256 // Helper function for generating partitions with distinct parts recursively
258  int max,
259  std::vector<int> &partition,
260  std::vector<std::vector<int>> &result) {
261  if (n == 0) {
262  result.push_back(partition);
263  return;
264  }
265 
266  for (int i = std::min(n, max); i >= 1; --i) {
267  if (std::find(partition.begin(), partition.end(), i) ==
268  partition.end()) {
269  partition.push_back(i);
270  gen_partition_distinct(n - i, i, partition, result);
271  partition.pop_back();
272  }
273  }
274 }
275 
276 // Helper function for generating compositions recursively
278  int max,
279  std::vector<int> &composition,
280  std::vector<std::vector<int>> &result) {
281  if (n == 0) {
282  result.push_back(composition);
283  return;
284  }
285 
286  for (int i = std::min(n, max); i >= 1; --i) {
287  composition.push_back(i);
288  gen_compositions(n - i, i, composition, result);
289  composition.pop_back();
290  }
291 }
292 
293 // Helper function for generating all Dyck words recursively
294 void gpmp::Comb::gen_dyck_words(std::string prefix,
295  int open,
296  int close,
297  std::vector<std::string> &dyck_words) {
298  if (open == 0 && close == 0) {
299  dyck_words.push_back(prefix);
300  return;
301  }
302  if (open > 0) {
303  gen_dyck_words(prefix + "(", open - 1, close, dyck_words);
304  }
305  if (close > open) {
306  gen_dyck_words(prefix + ")", open, close - 1, dyck_words);
307  }
308 }
309 
310 // Helper function for generating all necklaces recursively
311 void gpmp::Comb::gen_necklaces(std::vector<int> &necklace,
312  int index,
313  int n,
314  int k,
315  std::vector<std::vector<int>> &necklaces) {
316  if (index == n) {
317  necklaces.push_back(necklace);
318  return;
319  }
320 
321  for (int color = 1; color <= k; ++color) {
322  necklace[index] = color;
323  gen_necklaces(necklace, index + 1, n, k, necklaces);
324  }
325 }
326 
327 // Helper function for generating all Lyndon words recursively
328 void gpmp::Comb::gen_lyndon_words(std::string prefix,
329  int n,
330  int max,
331  std::vector<std::string> &lyndonWords) {
332  if (static_cast<int>(prefix.size()) == n) {
333  lyndonWords.push_back(prefix);
334  return;
335  }
336 
337  for (char c = (prefix.empty() ? 'a' : prefix.back() + 1);
338  c <= 'z' && static_cast<int>(prefix.size()) < n;
339  ++c) {
340  if (n % (static_cast<int>(prefix.size()) + 1) == 0) {
341  gen_lyndon_words(prefix + c, n, prefix.size() + 1, lyndonWords);
342  } else {
343  gen_lyndon_words(prefix + c, n, max, lyndonWords);
344  }
345  }
346 }
347 
348 // Function to compute all divisors of a positive integer
349 std::vector<int> gpmp::Comb::divisors(int n) {
350  std::vector<int> divs;
351  for (int i = 1; i * i <= n; ++i) {
352  if (n % i == 0) {
353  divs.push_back(i);
354  if (i != n / i) {
355  divs.push_back(n / i);
356  }
357  }
358  }
359  return divs;
360 }
void gen_dyck_words(std::string prefix, int open, int close, std::vector< std::string > &dyck_words)
Generate Dyck words.
Definition: comb.cpp:294
int64_t subfactorial(int n)
Calculate the subfactorial (!n) or derangement of n.
Definition: comb.cpp:131
int phi(int n)
Calculate Euler's totient function (φ)
Definition: comb.cpp:199
int64_t necklaces(int n, int k)
Calculate the number of necklaces of length n with k colors.
Definition: comb.cpp:188
void gen_compositions(int n, int max, std::vector< int > &composition, std::vector< std::vector< int >> &result)
Generate compositions of a positive integer.
Definition: comb.cpp:277
void gen_partition_distinct(int n, int max, std::vector< int > &partition, std::vector< std::vector< int >> &result)
Generate distinct partitions of a positive integer.
Definition: comb.cpp:257
void generatePartitions(int n, int max, std::vector< int > &partition, std::vector< std::vector< int >> &result)
Generate partitions of a positive integer.
Definition: comb.cpp:240
int64_t lyndonWords(int n)
Calculate the number of Lyndon words of length n.
Definition: comb.cpp:224
std::vector< int64_t > bell_num(int n)
Calculate the Bell numbers.
Definition: comb.cpp:99
int64_t dyck_words(int n)
Calculate the number of Dyck words of length 2n.
Definition: comb.cpp:175
int64_t binom_coeff(int n, int r)
Calculate the binomial coefficient (n choose r)
Definition: comb.cpp:65
void gen_necklaces(std::vector< int > &necklace, int index, int n, int k, std::vector< std::vector< int >> &necklaces)
Generate necklaces.
Definition: comb.cpp:311
int64_t factorial(int n)
Calculate the factorial of a number.
Definition: comb.cpp:80
std::vector< std::vector< int > > partitions(int n)
Generate all partitions of a positive integer n.
Definition: comb.cpp:91
std::vector< int > gray_code(int n)
Generate the Gray code sequence of length n.
Definition: comb.cpp:122
std::vector< std::vector< int > > partition_distinct(int n)
Generate all distinct partitions of a positive integer n.
Definition: comb.cpp:159
std::vector< int > divisors(int n)
Calculate the divisors of a positive integer.
Definition: comb.cpp:349
int64_t permutation(int n, int r)
Calculate the number of permutations (nPr)
Definition: comb.cpp:41
int64_t stirling_num(int n, int k)
Calculate the Stirling number of the second kind.
Definition: comb.cpp:111
int64_t combination(int n, int r)
Calculate the number of combinations (nCr)
Definition: comb.cpp:52
std::vector< std::vector< int > > compositions(int n)
Generate all compositions of a positive integer n.
Definition: comb.cpp:167
void gen_lyndon_words(std::string prefix, int n, int max, std::vector< std::string > &lyndonWords)
Generate Lyndon words.
Definition: comb.cpp:328
int64_t subsequences(int n, int k)
Calculate the number of subsequences of length k from n items.
Definition: comb.cpp:141
std::vector< std::string > generateLyndonWords(int n)
Generate all Lyndon words of length n.
Definition: comb.cpp:233
std::vector< std::string > gen_dyck_words_until(int n)
Generate Dyck words up to length n.
Definition: comb.cpp:180
std::vector< std::vector< int > > generateNecklaces(int n, int k)
Generate all necklaces of length n with k colors.
Definition: comb.cpp:216