Найдите сумму подмножества с умножением

Допустим, у нас есть набор

{a_1, a_2, a_3, ..., a_n}

Цель состоит в том, чтобы найти сумму, которую мы создаем следующим образом: мы находим все подмножества, длина которых равна 3, затем умножаем элементы каждого подмножества (для подмножества {b_1, b_2, b_3} результатом будет b_1*b_2*b_3). В конце мы суммируем все эти продукты.

Я ищу алгоритм с кратчайшим временем выполнения.

Пример

SET: {3, 2, 1, 2}

Let S be our sum.

S = 3*2*1 + 3*2*2 + 2*1*2 + 3*1*2 = 28

person Atomic Tomq    schedule 01.11.2013    source источник
comment
{3, 2, 1, 2} не является набором. Это мультисет / сумка   -  person amit    schedule 01.11.2013
comment
@amit Из проблемы кажется, что ее следует рассматривать как набор.   -  person Abhishek Bansal    schedule 01.11.2013
comment
@AbhishekBansal Не думайте - он дважды считает 2, важно количество вхождений каждого элемента - а в наборе - нет повторов.   -  person amit    schedule 01.11.2013
comment
@amit да, поэтому они рассматриваются как 4 разных элемента в уникальном наборе (4C3).   -  person Abhishek Bansal    schedule 01.11.2013


Ответы (3)


Легче вычислить сумму умноженных троек, когда разрешены повторения (например, a_1*a_1*a_1). Эта сумма составляет всего (sum^3).

Поскольку повторения запрещены, мы можем просто их вычесть: (sum^3 - 3*sumsquares*sum).

Но приведенная выше формула вычитает элементы на главной диагонали 3 раза, тогда как ее нужно вычесть только один раз. Нужно компенсировать это: (sum^3 - 3*sumsquares*sum + 2*sumcubes).

Приведенная выше формула не учитывает 3! перестановки каждой тройки. Значит надо разделить на 3!.

Наконец, у нас есть алгоритм с линейным временем:

  1. Вычислить сумму заданных элементов мультимножества, сумму квадратов, сумму кубов.
  2. result = (sum^3 - 3*sumsquares*sum + 2*sumcubes)/6
person Evgeny Kluev    schedule 01.11.2013
comment
Извините, не получил ваш ответ. В расширении (a+b+c)^3 есть такие термины, как (a^2)*b. Как ваша формула вычитает эти члены? - person Abhishek Bansal; 01.11.2013
comment
@AbhishekBansal: это проще объяснить для подмножеств длины 2 (пары). Мы могли бы записать произведения всех возможных пар в виде матрицы. Затем, чтобы получить правильную сумму, мы могли бы сложить вместе все элементы выше (или ниже) главной диагонали. Сложность O(N^2). В качестве альтернативы мы могли бы сложить каждую строку (что дает a_i*sum), затем сложить все эти суммы (что дает sum^2), затем удалить элементы главной диагонали и разделить на 2. Теперь сложность равна O(N). То, что я предложил, является просто обобщением этого подхода для подмножеств длины 3. - person Evgeny Kluev; 01.11.2013

Вот O(n^2) подход:

sum = SUM(list)
answer = 0
for each i from 0 to n:
   sum -= list[i]
   remains = sum
   for each j from i+1 to n:
      remains -= list[j]
      answer += list[i] * list[j] * (remains)

Это работает, потому что для каждых двух элементов x,y нужно суммировать x*y*z (для всех элементов z), но сумма всех возможных значений z равна SUM(list) - x - y.

Итак, вместо того, чтобы делать: x*y*z1 + x*y*z2 + ... + x*y*z(n-2), вы в основном делаете x*y*(z1 + ... + z(n-2))

РЕДАКТИРОВАТЬ: Отредактированный множественный подсчет из-за того, что он не умножается только в «хвосте», как упоминал @AbhishekBansal . Вам нужно умножить каждый элемент только на «хвост» списка, где хвост — это все элементы после последнего элемента среди x,y.

person amit    schedule 01.11.2013
comment
Вау не подумал об этом. - person Abhishek Bansal; 01.11.2013
comment
Если подумать, я почему-то думаю, что у него могут быть дубликаты. - person Abhishek Bansal; 01.11.2013
comment
@AbhishekBansal TBH, я не уверен (после повторного прочтения), чего на самом деле хочет ОП, его пример добавляет 3 * 2 * 1 и 3 * 1 * 2, но считает 3 * 2 * 2 только один раз. - person amit; 01.11.2013
comment
Ааа да ты прав. Кстати, для уникального набора моя реализация вашей идеи правильная? - person Abhishek Bansal; 01.11.2013
comment
@AbhishekBansal Нет, я прокомментировал твой пост. - person amit; 01.11.2013
comment
Я думаю, что вопрос вполне ясен, потому что вы можете выбрать 3 элемента из 4 разных элементов 4 возможными способами. ОП так и делает. Более того, я все еще думаю, что ваше решение переоценено. - person Abhishek Bansal; 01.11.2013
comment
@AbhishekBansal Да, вы правы, вам нужно умножать только в «хвосте» .. объединили усилия, чтобы получить оптимальное решение. - person amit; 01.11.2013

Полный рабочий код на C++ (продолжение идеи Амита)

#include <iostream>

using namespace std;

int main()
{
    int s[] = {3, 2, 1, 2};

    double sumOfFullList = 0;
    for ( int i = 0; i < 4; i++ )
        sumOfFullList += s[i];

    double sum = 0;

    for ( int i = 0; i < 4; i++ ) {
      double sumOfList = sumOfFullList - s[i];
      sumOfFullList -= s[i];
      for ( int j = i+1; j < 4; j++ ) {
          sumOfList -= s[j];
          sum += s[i]*s[j]*(sumOfList);
          //cout << s[i] << " " << s[j] << " " << sumOfList;
      }
    }

    cout << sum << endl;
    return 0;
}

Выход:

28
person Abhishek Bansal    schedule 01.11.2013
comment
Обратите внимание, что при условии, что все числа положительные, sumOfList быстро станет отрицательным. - person amit; 01.11.2013