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::prevorstd::nextis O(N) operation.std::map::lower_boundis O(log N) operation. So can't dofirst = map.lower_bound(first),last = map.lower_bound(last).std::map::insert_or_assignis O(log N).std::map::eraselog(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