#include <cstdint>
#include <cmath>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
#include <random>
#include <deque>
#include <unordered_map>
using namespace std;
inline constexpr auto select_n_elements_a_1 = []<typename E>(const vector<E> &in, vector<E> &out, const uint32_t n)
{
if (in.size() == 0 || n == 0 || n > in.size())
{
return;
}
out.clear();
out.reserve(n);
static auto gen = default_random_engine{random_device{}()};
vector<uint32_t> indexes(in.size());
iota(begin(indexes), end(indexes), 0);
shuffle(indexes.begin(), indexes.end(), gen);
sort(indexes.begin(), indexes.begin() + n);
for (auto it = indexes.begin(); it != indexes.begin() + n; it++)
{
out.push_back(in[*it]);
}
};
inline constexpr auto select_n_elements_a_2 = []<typename E>(const vector<E> &in, vector<E> &out, const uint32_t n)
{
if (in.size() == 0 || n == 0 || n > in.size())
{
return;
}
out.clear();
out.reserve(n);
static auto gen = default_random_engine{random_device{}()};
uint32_t indexes[in.size()];
for (uint32_t i = 0; i < in.size(); i++)
{
indexes[i] = i;
}
const uint32_t limit = in.size() - 1;
for (uint32_t i = 0; i < in.size(); i++)
{
uint32_t j = uniform_int_distribution<uint32_t>{0, limit}(gen);
swap(indexes[i], indexes[j]);
}
sort(indexes, indexes + n);
for (uint32_t i = 0; i < n; i++)
{
out.push_back(in[indexes[i]]);
}
};
template <typename E>
void insert_randomly(const vector<E> &in, deque<uint32_t> &dq, const uint32_t start, const uint32_t end)
{
static auto gen = default_random_engine{random_device{}()};
uniform_int_distribution<size_t> dist(0, 1);
if (start >= end)
{
if (start == end)
{
const size_t i = dist(gen);
switch (i)
{
case 0:
dq.push_front(start);
break;
case 1:
dq.push_back(start);
break;
default:
break;
}
}
return;
}
const size_t i = dist(gen);
const uint32_t mid = (start + end) / 2;
switch (i)
{
case 0:
insert_randomly(in, dq, start, mid);
insert_randomly(in, dq, mid + 1, end);
break;
case 1:
insert_randomly(in, dq, mid + 1, end);
insert_randomly(in, dq, start, mid);
break;
default:
break;
}
}
inline constexpr auto select_n_elements_a_3 = []<typename E>(const vector<E> &in, vector<E> &out, const uint32_t n)
{
if (in.size() == 0 || n == 0 || n > in.size())
{
return;
}
out.clear();
out.reserve(n);
deque<uint32_t> dq;
insert_randomly(in, dq, 0, in.size() - 1);
for (uint32_t i = 0; i < n; i++)
{
out.push_back(in[dq.front()]);
dq.pop_front();
}
};
inline constexpr auto select_n_elements_b_1 = []<typename E>(const vector<E> &in, vector<E> &out, const uint32_t n)
{
if (in.size() == 0 || n == 0 || n > in.size())
{
return;
}
out.clear();
out.reserve(n);
static auto gen = default_random_engine{random_device{}()};
sample(in.begin(), in.end(), back_inserter(out), n, gen);
};
template <typename T>
chrono::duration<double> test_runtime(string func_name, T select_n_elements, const uint32_t n1, const uint32_t n2)
{
vector<int> in(n1);
iota(begin(in), end(in), 0);
vector<int> out = {};
auto start = chrono::system_clock::now();
select_n_elements(in, out, n2);
auto end = chrono::system_clock::now();
cout << func_name << ": Selected " << out.size() << " elements from " << in.size() << " elements. Time taken: " << chrono::duration<double>(end - start).count() << " seconds." << endl;
return end - start;
}
template <typename T>
void test_probabilites_1(string func_name, T select_n_elements)
{
unordered_map<uint32_t, uint32_t> counts;
vector<uint32_t> in(1000);
iota(begin(in), end(in), 0);
for (uint32_t i = 0; i < 1000; i++)
{
vector<uint32_t> out = {};
select_n_elements(in, out, 100);
for (const auto &item : out)
{
counts[item]++;
}
}
vector<pair<uint32_t, uint32_t>> pairs;
for (uint32_t i = 0; i < in.size(); i++)
{
pairs.push_back({i, counts[i]});
}
sort(pairs.begin(), pairs.end(), [=](std::pair<uint32_t, uint32_t> &a, std::pair<uint32_t, uint32_t> &b)
{ return a.second < b.second; });
cout << "Highest probability for " << func_name << ":" << endl;
cout << pairs.back().first << " with " << pairs.back().second << " occurrences." << endl;
cout << "Lowest probability for " << func_name << ":" << endl;
cout << pairs.front().first << " with " << pairs.front().second << " occurrences." << endl;
uint32_t ten_count = 0;
for (const auto &pair : counts)
{
if (pair.second == 100)
{
ten_count++;
}
}
cout << "Number of elements with 100 occurrences: " << ten_count << endl;
cout << endl;
}
template <typename T>
void test_probabilites_2(string func_name, T select_n_elements)
{
unordered_map<uint32_t, uint32_t> counts;
vector<uint32_t> in(1000);
iota(begin(in), end(in), 0);
for (uint32_t i = 0; i < 100000; i++)
{
vector<uint32_t> out = {};
select_n_elements(in, out, 1);
for (const auto &item : out)
{
counts[item]++;
}
}
vector<pair<uint32_t, uint32_t>> pairs;
for (uint32_t i = 0; i < in.size(); i++)
{
pairs.push_back({i, counts[i]});
}
sort(pairs.begin(), pairs.end(), [=](std::pair<uint32_t, uint32_t> &a, std::pair<uint32_t, uint32_t> &b)
{ return a.second < b.second; });
cout << "Highest probability for " << func_name << ":" << endl;
cout << pairs.back().first << " with " << pairs.back().second << " occurrences." << endl;
cout << "Lowest probability for " << func_name << ":" << endl;
cout << pairs.front().first << " with " << pairs.front().second << " occurrences." << endl;
uint32_t count = 0;
for (const auto &pair : counts)
{
if (pair.second == 100)
{
count++;
}
}
cout << "Number of elements with 100 occurrences: " << count << endl;
cout << endl;
}
int main()
{
test_probabilites_1("select_n_elements_a_3", select_n_elements_a_3);
test_probabilites_2("select_n_elements_a_3", select_n_elements_a_3);
// test_runtime("select_n_elements_a_1", select_n_elements_a_1, 1000000, 900000);
// test_runtime("select_n_elements_a_2", select_n_elements_a_2, 1000000, 900000);
// test_runtime("select_n_elements_a_3", select_n_elements_a_3, 1000000, 900000);
// test_runtime("select_n_elements_b_1", select_n_elements_b_1, 1000000, 900000);
return 0;
}