Вычислить минимальный скалярный продукт С++

Я пытаюсь вычислить минимальное скалярное произведение для двух массивов/векторов. Ниже приведены подробности:

Задача: даны две последовательности a1, a2, . . . , и b1, b2, . . . , bn, найти такую ​​перестановку π второй последовательности, что скалярное произведение a1, a2, . . . , an и bπ1, bπ2, . . . , bπn минимально.

Моя логика работает нормально, но когда я пытаюсь ввести данные, как показано ниже, происходит сбой из-за целочисленного переполнения. Какие типы данных я буду использовать для выполнения моего условия 1 ≤ n ≤ 10^3; −10^5 ≤ ai, bi ≤ 10^5 для всех 1 ≤ i ≤ n

1

99999

99999

Результат для приведенного выше сценария должен быть 9999800001, но я получаю 1409865409.

#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric>

using std::vector;

int64_t min_dot_product(int n, vector<int> a, vector<int> b) {
    int64_t result = 0;
    if (n != 0)
    {
        std::sort(a.begin(), a.end());
        std::sort(b.begin(), b.end());
        std::reverse(a.begin(), a.end());

        /*for (long long int i = 0; i < n; i++) {
            result += a[i] * b[n - 1 - i];
        }*/

        result = std::inner_product(a.begin(), a.end(), b.begin(), 0);
    }
    return result;
}

int main() {
    int n;
    std::cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }
    for (int i = 0; i < n; i++) {
        std::cin >> b[i];
    }
    std::cout << min_dot_product(n, a, b) << std::endl;
}

person hakuna    schedule 08.05.2016    source источник
comment
Вы не должны менять порядок a, только b.   -  person chris    schedule 08.05.2016
comment
Также вас может заинтересовать std::next_permutation.   -  person StoryTeller - Unslander Monica    schedule 08.05.2016
comment
Нет, это работает, даже если я изменю порядок, я проверял то же самое.   -  person hakuna    schedule 08.05.2016
comment
На самом деле, ты прав. Любое изменение порядка а совпадает с изменением результирующего b, если а не меняется.   -  person chris    schedule 08.05.2016
comment
Я пытаюсь узнать, есть ли что-то не так в вопросе, который я задал? поскольку кто-то проголосовал за него. Любой вклад приветствуется.   -  person hakuna    schedule 08.05.2016


Ответы (1)


Внесите следующие изменения:

  1. Замените vector<int> на vector<int64_t>, чтобы сохранить число в виде 64-битного целого числа.

  2. Используйте result = std::inner_product(a.begin(), a.end(), b.begin(), 0LL);
    (0LL предполагает, что результат int64_t)

Проблема в том, что вы храните его в int32_t, поэтому умножение переполняется.

person Rishit Sanmukhani    schedule 08.05.2016
comment
Учитывая диапазон чисел в каждом векторе, нет необходимости заменять vector<int> на vector<int64_t>. int32_t подойдет. - person StoryTeller - Unslander Monica; 08.05.2016
comment
По какой-то причине Int32 не работает. Int64_t заработал. и что 0LL - это главное, что я пропустил раньше, когда перешел на Int64_t. Большое спасибо ! - person hakuna; 08.05.2016
comment
@StoryTeller, int32_t в нашем случае не сработает. Потому что в библиотечной реализации std::inner_product он умножает значения, не приводя их к типу данных результата. Таким образом, int32_t будет умножено, и результаты переполнятся. - person Rishit Sanmukhani; 08.05.2016
comment
Вы сильно ошибаетесь в этом. Тип возвращаемого значения inner_product совпадает с переданным ему исходным значением. А требование к операторам таково, что возвращаемое ими значение можно преобразовать в возвращаемый тип. Короче говоря, если неявное преобразование из int32_t в int64_t не завершится ошибкой, вам не нужно без необходимости расширять свои векторы. - person StoryTeller - Unslander Monica; 08.05.2016
comment
@StoryTeller, представьте, что вы суммируете внутренние векторы математических векторов, где вы перегрузили *, чтобы сделать скалярное произведение. Если реализация возвращает a1*b1 + a2*b2 + ..., все в порядке, и она возвращает скаляр. Однако, если он попытается выполнить преобразование перед умножением (R(a1)*R(b1) + ...), он потерпит неудачу из-за отсутствия преобразования между вектором Math и скаляром. Я не вижу стандарта, предписывающего, чтобы операнды умножения предварительно преобразовывались в тип результата, просто чтобы общий результат был для большей универсальности. - person chris; 08.05.2016
comment
@StoryTeller, int32_t не будет повышен до int64_t до умножения. Формула res = res + a*b, где a, b равны int32_t. Результат a*b будет повышен до int64_t. - person Rishit Sanmukhani; 08.05.2016
comment
@chris, да, я могу представить случай, когда преобразование не удается. Отсюда и мой тщательно сформулированный комментарий. Однако стандарт предписывает преобразование, если вы ожидаете, что ваш код скомпилируется, так что это спорный вопрос. - person StoryTeller - Unslander Monica; 08.05.2016
comment
@StoryTeller, требование к операторам таково, что возвращаемое ими значение может быть преобразовано в возвращаемый тип - мой пример этому удовлетворяет. Скаляр, возвращаемый в результате умножения двух векторов Math, можно преобразовать в любой скалярный тип возвращаемого значения, который вы ожидаете от выполнения внутреннего произведения (упс, в прошлый раз упоминалась внутренняя сумма) для группы векторов Math с заданными скалярными произведениями для умножения. - person chris; 08.05.2016
comment
@chris, и ваш код будет правильно построен. Вторая реализация не сможет собрать ваш код (который удовлетворяет всем базовым требованиям). Поэтому я бы сказал, что это не соответствует стандарту, и поэтому я не ожидаю его увидеть. - person StoryTeller - Unslander Monica; 08.05.2016
comment
@StoryTeller, я сдался и открыл черновик стандарта. Вычисляет результат, инициализируя аккумулятор acc с начальным значением init и затем изменяя его с помощью acc = acc + (*i1) * (*i2) - person chris; 08.05.2016
comment
@Крис, это то, что я вспомнил. И это также подтверждает точку зрения Ришита. Я ошибся, думая, что он не переполнится. Я надеялся, что целочисленное продвижение нас спасет, но увы. - person StoryTeller - Unslander Monica; 08.05.2016
comment
@StoryTeller, интересно, продвижение было бы, если бы у нас был тип данных меньше int, например short (при условии, что они не одного размера в этой реализации). В этом случае shorts будут повышены до ints перед умножением только из-за того, как язык использует интегральные операторы. - person chris; 08.05.2016
comment
@chris, с 16-битными шортами и 32-битными целыми, шорт может содержать значения до 10⁵, но умножение 10⁵ * 10⁵ приведет к переполнению, несмотря на то, что операнды повышены до целых. :) - person bipll; 10.05.2016
comment
@bipll, Да, я говорил в более общем смысле. - person chris; 10.05.2016