Интегрирование условия между + = (и, возможно, вызовом для математических свистов) для матричного итератора - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть вопрос и, возможно, математический вызов.У меня есть шаблон класса Matrix с динамическим хранением данных (имеется в виду, что для типа имеется только один параметр шаблона).

Я сохраняю данные матрицы в виде std::vector в мажорном порядке строк, и я пишу для него итератор столбцов, который будет идти вниз на один столбец, начинаться сверху следующего, идти вниз по нему и продолжаться (итератор строки тривиален, поскольку это просто итератор для std::vector).Для увеличения / уменьшения для итератора с произвольным доступом я использую частную функцию advance, чтобы избежать дублирования кода между operator с.Это выглядит так:

template<typename T>
void Matrix<T>::col_iterator::advance(std::ptrdiff_t movement) { // defaulted to movement = 1 in header
    _ptr += movement * _shiftFactor;
    if (ge(_ptr, _start + _l)) {
        _currCol++; // increments column count
        _ptr = _start + (_currCol < _shiftFactor ? _currCol : _l); // moves pointer to the top of the column, or to the end if there are no more columns
    }
}

Чтобы дать некоторый контекст, часть ge() фактически вызывает operator() на статическом функторе std::greater_equal<T*>, _ptr - указатель на текущую позициюитератор, _shiftFactor - это число столбцов матрицы (n), _start - это начальная точка вектора (это не меняется), а _l - это размер целого *.1021 * вектор данных.Хорошо, я думаю, что графика была бы хороша, чтобы понять это.

Предположим, у меня есть Matrix<int> A, который выглядит так:

Element: 1 2 3 4
Index:   0 1 2 3
----------------
Element: 5 6 7 8
Index:   4 5 6 7
----------------
Element: 9 1 2 3
Index:   8 9 10 11
----------------
Element: 4 5 6 7
Index:   12 13 14 15
----------------

, где Index - индекс, соответствующийэлемент хранится в std::vector<int>.Глядя на мою функцию advance, я обнаружил, что когда она вызывается (например,

template<typename T>
inline typename Matrix<T>::col_iterator &Matrix<T>::col_iterator::operator++() {
    advance(); // remember movement is defaulted to 1
    return *this;
}

или

template<typename T>
inline typename Matrix<T>::col_iterator &Matrix<T>::col_iterator::operator+=(std::ptrdiff_t movement) {
    advance(movement);
    return *this;
}

), то происходит то, что _ptrв случае operator++ перейдет к первому элементу следующей строки.Таким образом, один вызов ++ приведет к этому с индексом 4, другой вызов с индексом 8, и так далее.И, как вы видите, если вы продолжите идти, пока не доберетесь до 12 и не попробуете снова вызвать ++, оператор if поймает его (начиная с 12 + 4> 15) и перейдет к индексу 1 и продолжит к 59, 13, затем 2 и т. Д. Он попадет в индекс 15, а затем троичный установит указатель на индекс «16», который находится вне вектора, что на самом деле Matrix<T>::col_end() (не показано).

Однако в этом и заключается проблема.Смотрите, для operator++ он работает отлично , потому что movement = 1, так что if происходит один раз при каждом вызове, поэтому он проверяет, находится ли _ptr в правильном положении каждый раз.Но когда вы расширяете его на operator+=, возникает проблема.То, как я это делаю, у меня есть _ptr = movement * _shiftFactor, поэтому, предположительно, добавив кратные числа _shiftFactor, нужно пропустить movement количество строк.movement потенциально может сместить _ptr на величину, достаточную для того, чтобы приземлиться далеко выше _start + _l, но это считается только одним шагом - к вершине следующего столбца.Эта проблема возникает из-за того, что if вызывается только ONCE после того, как _ptr ** увеличивается на все ** movement * _shiftFactor.

