C++ Library Extensions 2022.12.09
To help learn modern C++ programming
012-cross_thread_recursion.cpp
Go to the documentation of this file.
1#include <tpf_ncrnpr.hpp>
3#include <tpf_output.hpp>
4
5#include <future>
6#include <atomic>
7
8// (n, k) = (n-1, k-1) + (n-1, k)
10{
11 using big_int_t = long long;
12
13 static constexpr int factor = 1;
14
15 // "inline static" was introduced to C++17 standard
16 inline static int max_thread_count = factor *
17 std::thread::hardware_concurrency();
18
19 inline static std::atomic<int> thread_count{0};
20
21 static constexpr auto async = std::launch::async;
22 static constexpr auto deferred = std::launch::deferred;
23
24 static big_int_t
26 {
27 if (k == 0 || n == k)
28 {
29 return 1;
30 }
31 else if (k == 1 || k == (n - 1))
32 {
33 return n;
34 }
35 else
36 {
37 // nCk = nCn-k, (n, k) == (n, n-k)
38 if(k > (n-k) ) k = n-k;
39 // (n, k) = (n-1, k-1) + (n-1, k)
40
41 return single_thread_combination_recursion(n-1, k-1) // (n-1, k-1)
42 + single_thread_combination_recursion(n-1, k); // (n-1, k)
43 }
44 }
45
47 static big_int_t
49 {
50 if (k == 0 || n == k)
51 {
52 return 1;
53 }
54 else if (k == 1 || k == (n - 1))
55 {
56 return n;
57 }
58 else
59 {
60 // nCk = nCn-k, (n, k) == (n, n-k)
61 if(k > (n-k) ) k = n-k;
62 // (n, k) = (n-1, k-1) + (n-1, k)
63
64 try
65 {
66 // we want to limit the number of currently running
67 // threads less than max_thread_count
69 {
70 // (n-1, k-1) - calculate in a different new thread simultaneously
71 auto future_ncr_n_1_k_1
72 = std::async(async, cross_thread_combination_recursion, n-1, k-1);
73
74 ++thread_count; // this is wrong
75
76 // (n-1, k) - calculate in the current thread
77 auto current_thread_n_1_k = cross_thread_combination_recursion(n-1, k);
78
79 auto rlt = current_thread_n_1_k + future_ncr_n_1_k_1.get();
80
81 --thread_count; // this is wrong, but I will explain why in future sessions
82
83 return rlt;
84 }
85 else
86 {
87 // if concurrently running threads are too many,
88 // then use current thread only
89 return cross_thread_combination_recursion(n-1, k-1) // (n-1, k-1)
90 + cross_thread_combination_recursion(n-1, k); // (n-1, k)
91 }
92 }
93 catch(const std::exception& e)
94 {
95 std::cerr << e.what() << '\n';
96
97 // this is to suppress warning
98 return {};
99 }
100 }
101 }
102
105 static big_int_t
107 {
108 if (k == 0 || n == k)
109 {
110 return 1;
111 }
112 else if (k == 1 || k == (n - 1))
113 {
114 return n;
115 }
116 else
117 {
118 // nCk = nCn-k, (n, k) == (n, n-k)
119 if(k > (n-k) ) k = n-k;
120 // (n, k) = (n-1, k-1) + (n-1, k)
121
122 try
123 {
124 // we want to limit the number of currently running
125 // threads less than max_thread_count
127 {
128 // (n-1, k-1) - calculate in a different new thread simultaneously
129 auto future_ncr_n_1_k_1
130 = std::async(async, smart_cross_thread_combination_recursion, n-1, k-1);
131
132 ++thread_count; // this is wrong
133
134 // (n-1, k) - calculate in the current thread
135 auto current_thread_n_1_k = smart_cross_thread_combination_recursion(n-1, k);
136
137 auto rlt = current_thread_n_1_k + future_ncr_n_1_k_1.get();
138
139 --thread_count; // this is wrong, but I will explain why in future sessions
140
141 return rlt;
142 }
143 else
144 {
145 // if concurrently running threads are too many,
146 // then use current thread only
147 return single_thread_combination_recursion(n-1, k-1) // (n-1, k-1)
148 + single_thread_combination_recursion(n-1, k); // (n-1, k)
149 }
150 }
151 catch(const std::exception& e)
152 {
153 std::cerr << e.what() << '\n';
154
155 // this is to suppress warning
156 return {};
157 }
158 }
159 }
160};
161
163{
164 using ctr = cross_thread_recursion;
165
166 ctr::big_int_t n = 40;
167 ctr::big_int_t k = 12;
168
169 // https://en.wikipedia.org/wiki/Binomial_coefficient
170 // https://en.wikipedia.org/wiki/Combination
171 // (n, k) == (40, 12) <- binomial coefficient
172
174
175 std::cout << "tpf::ncrnpr::ncr : "
176 << tpf::ncrnpr::ncr(n, k)
177 << ", Elapsed: " << sw.elapsed_time() << std::endl;
178
179 std::cout << "single_thread_combination_recursion : "
180 << ctr::single_thread_combination_recursion(n, k)
181 << ", Elapsed: " << sw.elapsed_time() << std::endl;
182}
183
185{
186 using ctr = cross_thread_recursion;
187
188 ctr::big_int_t n = 40;
189 ctr::big_int_t k = 12;
190
191 // https://en.wikipedia.org/wiki/Binomial_coefficient
192 // https://en.wikipedia.org/wiki/Combination
193 // (n, k) == (40, 12) <- binomial coefficient
194
196
197 std::cout << "smart_cross_thread_combination_recursion : "
198 << ctr::smart_cross_thread_combination_recursion(n, k)
199 << ", Elapsed: " << sw.elapsed_time() << std::endl;
200
201 std::cout << "cross_thread_combination_recursion : "
202 << ctr::cross_thread_combination_recursion(n, k)
203 << ", Elapsed: " << sw.elapsed_time() << std::endl;
204
205 std::cout << "single_thread_combination_recursion : "
206 << ctr::single_thread_combination_recursion(n, k)
207 << ", Elapsed: " << sw.elapsed_time() << std::endl;
208}
209
210int main()
211{
212 std::cout << "========= single thread : loop vs recursion ==== " << std::endl;
214
215 std::cout << "\n\n========= single thread vs. multithread recursion ==== " << std::endl;
216
218}
219
void single_vs_multiple_thread_recursion()
void loop_vs_recursion()
auto & cout
auto & endl
std::string elapsed_time(bool bReset=true, TimeUnit dummy_time=TimeUnit{}) const
enable_if_all_in_list_t< types::type_list_t< Type1, Type2 >, integral_list_t, ReturnType > ncr(Type1 nn, Type2 rr)
Definition: tpf_ncrnpr.hpp:390
static big_int_t smart_cross_thread_combination_recursion(big_int_t n, big_int_t k)
static big_int_t cross_thread_combination_recursion(big_int_t n, big_int_t k)
static std::atomic< int > thread_count
static big_int_t single_thread_combination_recursion(big_int_t n, big_int_t k)
long long big_int_t
Stream output operators << are implemented.