Mercurial > lbo > hg > ccplay
changeset 17:f3324364ceda
clang-format: dynprog
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Mon, 03 Apr 2023 22:31:57 +0200 |
parents | d6798d2acfb3 |
children | c875da0d9fdb |
files | CMakeLists.txt dynprog.cc |
diffstat | 2 files changed, 301 insertions(+), 253 deletions(-) [+] |
line wrap: on
line diff
--- a/CMakeLists.txt Sat Apr 01 19:35:47 2023 +0200 +++ b/CMakeLists.txt Mon Apr 03 22:31:57 2023 +0200 @@ -3,7 +3,7 @@ SET(CMAKE_CXX_COMPILER g++) -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -g -Wall") +SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++23 -g -Wall") ADD_SUBDIRECTORY(lib) LINK_LIBRARIES(fmt)
--- a/dynprog.cc Sat Apr 01 19:35:47 2023 +0200 +++ b/dynprog.cc Mon Apr 03 22:31:57 2023 +0200 @@ -12,356 +12,404 @@ #include <vector> using namespace std; +using namespace std::ranges; using MemoKey = tuple<size_t, size_t, size_t>; class TupleHash : function<size_t(const MemoKey &)> { - public: - size_t operator()(const MemoKey &k) const { - return get<0>(k) ^ get<1>(k) ^ get<2>(k); - } +public: + size_t operator()(const MemoKey &k) const { + return get<0>(k) ^ get<1>(k) ^ get<2>(k); + } }; class PairHash : function<size_t(const MemoKey &)> { - public: - template<typename T, typename U> - size_t operator()(const pair<T, U>& p) const { - return hash<T>{}(p.first) ^ hash<U>{}(p.second); - } +public: + template <typename T, typename U> + size_t operator()(const pair<T, U> &p) const { + return hash<T>{}(p.first) ^ hash<U>{}(p.second); + } }; template <typename T> size_t lcss_inner( - const vector<T> &a, const vector<T> &b, size_t i, size_t j, size_t length, - size_t &longest, vector<T> &longest_seq, - unordered_map<tuple<size_t, size_t, size_t>, size_t, TupleHash> &cache) { - if (i >= a.size() || j >= b.size()) { - return length; - } + const vector<T> &a, const vector<T> &b, size_t i, size_t j, size_t length, + size_t &longest, vector<T> &longest_seq, + unordered_map<tuple<size_t, size_t, size_t>, size_t, TupleHash> &cache) { + if (i >= a.size() || j >= b.size()) { + return length; + } - const auto key = make_tuple(i, j, length); - if (cache.contains(key)) { - return get<1>(*cache.find(key)); - } + const auto key = make_tuple(i, j, length); + if (cache.contains(key)) { + return get<1>(*cache.find(key)); + } - if (a.at(i) == b.at(j)) { - // Current (i, j) is member of a common subsequence. - longest = max(longest, length + 1); - size_t remaining_longest = - lcss_inner(a, b, i + 1, j + 1, length + 1, longest, longest_seq, cache); - if (remaining_longest == longest) { - // This element is part of the longest common subsequence. - longest_seq.at(length) = a.at(i); - } - cache.insert(make_pair(key, remaining_longest)); - return remaining_longest; - } else { - // Skip element from a. - size_t remaining_longest_skip_a = - lcss_inner(a, b, i + 1, j, length, longest, longest_seq, cache); - // Skip element from b. - size_t remaining_longest_skip_b = - lcss_inner(a, b, i, j + 1, length, longest, longest_seq, cache); - size_t result = max(remaining_longest_skip_a, remaining_longest_skip_b); - cache.insert(make_pair(key, result)); - return result; + if (a.at(i) == b.at(j)) { + // Current (i, j) is member of a common subsequence. + longest = max(longest, length + 1); + size_t remaining_longest = + lcss_inner(a, b, i + 1, j + 1, length + 1, longest, longest_seq, cache); + if (remaining_longest == longest) { + // This element is part of the longest common subsequence. + longest_seq.at(length) = a.at(i); } + cache.insert(make_pair(key, remaining_longest)); + return remaining_longest; + } else { + // Skip element from a. + size_t remaining_longest_skip_a = + lcss_inner(a, b, i + 1, j, length, longest, longest_seq, cache); + // Skip element from b. + size_t remaining_longest_skip_b = + lcss_inner(a, b, i, j + 1, length, longest, longest_seq, cache); + size_t result = max(remaining_longest_skip_a, remaining_longest_skip_b); + cache.insert(make_pair(key, result)); + return result; + } } template <typename T> vector<T> lcss(const vector<T> &a, const vector<T> &b) { - size_t longest = 0; - vector<T> longest_seq(min(a.size(), b.size())); - unordered_map<tuple<size_t, size_t, size_t>, size_t, TupleHash> cache(100); - size_t longest_r = lcss_inner(a, b, 0, 0, 0, longest, longest_seq, cache); + size_t longest = 0; + vector<T> longest_seq(min(a.size(), b.size())); + unordered_map<tuple<size_t, size_t, size_t>, size_t, TupleHash> cache(100); + size_t longest_r = lcss_inner(a, b, 0, 0, 0, longest, longest_seq, cache); - fmt::print("Length: {} / {}\n", longest, longest_r); + fmt::print("Length: {} / {}\n", longest, longest_r); - return longest_seq; + return longest_seq; } // g++ only - template <typename T, template <typename> typename C> +template <typename T, template <typename> typename C> void evaluate_lcss(const C<T> &a, const C<T> &b) - requires ranges::range<C<T>> + requires ranges::range<C<T>> { - vector<T> va(a.begin(), a.end()); - vector<T> vb(b.begin(), b.end()); - auto result = timeit_f(function([&va, &vb]() { return lcss(va, vb); })); + vector<T> va(a.begin(), a.end()); + vector<T> vb(b.begin(), b.end()); + auto result = timeit_f(function([&va, &vb]() { return lcss(va, vb); })); - fmt::print("{}\n", va); - fmt::print("{}\n", vb); - fmt::print("{}\n", result); + fmt::print("{}\n", va); + fmt::print("{}\n", vb); + fmt::print("{}\n", result); } template <typename T> size_t longest_increasing_subsequence(const vector<T> &a, size_t i, size_t prev, - size_t length, size_t &longest, - vector<size_t> &seqixs) { - longest = max(length, longest); - if (i >= a.size()) - return length; + size_t length, size_t &longest, + vector<size_t> &seqixs) { + longest = max(length, longest); + if (i >= a.size()) + return length; - size_t length_with_current = - a.at(i) < a.at(prev) ? length - : longest_increasing_subsequence( - a, i + 1, i, length + 1, longest, seqixs); - size_t length_without_current = - longest_increasing_subsequence(a, i + 1, prev, length, longest, seqixs); - size_t result = max(length_with_current, length_without_current); + size_t length_with_current = + a.at(i) < a.at(prev) ? length + : longest_increasing_subsequence( + a, i + 1, i, length + 1, longest, seqixs); + size_t length_without_current = + longest_increasing_subsequence(a, i + 1, prev, length, longest, seqixs); + size_t result = max(length_with_current, length_without_current); - if (result == length_with_current && result == longest) { - seqixs.at(length) = i; - } - return result; + if (result == length_with_current && result == longest) { + seqixs.at(length) = i; + } + return result; } // Bug: if first element is 0 or negative, may repeat last index in seqixs template <typename T> T max_sum_incr_subseq(const vector<T> &a, size_t i, size_t prev, size_t length, - T sum, T &highest_sum, vector<size_t> &seqixs) { - highest_sum = max(sum, highest_sum); - if (i >= a.size()) - return sum; + T sum, T &highest_sum, vector<size_t> &seqixs) { + highest_sum = max(sum, highest_sum); + if (i >= a.size()) + return sum; - T sum_with_this = - a.at(i) < a.at(prev) - ? sum - : max_sum_incr_subseq(a, i + 1, i, length + 1, sum + a.at(i), - highest_sum, seqixs); - T sum_without_this = - max_sum_incr_subseq(a, i + 1, prev, length, sum, highest_sum, seqixs); - T result = max(sum_with_this, sum_without_this); + T sum_with_this = + a.at(i) < a.at(prev) + ? sum + : max_sum_incr_subseq(a, i + 1, i, length + 1, sum + a.at(i), + highest_sum, seqixs); + T sum_without_this = + max_sum_incr_subseq(a, i + 1, prev, length, sum, highest_sum, seqixs); + T result = max(sum_with_this, sum_without_this); - if (result == sum_with_this && sum_with_this > sum_without_this && - sum_with_this == highest_sum) { - seqixs.at(length) = i; - } + if (result == sum_with_this && sum_with_this > sum_without_this && + sum_with_this == highest_sum) { + seqixs.at(length) = i; + } - return result; + return result; } - template <typename T, template <typename> typename C> +template <typename T, template <typename> typename C> void evaluate_liss(const C<T> &v) - requires ranges::range<C<T>> + requires ranges::range<C<T>> { - vector<T> a(v.begin(), v.end()); - size_t longest = 0; - vector<size_t> seqixs(a.size()); - size_t result = timeit_f([&a, &longest, &seqixs]() -> size_t { - return longest_increasing_subsequence( - a, 0, 0, 0, longest, seqixs); - }, - "longest incr. subsequence"); + vector<T> a(v.begin(), v.end()); + size_t longest = 0; + vector<size_t> seqixs(a.size()); + size_t result = timeit_f( + [&a, &longest, &seqixs]() -> size_t { + return longest_increasing_subsequence(a, 0, 0, 0, longest, seqixs); + }, + "longest incr. subsequence"); - fmt::print("Longest incr. subsequence has length {}/{} and has indices: {}\n", - longest, result, seqixs); + fmt::print("Longest incr. subsequence has length {}/{} and has indices: {}\n", + longest, result, seqixs); - vector<T> seq(longest); - for (size_t i = 0; i < seq.size(); i++) - seq.at(i) = a.at(seqixs.at(i)); - fmt::print("Longest incr. subsequence is: {}\n", seq); + vector<T> seq(longest); + for (size_t i = 0; i < seq.size(); i++) + seq.at(i) = a.at(seqixs.at(i)); + fmt::print("Longest incr. subsequence is: {}\n", seq); } - template <typename T, template <typename> typename C> +template <typename T, template <typename> typename C> void evaluate_msis(const C<T> &v) - requires ranges::range<C<T>> + requires ranges::range<C<T>> { - vector<T> a(v.begin(), v.end()); - T highest = 0; - vector<size_t> seqixs(a.size()); - size_t result = - timeit_f([&a, &highest, &seqixs]() -> size_t { - return max_sum_incr_subseq(a, 0, 0, 0, static_cast<T>(0), - highest, seqixs); - }, - "max sum incr. subsequence"); + vector<T> a(v.begin(), v.end()); + T highest = 0; + vector<size_t> seqixs(a.size()); + size_t result = timeit_f( + [&a, &highest, &seqixs]() -> size_t { + return max_sum_incr_subseq(a, 0, 0, 0, static_cast<T>(0), highest, + seqixs); + }, + "max sum incr. subsequence"); - fmt::print( - "Max incr sum subseq subsequence has sum {}/{} and has indices: {}\n", - highest, result, seqixs); + fmt::print( + "Max incr sum subseq subsequence has sum {}/{} and has indices: {}\n", + highest, result, seqixs); - vector<T> seq(seqixs.size()); - for (size_t i = 0; i < seq.size(); i++) { - if (i > 0 && seqixs.at(i) == 0) - break; - seq.at(i) = a.at(seqixs.at(i)); - } - fmt::print("Max sum incr. subsequence is: {}\n", seq); + vector<T> seq(seqixs.size()); + for (size_t i = 0; i < seq.size(); i++) { + if (i > 0 && seqixs.at(i) == 0) + break; + seq.at(i) = a.at(seqixs.at(i)); + } + fmt::print("Max sum incr. subsequence is: {}\n", seq); } -template<typename T> -string digits2string(const vector<T>& v) { - ostringstream s; - for (const T& e : v) { - s << e; - } - return s.str(); +template <typename T> string digits2string(const vector<T> &v) { + ostringstream s; + for (const T &e : v) { + s << e; + } + return s.str(); } -void keypad(const unordered_map<int, vector<int>>& adj, vector<int> seq, int n) { - if (n <= 0) { - cout << digits2string(seq) << " "; +void keypad(const unordered_map<int, vector<int>> &adj, vector<int> seq, + int n) { + if (n <= 0) { + cout << digits2string(seq) << " "; + } else { + if (seq.empty()) { + for (int k : views::keys(adj)) { + keypad(adj, vector<int>{k}, n - 1); + } } else { - if (seq.empty()) { - for (int k : views::keys(adj)) { - keypad(adj, vector<int>{k}, n-1); - } - } else { - int last = seq.back(); - const vector<int>& neighbors = adj.at(last); - for (int neigh : neighbors ) { - seq.push_back(neigh); - keypad(adj, seq, n-1); - seq.pop_back(); - } - } + int last = seq.back(); + const vector<int> &neighbors = adj.at(last); + for (int neigh : neighbors) { + seq.push_back(neigh); + keypad(adj, seq, n - 1); + seq.pop_back(); + } } + } } -size_t count_keypad(const unordered_map<int, vector<int>>& adj, int last, int n, unordered_map<pair<int,int>, size_t, PairHash>& memo) { - if (n == 0) - return 1; +size_t count_keypad(const unordered_map<int, vector<int>> &adj, int last, int n, + unordered_map<pair<int, int>, size_t, PairHash> &memo) { + if (n == 0) + return 1; - const auto memopos = memo.find(make_pair(last, n)); - if (memopos != memo.end()) - return memopos->second; + const auto memopos = memo.find(make_pair(last, n)); + if (memopos != memo.end()) + return memopos->second; - if (last < 0) { - const auto ks = views::keys(adj); - auto tf = views::transform(ks, [&adj, &memo, n](int key) -> size_t { return count_keypad(adj, key, n-1, memo); }); - size_t result = reduce(tf.begin(), tf.end()); - memo.insert(make_pair(make_pair(last, n), result)); - return result; - } else { - const auto& neighbors = adj.at(last); - const auto counts = views::transform(neighbors, [&adj, &memo, n](int neigh) -> size_t { return count_keypad(adj, neigh, n-1, memo); }); - size_t result = reduce(counts.begin(), counts.end()); - memo.insert(make_pair(make_pair(last, n), result)); - return result; - } + if (last < 0) { + const auto ks = views::keys(adj); + auto tf = views::transform(ks, [&adj, &memo, n](int key) -> size_t { + return count_keypad(adj, key, n - 1, memo); + }); + size_t result = reduce(tf.begin(), tf.end()); + memo.insert(make_pair(make_pair(last, n), result)); + return result; + } else { + const auto &neighbors = adj.at(last); + const auto counts = + views::transform(neighbors, [&adj, &memo, n](int neigh) -> size_t { + return count_keypad(adj, neigh, n - 1, memo); + }); + size_t result = reduce(counts.begin(), counts.end()); + memo.insert(make_pair(make_pair(last, n), result)); + return result; + } } // https://www.techiedelight.com/count-total-possible-combinations-n-digit-numbers-mobile-keypad/ void evaluate_keypad(size_t n = 2) { - unordered_map<int, vector<int>> adjacency{ - {1, {1, 2, 4}}, - {2, {1, 2, 3, 5}}, - {3, {2, 3, 6}}, - {4, {1, 4, 5, 7}}, - {5, {2, 5, 6, 8, 4}}, - {6, {3, 6, 5, 9}}, - {7, {4, 7, 8}}, - {8, {7, 5, 8, 9, 0}}, - {9, {6, 8, 9}}, - {0, {0, 8}}, - }; - unordered_map<pair<int, int>, size_t, PairHash> memo; + unordered_map<int, vector<int>> adjacency{ + {1, {1, 2, 4}}, {2, {1, 2, 3, 5}}, {3, {2, 3, 6}}, + {4, {1, 4, 5, 7}}, {5, {2, 5, 6, 8, 4}}, {6, {3, 6, 5, 9}}, + {7, {4, 7, 8}}, {8, {7, 5, 8, 9, 0}}, {9, {6, 8, 9}}, + {0, {0, 8}}, + }; + unordered_map<pair<int, int>, size_t, PairHash> memo; - - //keypad(adjacency, vector<int>{}, n); - cout << endl; + // keypad(adjacency, vector<int>{}, n); + cout << endl; - size_t ck = timeit_f(([&adjacency, &memo, n]() { return count_keypad(adjacency, -1, n, memo); }), "count_keypad"); - fmt::print("count = {}\n", ck); + size_t ck = timeit_f(([&adjacency, &memo, n]() { + return count_keypad(adjacency, -1, n, memo); + }), + "count_keypad"); + fmt::print("count = {}\n", ck); - fmt::print("memoize count = {}\n", memo.size()); + fmt::print("memoize count = {}\n", memo.size()); } -bool wildcard_matches(string::const_iterator pbegin, string::const_iterator pend, string::const_iterator sbegin, string::const_iterator send) { - // Pattern finished but string not finished. - if (pbegin == pend && sbegin < send) { - return false; - } - // String ended and pattern not definitely ended. - if (sbegin == send) { - return pbegin == pend; - } - if (*pbegin == *sbegin || *pbegin == '?') { - return wildcard_matches(pbegin+1, pend, sbegin+1, send); - } - if (*pbegin == '*') { - // Keep or consume wildcard. - return wildcard_matches(pbegin, pend, sbegin+1, send) || wildcard_matches(pbegin+1, pend, sbegin+1, send); - } +bool wildcard_matches(string::const_iterator pbegin, + string::const_iterator pend, + string::const_iterator sbegin, + string::const_iterator send) { + // Pattern finished but string not finished. + if (pbegin == pend && sbegin < send) { return false; + } + // String ended and pattern not definitely ended. + if (sbegin == send) { + return pbegin == pend; + } + if (*pbegin == *sbegin || *pbegin == '?') { + return wildcard_matches(pbegin + 1, pend, sbegin + 1, send); + } + if (*pbegin == '*') { + // Keep or consume wildcard. + return wildcard_matches(pbegin, pend, sbegin + 1, send) || + wildcard_matches(pbegin + 1, pend, sbegin + 1, send); + } + return false; } -bool wildcard_matches(const string& pattern, const string& str) { - return wildcard_matches(pattern.begin(), pattern.end(), str.begin(), str.end()); +bool wildcard_matches(const string &pattern, const string &str) { + return wildcard_matches(pattern.begin(), pattern.end(), str.begin(), + str.end()); } void evaluate_wildcard_matches() { - vector<pair<string,string>> tests{ - {"xyxzzxy", "x***y"}, - {"xyxzzxy", "x***x"}, - {"xyxzzxy", "x***x?"}, - {"xyxzzxy", "*"}, - }; + vector<pair<string, string>> tests{ + {"xyxzzxy", "x***y"}, + {"xyxzzxy", "x***x"}, + {"xyxzzxy", "x***x?"}, + {"xyxzzxy", "*"}, + }; - for (const auto& p : tests) { - benchmarkit_f([&]() { wildcard_matches(p.second, p.first); }, "wildcard_matches"); - fmt::print("{} matches {} ? => {}\n", p.first, p.second, wildcard_matches(p.second, p.first)); - } + for (const auto &p : tests) { + benchmarkit_f([&]() { wildcard_matches(p.second, p.first); }, + "wildcard_matches"); + fmt::print("{} matches {} ? => {}\n", p.first, p.second, + wildcard_matches(p.second, p.first)); + } } // TODO: store cutting way int rodcutting_prod_max(int length) { - if (length <= 1) return 1; - int max_len = 0; - for (int this_part = length; this_part > 0; this_part--) { - max_len = max(max_len, this_part * rodcutting_prod_max(length-this_part)); - } - return max_len; + if (length <= 1) + return 1; + int max_len = 0; + for (int this_part = length; this_part > 0; this_part--) { + max_len = max(max_len, this_part * rodcutting_prod_max(length - this_part)); + } + return max_len; } // https://www.techiedelight.com/coin-change-problem-find-total-number-ways-get-denomination-coins/ -int coin_change_problem(const vector<int>& coins, int amount_left, vector<int>& current, vector<vector<int>>& possibilities) { - if (amount_left == 0 && !current.empty()) { - possibilities.push_back(current); - return 1; - } else if (amount_left > 0) { - int possible_count = 0; - for (int coin : coins) { - if (coin > amount_left) continue; - if (!current.empty() && coin < *(current.end()-1)) continue; - current.push_back(coin); - possible_count += coin_change_problem(coins, amount_left-coin, current, possibilities); - current.pop_back(); - } - return possible_count; +int coin_change_problem(const vector<int> &coins, int amount_left, + vector<int> ¤t, + vector<vector<int>> &possibilities) { + if (amount_left == 0 && !current.empty()) { + possibilities.push_back(current); + return 1; + } else if (amount_left > 0) { + int possible_count = 0; + for (int coin : coins) { + if (coin > amount_left) + continue; + if (!current.empty() && coin < *(current.end() - 1)) + continue; + current.push_back(coin); + possible_count += coin_change_problem(coins, amount_left - coin, current, + possibilities); + current.pop_back(); } - return 0; + return possible_count; + } + return 0; } void evaluate_coin_change() { - vector<int> coins{1, 3, 5, 7}; - int amount = 8; - vector<int> current{}; - vector<vector<int>> possibilities{}; + vector<int> coins{1, 3, 5, 7}; + int amount = 8; + vector<int> current{}; + vector<vector<int>> possibilities{}; + + int N = timeit_f([&]() -> int { + return coin_change_problem(coins, amount, current, possibilities); + }); + + fmt::print("{}: {}\n", N, possibilities); +} + +template <typename R, typename RT> + requires range<R> && is_same_v<range_value_t<R>, RT> bool +three_partition_inner(const R &r, size_t ix, RT a, RT b, RT c) { + if (r.begin() + ix == r.end()) { + return (a == b) && (b == c); + } + using It = decltype(std::ranges::begin(r)); - int N = timeit_f([&]() -> int {return coin_change_problem(coins, amount, current, possibilities); }); + It beg = r.begin() + ix; + RT val = *beg; + + // Check three cases. + return three_partition_inner(r, ix + 1, a + val, b, c) || + three_partition_inner(r, ix + 1, a, b + val, c) || + three_partition_inner(r, ix + 1, a, b, c + val); +} - fmt::print("{}: {}\n", N, possibilities); +template <typename R> + requires range<R> bool +three_partition(const R &r) { + using T = range_value_t<R>; + return three_partition_inner<R, T>(r, 0, T(0), T(0), T(0)); +} + +int three_partition_main(void) { + vector<int> a{50, 49, 1, 98, 1, 1, 90, 3, 3, 4}; + fmt::print("{}: {}\n", a, timeit_f([&a]() { return three_partition(a); })); + return 0; } int lcss_main(void) { - vector<int> a{1, 6, 2, -1, 3, 55, 2, 4, 23, 5, 6, 7, 8, 9, 10}; - vector<int> b{-3, 1, 5, 7, 2, 4, -1, 3, 6, 7, 9, 11, 10}; + vector<int> a{1, 6, 2, -1, 3, 55, 2, 4, 23, 5, 6, 7, 8, 9, 10}; + vector<int> b{-3, 1, 5, 7, 2, 4, -1, 3, 6, 7, 9, 11, 10}; - evaluate_lcss(a, b); - evaluate_lcss(views::single(11), views::single(11)); - return 0; + evaluate_lcss(a, b); + evaluate_lcss(views::single(11), views::single(11)); + return 0; } int liss_main(void) { - vector<int> a{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11}; - // Expect: 0,2,6,9,13 or ,11 - // or 0,4,6,9,13 - evaluate_liss(a); - return 0; + vector<int> a{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11}; + // Expect: 0,2,6,9,13 or ,11 + // or 0,4,6,9,13 + evaluate_liss(a); + return 0; } int main(void) { - evaluate_coin_change(); - return 0; + three_partition_main(); + return 0; }