Skip to main content

Subset Transforms (Dynamic Programming)

Zeta Transform & its inverse

namespace Algorithm {
template<typename T>
void zetaTransform(std::span<T> f) {
int n = std::countr_zero(f.size());
for (int i = 0; i < n; ++i)
for (int mask = 0; mask < (1 << n); ++mask)
if (mask & (1 << i))
f[mask] += f[mask ^ (1 << i)];
}

/* this is inverse of zetaTransform */
template<typename T>
void mobiusTransform(std::span<T> f) {
int n = std::countr_zero(f.size());
for (int i = 0; i < n; ++i)
for (int mask = 0; mask < (1 << n); ++mask)
if (mask & (1 << i))
f[mask] -= f[mask ^ (1 << i)];
}
}

int main() {
std::vector<int> a = {3,7,5,6,1,2,2,1};
Algorithm::zetaTransform<int>(a);
std::ranges::copy(a, std::ostream_iterator<int>(std::cout, " "));
}

Subset Sum Convolution

Time Complexity: where

namespace Algorithm {
/* unverified */
template<typename T>
std::vector<T> subsetSumConvolution(std::span<T> f, std::span<T> g) {
uint64_t two_power_n = f.size();
int n = std::countr_zero(two_power_n);

std::array<std::vector<T>, 32> f_hat, g_hat, h_hat;
std::ranges::fill(f_hat, std::vector<int>(two_power_n, 0));
std::ranges::fill(g_hat, std::vector<int>(two_power_n, 0));
std::ranges::fill(h_hat, std::vector<int>(two_power_n, 0));

for (int mask = 0; mask < (1 << n); ++mask) {
int set_bit_count = std::popcount((uint64_t) mask);
f_hat[set_bit_count][mask] = f[mask];
g_hat[set_bit_count][mask] = g[mask];
}

for (int k = 0; k <= n; ++k) zetaTransform<T>(f_hat[k]);
for (int k = 0; k <= n; ++k) zetaTransform<T>(g_hat[k]);

for (int i = 0; i <= n; ++i)
for (int j = 0; j <= i; ++j)
for (int mask = 0; mask < (1 << n); ++mask)
h_hat[i][mask] += f_hat[j][mask] * g_hat[i - j][mask];

for (int k = 0; k <= n; ++k) mobiusTransform<T>(h_hat[k]);

std::vector<T> fog(two_power_n);

for (int mask = 0; mask < (1 << n); ++mask)
fog[mask] = h_hat[mask][std::popcount((uint64_t) mask)];

return fog;
}
}