Обмен и пересчет дают разные результаты - PullRequest
1 голос
/ 29 марта 2012

Я не уверен, что делаю неправильно, но две версии кода, которые должны давать одинаковые результаты, дают разные результаты. Если бы кто-нибудь мог объяснить, что происходит, я был бы очень признателен.

Ситуация следующая. Я работаю с массивами как «векторами», и у меня есть простая функция sub с двумя перегрузками для вычисления разницы между двумя векторами. Первый в основном вычисляет v := v - w, а второй - x := v - w.

// Subtract w[] from v[]
template <class T>
void sub(T *v, T *w, short m)
{
    for (short r = 0; r < m; r++)
        v[r] = v[r] - w[r];
}

// Subtract w[] from v[] and store result in x[]
template <class T>
void sub(T *v, T *w, T *x, short m)
{
    for (short r = 0; r < m; r++)
        x[r] = v[r] - w[r];
}

Теперь в какой-то момент мне нужно вычислить v - w, и если оно удовлетворяет некоторому условию, заменить v на v - w. Если нет, v должен остаться без изменений. Сначала у меня было

...
// temp := v - w
sub<T>(v, w, temp, m);
if (condition on temp)
{
    // v := v - w
    sub<T>(v, w, m);
}
...

Чтобы повысить эффективность, я подумал, что будет бессмысленно дважды вычислять одно и то же, поэтому я заменил вышеприведенное на

...
// temp := v - w
sub<T>(v, w, temp, m);
if (condition on temp)
{
    // swap v and temp
    std::swap(v, temp);
}
...

Переменная temp фактически используется повторно, что может вызвать проблемы, но каждый раз, когда я впервые вызываю sub<T>(v, w, temp, m); (таким образом, стирая все содержимое в массиве), прежде чем снова использовать temp.

Теперь, после выполнения вышеуказанной замены, результаты моего алгоритма неожиданно меняются. Если бы кто-нибудь мог объяснить, почему результаты меняются и что происходит, я был бы очень признателен!

Заранее спасибо.


Редактировать

Быстрая проверка показывает, что в обоих случаях на каждой итерации конечное значение v и начальное значение temp совпадают. Так что функции делают то, что они должны делать ...

Единственная возможность объяснить причудливое поведение, которое я могу придумать, заключается в том, что по какой-то причине функция std::swap использует случайность, что приводит к различным результатам. Я использую одно и то же начальное число для каждого прогона, и каждый раз получаю одинаковые результаты, но если std::swap где-то использует rand(), это объясняет разные результаты. Но я понятия не имею, почему эта функция будет использовать rand().

Ответы [ 2 ]

0 голосов
/ 29 марта 2012

Оказывается, на тот момент в коде «ущерб уже был нанесен».Перестановка была сделана внутри функции bla, которая принимала v в качестве аргумента следующим образом

void bla(..., T* v, ...)
{
    ...
    // temp := v - w
    sub<T>(v, w, temp, m);
    if (condition on temp)
    {
        // swap v and temp
        std::swap(v, w, m);
    }
    ...
}

Но сегодня я узнал, что таким образом функция bla делает только локальный копирует указатель и работает с этим, вместо использования фактического адреса с фактическим указателем, хранящимся в памяти.Поэтому вместо того, чтобы поменять глобальный v на temp, я поменял местный v на temp, и это не повлияло на глобальный v.Это также объясняет, почему я не заметил никаких проблем локально, поскольку внутри функции v действительно ведет себя так, как должно.Таким образом, в этой точке кода единственным способом поменять значения будет пересчет выражения.

Простое решение работает: измените объявление функции на void bla(..., T*& v, ...), чтобы локальная копия vиспользуемый в функции bla фактически соответствует тому же адресу физической памяти, что и глобальный v.Тогда std::swap() отлично работает.

0 голосов
/ 29 марта 2012

Если вы пересчитываете temp как v-w каждый раз, когда собираетесь использовать temp, значение будет другим, когда v не изменяется и когда v указывает на ранее указанный массив на temp.

Учтите это: в первом случае ваш расчет: temp=v-w каждый раз. Во втором случае: temp=v-w; swap(temp, v); temp=v-w; - тот, что после свопа, отличается от предыдущего, так как массив, на который указывает v, теперь содержит значения из temp-w (используя значения указателей после свопа). То есть второе назначение эквивалентно temp=(v-w)-w; с использованием исходных массивов

...