I have such part of the code:
for (auto i = 0; i < WINDOW_SIZE; ++i) {
if (current == goal) break;
if (came_from.find(current) == came_from.end())
break;
next_best = came_from[current];
if (space_map[i].find(next_best) != space_map[i + 1].end()) {
auto search1 = space_map[i].find(current);
... ... ... ... ... ...
Where
1) space_map: std::vector<std::unordered_map<Node, Agent*>> (where Node and Agent are my onw classes.)
2) came_from: std::unordered_map<Node, Node>
3) current, goal: Node
4) WINDOW_SIZE: define, which equal 8
There is listing of hash_map specialization for the unordered_map, and Node:
namespace std {
template <>
struct hash<Node> {
std::size_t operator()(const Node &n) const {
std::size_t const h1 = std::hash<std::size_t>{}(n.x);
std::size_t const h2 = std::hash<std::size_t>{}(n.y);
return (h1 ^ (h2 << 1));
}
};
} // namespace std
typedef struct Node {
uint x, y, f_score, g_score;
bool operator==(const Node &n) const { return (x == n.x && y == n.y); }
bool operator!=(const Node &n) const { return (x != n.x || y != n.y); }
friend std::ostream &operator<<(std::ostream &out, const Node &n) {
out << '[' << n.x << ", " << n.y << ']';
return (out);
}
} Node;
When I'm starting the Debuging, I receive such error:
I considered the case with WINDOW_SIZE - 1, probably it was an overflow error. But not sill the same error.
I figgured out that an error occurs here:
if (space_map[i].find(next_best) != space_map[i + 1].end())
Also I have such notification, that I have a "C26451: Arithmetic overflow" with "+" here space_map[i + 1].end() [i + 1]. Probably it will helpful.
Could someone explain me, where am I wrong?
You didn't give a Minimal, complete verifiable example
But space_map[i].find(next_best) != space_map[i + 1].end()
Is not possible. std::unordered_map::find() returns an iterator. And:
Return value
Iterator to an element with key equivalent to key. If no such element is found, past-the-end (see end()) iterator is returned.
But the iterator of space_map[i] is different from that of space_map[i+1]. You cannot compare two different iterators
So it either should be
space_map[i].find(next_best) != space_map[i].end()
or
space_map[i+1].find(next_best) != space_map[i+1].end()
You don't show enough code, so I have to make assumptions here. I would use a a local reference, to prevent making this mistake.
for (auto spMpCurrent = std::cbegin(space_map);
spMpCurrent != std::next(std::cbegin(space_map), WINDOW_SIZE);
spMpCurrent++) {
if (current == goal) break;
if (came_from.find(current) == came_from.end()) break;
next_best = came_from[current]; // inline this?
auto spMpNext = std::next(spMpCurrent);
if (spMpNext->find(next_best) != spMpNext->end()) {
auto search1 = spMpCurrent->find(current);
Your design looks very (unnecessarily?) complex. Maybe it can be simplified... but as you don't show enough code, we cannot help you
Related
I am trying to implement A* algorithm (with visualization in Qt). I've got this method:
result_path astar_algorithm::calculate(mapview* m_view)
{
map_view = m_view;
auto closed_set = std::vector<std::shared_ptr<node>>();
auto start_node = std::make_shared<node>(_start);
auto open_set = std::vector<std::shared_ptr<node>>{start_node};
std::map<node, node> came_from;
std::shared_ptr<node> current;
while (!open_set.empty())
{
current = *std::min_element(open_set.begin(), open_set.end());
if (*current == _end)
{
// TODO: Reconstruct a result path!!!
break;
}
open_set.erase(std::find(open_set.begin(), open_set.end(), current));
closed_set.push_back(current);
auto neighbors = get_neighbors(*current);
for (auto& neighbor : neighbors)
{
if (std::find_if(closed_set.begin(), closed_set.end(),
[&](std::shared_ptr<node> const& p) { return *p == neighbor; }) !=
closed_set.end())
continue;
auto tentative_g_score = current->G + 1;
if (std::find_if(open_set.begin(), open_set.end(), [&](std::shared_ptr<node> const& p) {
return *p == neighbor;
}) == open_set.end())
{
neighbor.G = tentative_g_score;
neighbor.H = heuristic_cost_estimate(neighbor.pos, _end);
neighbor.parent = current;
open_set.push_back(std::make_shared<node>(neighbor));
}
else if (tentative_g_score < neighbor.G)
{
neighbor.parent = current;
neighbor.G = tentative_g_score;
}
}
}
auto result = result_path();
while (*current != *start_node)
{
result.path.push_back(current->pos);
current = current->parent;
}
result.path.push_back(start_node.pos);
std::reverse(result.path.begin(), result.path.end());
return result;
}
It works, but I have a few problems:
if (std::find_if(closed_set.begin(), closed_set.end(),
[&](std::shared_ptr<node> const& p) { return *p == neighbor; }) !=
closed_set.end())
continue;
This line checks if a node is present in an std::vector and if so, it continues the loop (then there is a second line similar to this, it just checks if node is not actually present in the vector). I guess the better way would be to store those nodes in a vector and then searching and further adding would be easier (cuz I just have to check if the insert succeeded).
The problem is, afaik, to make this work I have to implement < operator. And so I did. I also made == and !=:
class node
{
public:
node() {}
node(const QPoint& p) : pos(p) {}
bool operator == (const node& o ) const { return pos == o.pos; }
bool operator == (const QPoint& o ) const { return pos == o; }
bool operator != (const node& o) const {return pos != o.pos; }
bool operator <(const node& o ) const { return G + H < o.G + o.H; }
QPoint pos;
std::shared_ptr<node> parent;
int G = 0;
int H = 0;
};
It works perfectly for the earlier search for std::min_element (it searches for a node with the lowest F value (F=G+H)), it uses < operator. But then I tried to use a set, so those two vectors at the beginning of the method were set and when I wanted to just insert or even check if a node is already in a set and then insert I had a problem. Many of those nodes will have the same G+H value, as the maze which I used was kind of simple (i.e. a maze completely without terrains). I checked it under the debugger and the nodes with unique .pos values (QPoint) were not added to the set just like they weren't unique (but if the node had a different G+H value than any node in the set, it would be added). For the vector the same nodes of course work, cuz there are no checks made, I checked everything carefully under the debugger.
I don't know if I am getting this wrong, but I thought it would use a == or != operators but as seen in this answer: link, it actually uses < operator, which in my case would not distinguish between two nodes (cuz the unique part of each node is its position in the grid (node represents a box in a grid, which can represent a maze or smth similar))
So, is there something I am doing wrong or am I actually getting this right, and the inserting (which checks if the element is unique) or checking if the element exists in a set uses < operator and I cannot do anything about it? (cuz I would like to have my < operator with comparing G+H and then I would like the searching/inserting to use the == operator to compare)
This is the example that I wrote (I forgot I have Microsoft C++ Compiler from the command line - cl.exe)
#include <algorithm>
#include <iostream>
#include <memory>
#include <set>
class Point
{
public:
int _x, _y;
Point() : _x(0), _y(0) {}
Point(int x, int y) : _x(x), _y(y) {}
bool operator==(const Point& p) const { return _x == p._x && _y == p._y; }
bool operator!=(const Point& p) const { return _x != p._x && _y != p._y; }
};
class node
{
public:
node() {}
node(const Point& p) : pos(p) {}
bool operator==(const node& o) const { return pos == o.pos; }
bool operator==(const Point& o) const { return pos == o; }
bool operator!=(const node& o) const { return pos != o.pos; }
bool operator<(const node& o) const { return G + H < o.G + o.H; }
Point pos;
std::shared_ptr<node> parent;
int G = 0;
int H = 0;
};
int main()
{
node n1(Point(0, 0));
n1.G = 1;
n1.H = 1;
node n2(Point(1, 1));
n2.G = 2;
n2.H = 2;
node n3(Point(2, 2));
n3.G = 1;
n3.H = 1;
std::set<node> nodes;
nodes.insert(n1);
nodes.insert(n2);
nodes.insert(n3);
auto min = (*std::min_element(nodes.begin(), nodes.end())).pos;
std::cout << min._x << " " << min._y << '\n';
std::cout << nodes.size() << '\n';
}
>main.exe
0 0
2
std::min_element works, but those are 3 unique nodes for me (differet .pos values) so there should be 3 nodes in the set. And that's what I want to achieve
I thought it would use a == or != operators
No, std::set does not use operators == and !=,
std::set uses just one function, the comparison function (the second template argument, which defaults to std::less<T>).
Uniqueness is based on the equivalence relation which is derived from applying the same comparison function twice: !a<b && !b<a.
It seems you don't really need uniqueness, in which case you can use std::multiset instead. It will maintain the order, but will not enforce uniqueness.
std::set<node> nodes;
. . .
auto min = (*std::min_element(nodes.begin(), nodes.end())).pos;
std::min_element is always O(N). Using it on a set defeats the purpose of having a set.
Just get the first element, which will be the smallest (according to the comparison function).
auto min = begin(nodes)->pos;
We have some "iterable" collection of data, for instance: std::vector<Foo> bar.
We wish to process Foo elements from bar until some condition is met in which point we "yield" (think Python) the result of all these processed Foos and wait until the next chunk of parsed information is requested.
So far what we're doing is this:
ParsedChunk foobar( std::vector<Foo> bar, size_t* start_from) {
size_t& i = *start_from;
ParsedChunk result_so_far;
for (;i < bar.size(); i++) {
process_some_foo_and_update_chunk(result_so_far, bar[i]);
if (check_condition(? ? ?) {
return result_so_far;
}
}
}
Any suggestions for doing this better?
As I already pointed out in my comment, this is IMO a very good case for an custom iterator:
The iterator scans through your range, as long as some predicate holds, and
when the predicate isn't true for some element, calls a function with the sub range of elements where the predicate held (plus the one that didn't satisfy the predicate). The result of that function call is then the value you get when you dereference the iterator.
template<typename Fn, typename Predicate, typename Iterator>
struct collector {
using value_type = typename std::result_of<Fn(Iterator, Iterator)>::type;
using pointer = value_type const *;
using reference = value_type const &;
using difference_type = std::ptrdiff_t;
using iterator_category = std::forward_iterator_tag;
value_type cache;
Fn fn;
Predicate predicate;
Iterator pos, from, stop;
collector(Fn&& fn, Predicate&& p, Iterator&& s, Iterator&& e)
: fn(std::forward<Fn>(fn)),
predicate(std::forward<Predicate>(p)),
pos(std::forward<Iterator>(s)),
from(pos),
stop(std::forward<Iterator>(e))
{
next_range();
}
collector & operator++(void) {
next_range();
return *this;
}
reference operator*(void) const {
return cache;
}
void next_range(void) {
from = pos;
if (pos == stop) return;
for (; pos != stop; ++pos) {
if (not predicate(*pos)) {
++pos;
break;
}
}
cache = fn(from, pos);
}
collector end_of_range(void) const {
auto copy = collector{*this};
copy.pos = copy.stop;
copy.from = copy.stop;
return copy;
}
bool operator==(collector const & rhs) const {
return (from == rhs.from) and (pos == rhs.pos) and (stop == rhs.stop);
}
bool operator!=(collector const & rhs) const {
return (from != rhs.from) or (pos != rhs.pos) or (stop != rhs.stop);
}
};
template<typename Fn, typename Predicate, typename Iterator>
auto make_collector_range(Fn&& fn, Predicate&& p, Iterator&& s, Iterator&& e) {
using I = typename std::decay<Iterator>::type;
using P = typename std::decay<Predicate>::type;
using F = typename std::decay<Fn>::type;
using C = collector<F,P,I>;
auto start = C{
std::forward<Fn>(fn), std::forward<Predicate>(p),
std::forward<Iterator>(s), std::forward<Iterator>(e)};
auto stop = start.end_of_range();
return make_pair(std::move(start), std::move(stop));
}
An example usage, calculating the sum of the numbers till 50, but not in one step, but in steps of 15 numbers each:
int main(int, char**) {
vector<int> numbers = vector<int>(50);
generate(begin(numbers), end(numbers),
[i = 0] (void) mutable{
return ++i;
});
copy(begin(numbers), end(numbers), ostream_iterator<int>{cout, " "});
cout << endl;
auto collected = make_collector_range(
[](auto const & from, auto const & to) {
return accumulate(from, to, 0);
},
[](auto const & num) {
return not ((num % 3 == 0) and (num % 5 == 0));
},
begin(numbers), end(numbers));
copy(collected.first, collected.second, ostream_iterator<int>{cout, " "});
cout << endl;
bool passed = accumulate(collected.first, collected.second, 0) == (50*51)/2;
cout << "test " << (passed ? "passed" : "failed") << endl;
return 0;
}
(Live demo here)
(Note: This example uses a fixed "step" width, and predicate and function are unrelated to each other and don't maintain state, but none of this is required by the iterator.)
I hope the intention of the code is clear, if not I can try to provide a more detailed explanation about its workings.
struct node {
int idx; // each node have a unique index
int value; // different nodes can have same value
}
struct node_help {
bool operator()(const node &a, const node &b) const
{
return a.value < b.value;
}
}
std::multiset<node, node_help> Nodes;
So far so good. Now I want to erase a specific node from the multiset. When I use this:
Nodes.erase(node (x, y));
Every node with the value y gets removed but I want only the node to be removed with the value y and index x.
I solved this by doing it manually:
for (std::multiset<node, node_comp>::iterator iter = Nodes.begin(); iter != Nodes.end(); iter++) {
node actual_node = *iter;
if (actual_node.idx == to_delete.idx && actual_node.value == to_delete.value) {
Nodes.erase(iter);
}
return;
}
But this seems to have a bad performance.
I have millions of nodes to I need every speedup I can get. :)
Any ideas?
OK all fine. I've just forgot to take the lines with the log off. So after every change in the set, a log was created and saved directly to the disk. Commenting this out reduced the time from 50 seconds to 0,0x seconds. Fast enough. :)
But ty anyways for all the responses.
struct node_help {
bool operator()(const node &a, const node &b) const
{
return a.value <= b.value; // changes the equality check
}
}
If you have C++11 you can define a lambda as follows and make it a better code. Otherwise you may need to use function object in place of lambda.
std::remove(std::begin(Nodes), std::end(Nodes),
[&to_delete]( struct node & x )
{
return ( x.idx == to_delete.idx &&
x.value == to_delete.value);
}
Please note, to use std::remove() you have to incclude algorithm header file:
#include<algorithm>
You can somehow obtain an iterator to an element which is equal to node (x, y). Then, a erase with an iterator will remove only one element.
That said, with such comparison function, you will have some trouble (in terms of complexity) finding node (x, y) anyway, since nodes with the same y and different x are not efficiently searchable by x.
One solution is to change the comparison function to order by y and then by x, and perhaps use a set instead of a multiset then.
After that, if you want an element with a specific y, you can use something like Nodes.lower_bound (node (-INFINITY, y)) which has complexity O (log n).
how about
std::multiset<node, node_help> Nodes;
Nodes.insert(node{ 1, 10 });
Nodes.insert(node{ 2, 1 });
Nodes.insert(node{ 3, 1 });
Nodes.insert(node{ 4, 100 });
cout << "before\n";
for (const auto& n : Nodes)
{
cout << n.idx << " " << n.value << "\n";
}
auto it = Nodes.find(node{ 0,1 });
while(it != Nodes.end())
{
Nodes.erase(it);
it = Nodes.find(node{ 0,1 });
}
cout << "\nafter\n";
for (const auto& n : Nodes)
{
cout << n.idx << " " << n.value << "\n";
}
You can get a constant factor speedup by only looking at the nodes where y matches
auto [first, last] = Nodes.equal_range(to_delete);
auto it = std::find(first, last, to_delete);
if (it != last) {
Nodes.erase(it);
}
Or without C++17
auto range = Nodes.equal_range(to_delete);
auto it = std::find(range.first, range.second, to_delete);
if (it != range.second) {
Nodes.erase(it);
}
With C++14 you could have Nodes searchable by either node or int
struct node {
int idx; // each node have a unique index
int value; // different nodes can have same value
};
struct node_less {
using is_transparent = void;
bool operator()(const node &a, const node &b) const
{
return std::tie(a.value, a.key) < std::tie(b.value, b.key);
}
bool operator()(int a, const node &b) const
{
return a < b.value;
}
bool operator()(const node &a, int b) const
{
return a.value < b;
}
};
std::set<node, node_less> Nodes;
Nodes.find(node{ x, y }); // single node
Nodes.equal_range(y); // all nodes that match y
I am having trouble writing a function to rotate a circular array. I need to rotate it in place (no temp arrays) and I need to move around as few elements as possible. For background information the class "Quack" is just a queue mixed with a stack. So items can be pushed and popped off of both ends of the circular array. Here is what I have so far:
void Quack::rotate(int r)
{
front = (front + capacity + r) % capacity;
back = (back + capacity + r) % capacity;
}
front and back are ints that act as indexes for the array. r is the amount to rotate. capacity is the max size of the array.
The problem is if the array has "garbage" values in it, I end up rotating those into the array. For example lets say the ACTUAL array of chars is {a, b, c, d, e, f, g}, and front is 5 and back is 3. If I printed the circular array I would see {f, g, a, b, c, d}. Since front is 5 and back is 3, the index 4 is a "garbage" value (it got popped at some point). So my rotate function as it stands has a problem in that the index 4 gets "rotated in". If I rotate the array by 2, the ACTUAL array is still {a, b, c, d, e, f, g}, except now when I print it out since front and back are different I get {a, b, c, d, e, f}. What I want to see is {a, b, c, d, f, g}. My print function just prints from front to back (wrapping around as needed) so I need my rotate function to somehow eliminate the garbage value.
I think I need to move the back elements over so I have consecutive values without garbage in the middle. But I'm not sure how to do it.
So if the array isn't full you need to move some char's around. First you check if r is positive or negative. If it's negative you need to push the back over to be flush with the front for the number r or vise versa. example:
print out: c, b, a, z, f, g rotate 2
a z f g - c b
0 1 2 3 4 5 6
the c (items[5]) is in the front and g (items[3]) is back. If we just add r to both it will print garbage for the empty space. If r is 2, we want to copy c to items[4] and b to items[5]. Then items[0] should be the front and b now items[5] should be the back.
void Quack::rotate(int r)
{
if (nItems < capacity) { //if it's not full
if (r < 0) { // if r is negative
int r2 = r*(-1); //find absolute value
for (int i = 0; i < r2; i++) {
items[(back + 1 - i) % (capacity)] = items[(back - i < 0) ? capacity + back - i : back - i]; //push back the back chars up to the front r times
}
}
else {
for (int i = 0; i < r; i++){
items[(back + 1 + i)% capacity] = items[(front + i) % capacity];
}
}
}
front = ((front + r > 0) ? (front + r)%capacity : front + r + capacity); //if front + r is negative add capacity to it
back = (back + r) % capacity;
}
With a cyclic iterator and std::rotate:
#include <algorithm>
#include <iterator>
template <typename Iterator>
class cyclic_range
{
public:
typedef Iterator iterator;
cyclic_range(iterator first, iterator middle, iterator last)
: m_first(first), m_middle(middle), m_last(last)
{
if(m_middle == m_last) m_middle = m_first;
}
iterator first() const { return m_first; }
iterator middle() const { return m_middle; }
iterator last() const { return m_last; }
private:
iterator m_first;
iterator m_middle;
iterator m_last;
};
template <typename Iterator>
inline cyclic_range<Iterator>
make_cyclic_range(Iterator first, Iterator middle, Iterator last) {
return cyclic_range<Iterator>(first, middle, last);
}
/// A cyclic random access iterator operating on a iterator range [first, last).
/// If an iterator reaches the last (or is at last) position of the range the iterator
/// becomes equal to the first position of the range.
template <
typename RandomAccessIterator,
typename CyclicRange = cyclic_range<RandomAccessIterator> >
class cyclic_iterator
{
public:
typedef RandomAccessIterator inner_iterator;
typedef std::iterator_traits<inner_iterator> inner_iterator_traits;
typedef typename std::random_access_iterator_tag iterator_category;
typedef typename inner_iterator_traits::value_type value_type;
typedef typename inner_iterator_traits::difference_type difference_type;
typedef typename inner_iterator_traits::reference reference;
typedef typename inner_iterator_traits::pointer pointer;
typedef CyclicRange range_type;
public:
cyclic_iterator(inner_iterator pos, range_type range, bool at_end = false)
: m_pos(pos), m_range(range), m_at_end(at_end)
{
if(m_pos == range.last()) {
m_pos = range.first();
}
if(m_range.first() == m_range.last()) m_at_end = true;
}
const range_type& range() const { return m_range; }
/// True if the incremented or decremented iterator is at the middle of
/// the circular range.
bool at_end() const { return m_at_end; }
reference operator * () const noexcept {
return *m_pos;
}
pointer operator -> () const noexcept { return &*m_pos; }
cyclic_iterator& operator ++ () noexcept {
if(++m_pos == m_range.last()) m_pos = m_range.first();
m_at_end = (m_pos == m_range.middle());
return *this;
}
cyclic_iterator operator ++ (int) noexcept {
return ++cyclic_iterator(*this);
}
cyclic_iterator& operator += (difference_type n) noexcept {
if(n) {
if(n < 0) *this -= -n;
else {
n %= (m_range.last() - m_range.first());
difference_type avail = m_range.last() - m_pos;
if(n < avail) m_pos += n;
else {
m_pos = m_range.first();
n -= avail;
m_pos += n;
}
m_at_end = (m_pos == m_range.middle());
}
}
return *this;
}
cyclic_iterator operator + (difference_type n) const noexcept {
return cyclic_iterator(*this) += n;
}
cyclic_iterator& operator -- () noexcept {
if(m_pos == m_range.first()) m_pos = m_range.last();
--m_pos;
m_at_end = (m_pos == m_range.middle());
return *this;
}
cyclic_iterator operator -- (int) noexcept {
return --cyclic_iterator(*this);
}
cyclic_iterator& operator -= (difference_type n) noexcept {
if(n) {
if(n < 0) *this += -n;
else {
n %= (m_range.last() - m_range.first());
difference_type avail = m_pos - m_range.first();
if(avail < n) {
m_pos = m_range.last();
n -= avail;
}
m_pos -= n;
m_at_end = (m_pos == m_range.middle());
}
}
return *this;
}
cyclic_iterator operator - (difference_type n) const noexcept {
return cyclic_iterator(*this) -= n;
}
difference_type operator - (const cyclic_iterator& other) const noexcept {
return index() - other.index();
}
bool operator == (const cyclic_iterator& other) noexcept {
return (index() == other.index());
}
bool operator != (const cyclic_iterator& other) noexcept {
return ! (*this == other);
}
bool operator < (const cyclic_iterator& other) noexcept {
return index < other.index();
}
bool operator <= (const cyclic_iterator& other) noexcept {
return ! (other < this);
}
bool operator > (const cyclic_iterator& other) noexcept {
return (other < this);
}
bool operator >= (const cyclic_iterator& other) noexcept {
return ! (this < other);
}
private:
/// The index of the iterator position.
typedef std::size_t size_type;
size_type index() const noexcept {
size_type n = m_range.last() - m_range.first();
if( ! m_at_end) {
if(m_range.middle() <= m_pos) {
n = m_pos - m_range.middle();
}
else {
n = (m_pos - m_range.first()) + (m_range.last() - m_range.middle());
}
}
return n;
}
private:
inner_iterator m_pos;
range_type m_range;
bool m_at_end;
};
template <typename Iterator>
cyclic_iterator<Iterator> begin(const cyclic_range<Iterator>& range) {
return cyclic_iterator<Iterator>(range.middle(), range);
}
template <typename Iterator>
cyclic_iterator<Iterator> end(const cyclic_range<Iterator>& range) {
return cyclic_iterator<Iterator>(range.middle(), range, true);
}
// Test
// ====
#include <iostream>
#include <vector>
template <typename Iterator>
void print_cyclic_range(Iterator first, Iterator last) {
std::cout << "Cyclic Range: ";
for( ; first != last; ++first) {
std::cout << *first << ' ';
}
std::cout << '\n';
}
template <typename Iterator>
void print_range(Iterator first, Iterator last) {
std::cout << " Range: ";
for( ; first != last; ++first) {
std::cout << *first << ' ';
}
std::cout << '\n';
}
int main()
{
typedef cyclic_iterator<char*> cyclic_iterator;
char v[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g'};
print_range(v, v + sizeof(v));
// The cyclic range from 'f' to (including) 'e'
cyclic_iterator::range_type range(v, v + 5, v + sizeof(v));
// The cyclic iterator pointing to 'f'
cyclic_iterator first = begin(range);
// The cyclic iterator pointing to 'e'
cyclic_iterator last = end(range) - 1;
print_cyclic_range(first, last);
// Rotate the cyclic range from 'f' to (including) 'd', excluding 'e'
std::rotate(first, first + 2, last);
print_range(v, v + sizeof(v));
print_cyclic_range(first, last);
return 0;
}
Giving:
Range: a b c d e f g
Cyclic Range: f g a b c d
Range: c d f g e a b
Cyclic Range: a b c d f g
I had this on an interview question. I'd like to see how StackOverflow would do it.
What would Bjarne Stroustrop think of my way? It's a little wordy, but I don't know how to make it better, unfortunately. I know you guys are gonna laugh at my stupidity.
template <class T>
T mode(T* arr, size_t n)
// If there's a tie, return an arbitrary element tied for 1st
// If the array size is 0, throw an error
{
if (n == 0)
{
throw("Mode of array of size 0 is undefined, bro.");
}
else if (n == 1)
{
return arr[0];
}
else
{
std::pair<T, int> highest(arr[0], 1);
std::map<T, int> S;
S.insert(highest);
for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)
{
if (S.count(*thisPtr) == 0)
{
S.insert(std::pair<T, int> (*thisPtr, 1);
}
else
{
++S[*thisPtr];
if (S[*thisPtr] > highest.second)
{
highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
}
}
}
}
}
You could do this, provided that T implements std::hash:
std::unordered_multiset<T> elems;
std::for_each(arr, arr + size, [&elems](T const & elem) { elems.insert(elem); }
//Now you have elems.count() for each entry
auto max_count = /*Guaranteed minimum value*/
T mode{};
for (auto const & set_elem : elems) {
if (max(elems.count(set_elem), max_count) == max_count)) {
mode = set_elem;
}
}
I think I'd use an std::map to do the counting, then find the item with the largest count:
template <class T>
T mode(T* arr, size_t n) {
std::map<T, size_t> counts;
for (size_t i=0; i<n; i++)
++counts[arr[i]];
return max_element(counts.begin(), counts.end(),
[](std::pair<T, size_t> const &a, std::pair<T, size_t> const &b) {
return a.second < b.second;
})->first;
}
If you expect a really large number of unique items, you might want to use an std::unordered_map instead of a std::map [should reduce expected complexity from O(n log n) to O(N)].
I found the following issues with your code.
Redundant check n == 1
You can remove the block
else if (n == 1)
{
return arr[0];
}
without affecting the outcome.
Declaration of the variables in the for loop:
T* thisPtr(arr + 1), lastPtr(arr+n);`
is equivalent to
T* thisPtr(arr + 10); T lastPtr(arr+n);
That's not what your intention is. The compiler will report an error too. So, move their declaration outside the for loop. Change
for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)
to
T* thisPtr(arr + 1);
T* lastPtr(arr+n);
for ( ; thisPtr != lastPtr; ++thisPtr)
Simplify the contents of the for loop
The lines
if (S.count(*thisPtr) == 0)
{
S.insert(std::pair<T, int> (*thisPtr, 1));
}
can be replaced by
++S[*thisPtr];
which is exactly what you are doing in the following else block.
You can change the contents of the entire for loop to:
++S[*thisPtr];
if (S[*thisPtr] > highest.second)
{
highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
}
You need to return the mode
Add
return highest.first;
before the closing of the else block.