Isn't it quicker to compute the full product in linear time and then divide by each N?
EDIT: bugfix - (O3N) time with zero check:
#include <numeric>
#include <array>
#include <vector>
#include <iostream>
namespace notstd {
template<class T>
auto to_vector(std::initializer_list<T> il) {
return std::vector<T>(il);
}
}
static constexpr struct {
template<class Iter>
void operator()(Iter first, Iter last) const {
if (first != last) {
auto zeros = std::count(first, last, 0);
switch (zeros) {
case 0: {
auto accum = std::accumulate(std::next(first), last,
*first, std::multiplies<>());
std::transform(first, last,
first,
[accum](auto &&v) {
return accum / v;
});
}
break;
case 1: {
auto maybe_multiply = [](auto &&x, auto &&y) { return y == 0 ? x : x * y; };
auto accum = std::accumulate(first, last,
1, maybe_multiply);
std::transform(first, last,
first,
[accum](auto &&v) {
if (v == 0)
return accum;
else
return 0;
});
}
break;
default: {
std::fill(first, last, 0);
} break;
}
}
}
} other_products {};
template<class Container, class Algo>
auto mutate_copy(Container c, Algo algo) {
algo(std::begin(c), std::end(c));
return c;
};
template<class T, class Algo>
auto mutate_copy(std::initializer_list<T> il, Algo &&algo) {
return mutate_copy(notstd::to_vector(il),
std::forward<Algo>(algo));
};
template<class T, std::size_t N, class Algo>
auto mutate_copy(std::array<T, N> const &a, Algo &&algo) {
return mutate_copy(std::vector<T>(a.begin(), a.end()),
std::forward<Algo>(algo));
};
template<class Container, class Algo>
auto &mutate_inplace(Container &c, Algo algo) {
algo(std::begin(c), std::end(c));
return c;
};
template<class Container>
std::ostream &emit(std::ostream &os, Container &&c) {
auto impl = [&os](auto first, auto last) {
using value_type = typename std::iterator_traits<decltype(first)>::value_type;
std::copy(first, last,
std::ostream_iterator<value_type>(os, ", "));
};
impl(std::begin(c), std::end(c));
return os;
}
int main() {
emit(std::cout, mutate_copy({1, 2, 0, 4, 5}, other_products)) << std::endl;
emit(std::cout, mutate_copy({2, 3, 4, 5, 6}, other_products)) << std::endl;
emit(std::cout, mutate_copy({6, 5, 4, 3, 2}, other_products)) << std::endl;
std::array<int, 5> ar{10, 20, 30, 40, 50};
emit(std::cout, mutate_copy(ar, other_products)) << std::endl;
emit(std::cout, mutate_inplace(ar, other_products)) << std::endl;
auto in = std::vector<int> {10, 11, 12, 13, 14};
emit(std::cout, mutate_copy(in, other_products)) << std::endl;
std::vector<int> foo{};
emit(std::cout, mutate_inplace(foo, other_products)) << std::endl;
}
previous code:
I believe this would give O(2N) time.
#include <numeric>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
template<class Iter>
auto other_products(Iter first, Iter last)
{
using type = typename std::iterator_traits<Iter>::value_type;
auto mega_product = std::accumulate(first, last,
type(1), std::multiplies<>());
std::vector<type> result;
result.reserve(std::distance(first, last));
std::transform(first, last,
std::back_inserter(result),
[mega_product](auto&& v)
{
return mega_product / v;
});
return result;
}
//
// convenience specialisation for any container
//
template<class Container>
auto other_products(Container&& c)
{
return other_products(std::begin(c), std::end(c));
}
template<class Value>
auto other_products(std::initializer_list<Value> c)
{
return other_products(std::begin(c), std::end(c));
}
template<class Container>
std::ostream& emit(std::ostream& os, Container&& c)
{
auto impl = [&os](auto first, auto last)
{
using value_type = typename std::iterator_traits<decltype(first)>::value_type;
std::copy(first, last,
std::ostream_iterator<value_type>(os, ", "));
};
impl(std::begin(c), std::end(c));
return os;
}
int main()
{
emit(std::cout, other_products({1, 2, 3, 4, 5})) << std::endl;
emit(std::cout, other_products({2, 3, 4, 5, 6})) << std::endl;
emit(std::cout, other_products({6, 5, 4, 3, 2})) << std::endl;
auto in = std::vector<int> { 10, 11, 12, 13, 14 };
emit(std::cout, other_products(in)) << std::endl;
}
As a further refinement, I had a go are separating the concerns of 'in-place' or 'copy' operations from the actual mutating algorithm.
See what you think:
#include <numeric>
#include <array>
#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
#include <iterator>
#include <initializer_list>
namespace notstd {
template<class T>
auto to_vector(std::initializer_list<T> il)
{
return std::vector<T>(il);
}
}
static constexpr struct
{
template<class Iter>
void operator()(Iter first, Iter last) const
{
if (first != last) {
auto accum = std::accumulate(std::next(first), last,
*first, std::multiplies<>());
std::transform(first, last,
first,
[accum](auto&& v)
{
return accum / v;
});
}
}
} other_products {};
template<class Container, class Algo>
auto mutate_copy(Container c, Algo algo)
{
algo(std::begin(c), std::end(c));
return c;
};
template<class T, class Algo>
auto mutate_copy(std::initializer_list<T> il, Algo&& algo)
{
return mutate_copy(notstd::to_vector(il),
std::forward<Algo>(algo));
};
template<class T, std::size_t N, class Algo>
auto mutate_copy(std::array<T, N> const& a, Algo&& algo)
{
return mutate_copy(std::vector<T>(a.begin(), a.end()),
std::forward<Algo>(algo));
};
template<class Container, class Algo>
auto& mutate_inplace(Container& c, Algo algo)
{
algo(std::begin(c), std::end(c));
return c;
};
template<class Container>
std::ostream& emit(std::ostream& os, Container&& c)
{
auto impl = [&os](auto first, auto last)
{
using value_type = typename std::iterator_traits<decltype(first)>::value_type;
std::copy(first, last,
std::ostream_iterator<value_type>(os, ", "));
};
impl(std::begin(c), std::end(c));
return os;
}
int main()
{
emit(std::cout, mutate_copy({1, 2, 3, 4, 5}, other_products)) << std::endl;
emit(std::cout, mutate_copy({2, 3, 4, 5, 6}, other_products)) << std::endl;
emit(std::cout, mutate_copy({6, 5, 4, 3, 2}, other_products)) << std::endl;
std::array<int, 5> ar { 10, 20, 30 , 40, 50 };
emit(std::cout, mutate_copy(ar, other_products)) << std::endl;
emit(std::cout, mutate_inplace(ar, other_products)) << std::endl;
auto in = std::vector<int> {10, 11, 12, 13, 14};
emit(std::cout, mutate_copy(in, other_products)) << std::endl;
}
1 2 3 4 5
? What happens if your array is2 3 4 5 6
? \$\endgroup\$