My goal is to overwrite [first, last) in std::map<int, char>
with only 1 amortized O(log N), the rest should be O(1) operations.
Honestly, I want to know how to build a good algorithm with other optimization technique. I am stuck with the flowchart technique and can't squeeze more performance out of this.
The specification:
- The map have initial value. so when you create it i.e.
abcMap<int, char> map{'A'}
, and access isM[0]
it will return'A'
- The stored entry must be canonical i.e.
map == { {1, 'A'}, {3, 'B'} }
is legal. However,map == { {1, 'A'}, {3, 'A'} }
is illegal. Also, if the initial value is 'A' thenmap == { {1, 'A'} }
is illegal. - The stored entry is a checkpoint i.e.
map == { {1, 'A'}, {2, 'A'}, {3, 'B'} }
is illegal. It should bemap == { {1, 'A'}, {3, 'B'} }
.
For example
abcMap<int, char> map{'A'};
map.assign(0, 1, 'B'); // map == { {0, 'B'} }
map[-1] == 'A'
map[0] == 'B'
map[1] == 'B'
The problem:
std::prev
orstd::next
is O(N) operation.std::map::lower_bound
is O(log N) operation. So can't dofirst = map.lower_bound(first)
,last = map.lower_bound(last)
.std::map::insert_or_assign
is O(log N).std::map::erase
log(c.size()) + std::distance(first, last) operation.
What I've tried:
- worse case 2 O(N) operations + 1 O(log N) operation + log(N) + std::distance(first, last) operation.
The interface
#pragma once
template<typename K, typename V>
class abcMap
{
friend void abcMapTest();
V m_valBegin;
std::map<K,V> m_map;
public:
// constructor associates whole range of K with val
abcMap(V const& val);
// Assign value val to interval [keyBegin, keyEnd).
// Overwrite previous values in this interval.
// Conforming to the C++ Standard Library conventions, the interval
// includes keyBegin, but excludes keyEnd.
// If !( keyBegin < keyEnd ), this designates an empty interval,
// and assign must do nothing.
void assign( K const& keyBegin, K const& keyEnd, V const& val );
V const& operator[]( K const& key ) const;
void print();
};
#include "abcMap.inl"
The implementation
#include "abcMap.hpp"
template<typename K, typename V>
abcMap<K,V>::abcMap(V const& val) : m_valBegin(val) {}
template<typename K, typename V>
void abcMap<K,V>::assign( K const& keyBegin, K const& keyEnd, V const& val ) {
using mapitr_t = typename decltype(m_map)::iterator;
mapitr_t eraseIteratorFirst;
if (keyBegin > keyEnd) return;
auto size = m_map.size();
if (size == 0)
{
if (m_valBegin == val) return;
else
{
m_map.insert_or_assign(m_map.end(), keyBegin, val);
return;
}
}
else if (size == 1)
{
auto begin = m_map.begin();
if (begin->first == keyBegin)
{
if (m_valBegin == val) return;
else
{
m_map.insert_or_assign(m_map.begin(), keyBegin, val);
return;
}
}
else if (begin->first > keyBegin)
{
if (m_valBegin == val)
{
eraseIteratorFirst = m_map.begin();
// goto erase
}
else
{
eraseIteratorFirst = m_map.insert_or_assign(begin, keyBegin, val);
++eraseIteratorFirst;
// goto erase
}
}
else
{
if (begin->second == val) return;
else
{
m_map.insert_or_assign(m_map.end(), keyBegin, val);
return;
}
}
}
else
{
auto begin = m_map.begin();
if (begin->first >= keyBegin)
{
if (m_valBegin == val)
{
eraseIteratorFirst = m_map.begin();
// goto erase
}
else
{
eraseIteratorFirst = m_map.insert_or_assign(begin, keyBegin, val);
++eraseIteratorFirst;
// goto erase
}
}
else
{
auto last = std::prev(m_map.end());
if (last->first == keyBegin)
{
auto beforeLast = std::prev(last);
if (beforeLast->second == val)
{
m_map.erase(last);
return;
}
else
{
m_map.insert_or_assign(last, keyBegin, val);
return;
}
}
else if (last->first < keyBegin)
{
if (last->second == val) return;
else
{
m_map.insert_or_assign(last, keyBegin, val);
return;
}
}
else
{
auto itBegin = m_map.lower_bound(keyBegin);
if (std::prev(itBegin)->second == val)
{
eraseIteratorFirst = itBegin;
// goto erase
}
else
{
eraseIteratorFirst = m_map.insert_or_assign(itBegin, keyBegin, val);
++eraseIteratorFirst;
// goto erase
}
}
}
}
// erase
auto last = std::prev(m_map.end());
if (last->first == keyEnd)
{
m_map.erase(eraseIteratorFirst, last);
return;
}
else if (last->first < keyEnd)
{
m_map.erase(eraseIteratorFirst, m_map.end());
return;
}
else
{
m_map.erase(eraseIteratorFirst, m_map.lower_bound(keyEnd));
return;
}
}
template<typename K, typename V>
V const& abcMap<K,V>::operator[]( K const& key ) const {
auto it=m_map.upper_bound(key);
if(it==m_map.begin()) {
return m_valBegin;
} else {
return (--it)->second;
}
}
template<typename K, typename V>
void abcMap<K,V>::print()
{
std::cout<< "{";
for (auto&&[key,val] : m_map)
{
std::cout << " {" << key << ",\'" << val << "\'},";
}
std::cout << "}\n";
}
The test
#include "abcMap.hpp"
#include <iostream>
void abcMapTest()
{
const auto start = std::chrono::steady_clock::now();
using map_t = abcMap<int, char>;
std::map<int, char> expected_map;
// MARK: M
// std::cout << "MARK: M" << std::endl;
map_t M{'A'};
assert(M[-2] == 'A');
assert(M[-1] == 'A');
assert(M[0] == 'A');
M.assign(1, 3, 'B');
// M.print(); // [1:'B'];
expected_map = { {1,'B'} };
assert(M.m_map == expected_map);
assert(M[1] == 'B');
assert(M[2] == 'B');
M.assign(3, 5, 'A');
// M.print(); // [1:'B'][3:'A'];
expected_map = { {1,'B'}, {3,'A'} };
assert(M.m_map == expected_map);
M.assign(5, 7, 'A');
// M.print(); // [1:'B'][3:'A'];
expected_map = { {1,'B'}, {3,'A'} };
assert(M.m_map == expected_map);
assert(M[3] == 'A');
assert(M[4] == 'A');
assert(M[5] == 'A');
M.assign(2, 4, 'B');
// M.print(); // [1:'B']
expected_map = { {1,'B'} };
assert(M.m_map == expected_map);
M.assign(-2, 1, 'C');
// M.print(); // [-2:'C'][1,'B']
expected_map = { {-2,'C'}, {1,'B'} };
assert(M.m_map == expected_map);
// MARK: M1
// std::cout << "MARK: M1" << std::endl;
map_t M1{'A'};
M1.assign(0, 0, 'A');
// M1.print();
expected_map = {};
assert(M1.m_map == expected_map);
// MARK: M2
// std::cout << "MARK: M2" << std::endl;
map_t M2{'A'};
M2.assign(3, 5, 'B');
// M2.print(); // [3:B]
expected_map = { {3,'B'} };
assert(M2.m_map == expected_map);
M2.assign(0, 7, 'A');
// M2.print(); //
expected_map = {};
assert(M2.m_map == expected_map);
// MARK: M3
// std::cout << "MARK: M3" << std::endl;
map_t M3{ 'a' };
M3.assign(3, 5, 'b');
// M3.print(); // [3:b]
expected_map = { {3, 'b'} };
assert(M3.m_map == expected_map);
M3.assign(2, 3, 'c');
// M3.print(); // [2:c][3:b]
expected_map = { {2, 'c'}, {3, 'b'} };
assert(M3.m_map == expected_map);
M3.assign(2, 3, 'd');
// M3.print(); // [2:d][3:b]
expected_map = { {2, 'd'}, {3, 'b'} };
assert(M3.m_map == expected_map);
M3.assign(2, 4, 'e');
// M3.print(); // [2:e]
expected_map = { {2, 'e'} };
assert(M3.m_map == expected_map);
M3.assign(4, 18, 'f');
// M3.print(); // [2:e][4:f]
expected_map = { {2, 'e'}, {4, 'f'} };
assert(M3.m_map == expected_map);
M3.assign(2, 8, 'g');
// M3.print(); // [2:g]
expected_map = { {2, 'g'} };
assert(M3.m_map == expected_map);
M3.assign(0, 10, 'a');
// M3.print(); //
expected_map = {};
assert(M3.m_map == expected_map);
M3.assign(0, 20, 'a');
// M3.print(); //
expected_map = {};
assert(M3.m_map == expected_map);
M3.assign(0, 30, 'a');
// M3.print(); //
expected_map = {};
assert(M3.m_map == expected_map);
M3.assign(0, 1, 'a');
// M3.print(); //
expected_map = {};
assert(M3.m_map == expected_map);
// MARK: M4
// std::cout << "MARK: M4" << std::endl;
map_t M4{'A'};
for (int i=0; i < 1000000; i++)
{
M4.assign(i, i+1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i, i-1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i+1, i, i % 2 == 0 ? 'A' : 'B');
M4.assign(i-1, i, i % 2 == 0 ? 'A' : 'B');
M4.assign(i+1, i+1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i+1, i-1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i-1, i+1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i-1, i-1, i % 2 == 0 ? 'A' : 'B');
M4.assign(i, i+1, i % 2 == 0 ? 'B' : 'A');
M4.assign(i, i-1, i % 2 == 0 ? 'B' : 'A');
M4.assign(i+1, i, i % 2 == 0 ? 'B' : 'A');
M4.assign(i-1, i, i % 2 == 0 ? 'B' : 'A');
M4.assign(i+1, i+1, i % 2 == 0 ? 'B' : 'A');
M4.assign(i+1, i-1, i % 2 == 0 ? 'B' : 'A');
M4.assign(i-1, i+1, i % 2 == 0 ? 'B' : 'A');
M4.assign(i-1, i-1, i % 2 == 0 ? 'B' : 'A');
}
const auto end = std::chrono::steady_clock::now();
const std::chrono::duration<double> elapsed_seconds = end - start;
std::cout << "abcMap elapsed time: " << elapsed_seconds.count() << std::endl;
}
Aucun commentaire:
Enregistrer un commentaire