Представьте себе этот сценарий.movement = 5 и я вызываю operator+=, когда _ptr находится в индексе 12 (т.е. у меня есть Matrix<int>::col_iterator ci с указанием в настоящее время на индекс 12, и я пытаюсь сделать ci += 5).Результирующее значение _ptr (_ptr += 5 * 4) на путь выше максимального индекса, но функция не знает, насколько.Таким образом, он просто видит, что он больше, и устанавливает значение индекса на следующее, 1. Это должно быть на уровне индекса 2, поскольку ci += 5 подразумевает, что он пропускает индексы 1, 5, 9, 13 и попадает на 2.!!Я подчеркиваю этот момент: оператор if вызывается только один раз, где в действительности он должен вызываться через каждый _shiftFactor шаг.

Теперь поверхностное решение для интеграции оператора if между каждым последующим шагом строки состоит в том, чтобы сделать это вцикл for, например:

template<typename T>
void Matrix<T>::col_iterator::advance(std::ptrdiff_t movement) { // defaulted to movement = 1 in header
    for (std::size_t i = 0; i < std::abs(movement); i++) {
        _ptr += static_cast<int>(std::copysign(1, movement)) * _shiftFactor; // always is either _ptr += _shiftFactor or _ptr += -_shiftFactor
        if (ge(_ptr, _start + _l)) {
            _currCol++;
            _ptr = _start + (_currCol < _shiftFactor ? _currCol : _l);
        } // this conditional is checked every time a row increments
    } // all of this is effectively a movement * _shiftFactor with the if between each increment
}

, и это будет нормально работать. Но , это O (movement) , что является слишком неудовлетворительным, чтобы игнорировать даже для такой тривиальной операции, как эта, по сравнению с O (1) время с умножением !!Должен быть более простой способ сделать это, верно?

Извините за эссе, но я действительно хотел убедиться, что я покрыл все.Мой вопрос, потенциально для вас там математические свистки: как я могу снова сделать это O (1), оставаясь при этом при переходе между каждым шагом строки? Может быть, что-то с подсчетом величин _shiftFactor выше _start + _l?Может быть, какой-то мод магия?Помогите!

Спасибо!

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Я также нашел способ сделать это (частично), который использует ту же логику, что и решения @Evg, @Damien и @ Caleth, но сохраняет все в линейном индексе.Я не знаю, является ли это более эффективным или менее, но вот оно.

template<typename T>
void Matrix<T>::col_iterator::advance(std::ptrdiff_t movement) {
    _ptr += movement * _shiftFactor;
    std::ptrdiff_t mag = std::floor((_ptr - (_start + _l)) / static_cast<double>(_shiftFactor));
    std::size_t rows = _l / _shiftFactor;
    if (mag >= 0) {
        _currCol += mag / rows + 1;
        _ptr = _start + (_currCol < _shiftFactor ? _currCol + mag % rows * _shiftFactor: _l);
    }   
}

(mag обозначает величину выше порога).

Это работает для обоих operator++ и произвольно operator+=. Однако , к сожалению, он все еще не работает с operator-- или operator-= из-за способа, которым я его настроил (в частности, в некоторых местах _start + _l необходимо изменить на _start и т. Д.. так далее.).Так что я породил новую проблему, и если бы я мог поработать над ней еще и исправить это, я бы сделал, но у меня есть другие, более важные вещи.Если бы вы все смогли найти решение, я бы тоже его очень оценил.

0 голосов
/ 21 ноября 2018

Использование предложения для преобразования в row, column пару, приращение column, обратное преобразование

template<typename T>
auto Matrix<T>::col_iterator::get_index(std::ptrdiff_t offset)
{
    return std::make_pair(offset / n_cols, offset % n_cols); 
}

template<typename T>
auto Matrix<T>::col_iterator::get_offset(std::pair<std::ptrdiff_t, std::ptrdiff_t> index)
{
    return index.first + index.second * n_rows; 
}

template<typename T>
void Matrix<T>::col_iterator::advance(std::ptrdiff_t movement) 
{
    auto index = get_index(_ptr - _start);

    index.second += movement;
    index.first += (index.second / n_cols); // carry
    index.second %= n_cols; // wrap

    _ptr = get_offset(index) + _start;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...