Найдите элементы массива на основе минимальной суммы

Я написал цикл на C++, чтобы получить 6 случайных чисел и сохранить их в массиве. Я хотел бы суммировать элементы массива до тех пор, пока не получу значение, превышающее число «x», но я хотел бы сделать это без необходимости добавления всех элементов. Цель состоит в том, чтобы найти первые элементы, сумма которых равна значению x.

Например, массив равен [1,2,3,4,5,6] и x = 6, поэтому я бы искал элементы [1,2,3].

Я просмотрел стандартную библиотеку и попытался использовать функцию суммы из «valarray», но это просто дает сумму всех элементов. Буду очень признателен за любые идеи о том, как это успешно закодировать.


person user103572    schedule 12.05.2009    source источник
comment
Какой ответ будет правильным для 10? это 1, 4, 5 или 2, 3, 5 или 4, 6?   -  person Binary Worrier    schedule 12.05.2009
comment
или это 1, 2, 3, 4? (я больше ничего не пропустил?)   -  person Binary Worrier    schedule 12.05.2009
comment
Вы сказали случайные числа, хотя массив упорядочен и имеет ли это значение? Может ли он содержать [10,8,5,3,2,1], и тогда 10 > 6, то есть 10 будет ответом?   -  person Trotts    schedule 12.05.2009
comment
Привет, порядок специально сгенерированного массива будет иметь значение, но, как и в случае, когда вы упомянули 10, это правильный ответ, поскольку это был единственный возможный способ получить сумму, равную или превышающую 6.   -  person user103572    schedule 12.05.2009


Ответы (9)


Напишите функтор, выполняющий сложение.

#include <algorithm>
struct SumToo
{
     SumToo(int val):m_val(val),m_sum(0) {}
     int m_val;
     int m_sum;

     bool operator()(int next)
     {
         m_sum += next;
         return m_sum >= m_val;
     }
 };

 int main()
 {
       int data[] = {1,2,3,4,5,6};

       int* find = std::find_if(data,data+6,SumToo(6));
 }
person Martin York    schedule 12.05.2009
comment
каждый раз, когда я думаю о функторах, я немного больше презираю Java (в настоящее время программист Java :() - person Tom; 12.05.2009
comment
Спрашивающий хочет, чтобы значение было больше x, но не равно x. - person Thomas L Holaday; 12.05.2009
comment
-1: предикатные функторы не должны иметь нестатических полей данных (securecoding.cert.org/confluence/display/cplusplus/) или я что-то упустил? - person stephan; 17.08.2010
comment
См.: sgi.com/tech/stl/table_of_contents.html. более сложные немутирующие алгоритмы. Таким образом, такие вещи, как объект remove_if, не должны сохранять состояние, в то время как неизменяющие алгоритмы (например, find_if) намного проще и будут в порядке. Хотя у меня нет какой-либо конкретной стандартной ссылки, в которой говорится, что где действительно (кроме тестов формы). - person Martin York; 17.08.2010
comment
Хммм, find_if не гарантирует порядок исполнения (25.1.2 стандарта). Хотя я согласен с тем, что ваша версия работает в большинстве реализаций, она вызывает ошибку, если find_if не выполняется последовательно. Возьмите параллельный find_if gcc (gcc.gnu.org/onlinedocs/libstdc++/manual/parallel_mode .html): он разбивает диапазон на n блоков и ищет блоки параллельно. См. __find_template: gcc.gnu.org/onlinedocs/libstdc++/latest-doxygen /. Следовательно, ошибка — это всего лишь -D_GLIBCXX_PARALLEL -fopenmp флаг компилятора (без учета дополнительных условий гонки). - person stephan; 17.08.2010
comment
@Stephan: Ошибки всегда на шаг впереди, когда вы используете нестандартные флаги / расширения компилятора. На самом деле 25.1.2 гарантирует порядок. (Посмотрите определение итератора ввода (доступ к элементам возможен только по порядку)). - person Martin York; 17.08.2010
comment
Я вежливо не согласен. IIRC, 1) ... for_each и некоторые алгоритмы в <numerics> порядке гарантии, но не find_if. Стандарт допускает специализацию алгоритмов, например. итераторы произвольного доступа. 2) ... хотя не все алгоритмы stl параллельного режима gcc соответствуют требованиям, я считаю, что find_if соответствует требованиям, потому что его наблюдаемое поведение соответствует требованиям стандарта. Хотя я не уверен на 100% в отношении всего этого, поэтому я мог бы открыть вопрос о SO после некоторого размышления. Спасибо за обсуждение. - person stephan; 17.08.2010
comment
@stephan: Вы правы, порядок применения функтора не гарантируется. Но итератор ввода разрешает только последовательный доступ (и вы должны прочитать значение перед переходом к следующему (иначе оно исчезнет), и вы не можете пройти мимо найденного элемента (поскольку вы должны вернуть итератор, и вы не можете получить назад, если пройти мимо него)). Но если у вас есть итератор с произвольным доступом, то, конечно, вы можете сделать это в любом порядке. У вас есть ссылка на стандарт о специализации алгоритмов на основе типов итераторов (для дальнейшего использования). - person Martin York; 17.08.2010
comment
У меня нет ссылки, кроме моего понимания наблюдаемого поведения и некоторых доказательств. Степанов часто упоминает специализацию (например, sgi.com/tech/stl/drdobbs-interview. html) и реализации делают это, например. std::fill звоню memset за char. Почему нельзя специализироваться на типе итератора? Кроме того, в стандарте четко прописан порядок, где это важно, например. в std::copy, чтобы разрешить перекрытие. Из этого я вытекаю, что реализация может свободно выбирать порядок на основе типа итератора, если порядок не определен. Я не могу предложить больше, чем это рассуждение. - person stephan; 19.08.2010
comment
@stephan: это специализации value_type (а не итератора). Для std::copy порядок упоминается только потому, что два диапазона могут перекрываться, и разработчики хотели убедиться, что операция четко определена. - person Martin York; 21.08.2010

Я предполагаю, что вам просто нужны первые X элементов в массиве, пока их сумма не достигнет порогового значения или не превысит его (там вопрос был немного расплывчатым).

Если да, то я не знаю, как это сделать без собственного цикла:

int sum = 0;
int i = 0;
for( ; i < len; ++i ) {
    sum += array[i];
    if( sum >= 6 ) {
        break;
    }
}

Теперь «i» содержит индекс, при котором сумма достигла или превысила ваш порог.

person Matt Dillard    schedule 12.05.2009

Избегайте ответов, которые предлагают использовать find_if с предикатом с отслеживанием состояния. Предикаты с сохранением состояния опасны, поскольку алгоритмы STL предполагают, что копирование предикатов безопасно. В этом случае, если копии предиката сделаны, то каждая из них будет иметь разную «промежуточную сумму» и не обязательно будет действовать на все значения или в правильном порядке.

Особенно избегайте решения, которое реализует член operator() своего предиката как константную функцию-член, но помечает его члены как изменяемые, поскольку это обманывает вас, заставляя думать, что это не предикат с отслеживанием состояния, что плохо.

Я бы предложил использовать либо один из ответов, который просто зацикливается, чтобы найти ответ, либо ответ, который использует аккумулятор, так как это самый правильный способ сделать это (даже если код выглядит немного громоздким.

Обратите внимание, что предупреждения могут не относиться к массивам C и find_if; Я просто не хочу, чтобы вы узнали, что предикаты с сохранением состояния — это правильный способ решить вашу проблему, поскольку вы можете в конечном итоге использовать это неправильное решение в ситуации, когда это опасно в будущем.

Ссылка: Стандарты кодирования C++: 101 правила, рекомендации и рекомендации, пункт 87.

person Ben Hymers    schedule 13.05.2009

Вот немного более общая версия:

#include <iostream>
#include <algorithm>

// return an iterator _Last such that sum 
// of all elements in the range [_First, _Last)
// satisfies the predicate Func
template<class InIt,
class Ty,
class Fn> inline
InIt accumulate_if(InIt First, InIt Last, Ty Val, Fn Func)
{   
    for (; Func(Val) && First != Last; ++First)
        Val = Val + *First;
    return (First);
}

int main() {
    int num[] = {1, 2, 3, 4, 5, 6};
    int *last = accumulate_if(num, num + sizeof num / sizeof num[ 0 ], 
                              0, std::bind2nd(std::less<int>(), 6));
    std::copy(num, last, std::ostream_iterator<int>(std::cout, "\n"));
    return 0;
}
person dirkgently    schedule 12.05.2009

Вычтите числа из x одно за другим, пока не получите 0 или меньше.

Без дополнений, как вы и хотели :)

person Daniel Daranas    schedule 12.05.2009

Надеюсь, это сработает:

/* Returns an index i, given array valarray[0,1..n] and number x where i is an index to valarry such that sum over j of valarray[j] for j = 0 to i > x */
int getFirstSum(int *valarray, int n, int x)
{
   int i = 0;
   int sum = x;
   while(sum > x && i < n)
   {
      i++;
      sum -= valarray[i];
   }
   return i;
}
person encee    schedule 13.05.2009

будет что-то вроде:

struct StopAtValue{
  StopAtValue(int sum) : m_sum(sum), m_accumulated(0){}
  bool operator()(int val){
    m_accumulated += val;
    return m_accumulated >= sum;
  }
  int m_sum;
  int m_accumulated;
}


int* pos = std::find_if(&array[0], &array[n], StopAtValue(6));
person Viktor Sehr    schedule 12.05.2009

Ну, я бы использовал вектор

T addUntil(T array[],size_t len,T thres){
    vector<T> vec = vector_from_array(array,len)
    T sum;
    for (size_t i=0;i< vec.size(),sum<thresh;i++){
          sum+= vec[i];
    }
    return sum;
}

Для определения T потребуется оператор+ и оператор‹.

person Tom    schedule 12.05.2009
comment
Я думал о том, чтобы не передавать длину массива в качестве параметра. Теперь, когда я вижу другие ответы, я понимаю, что это довольно плохо. - person Tom; 12.05.2009
comment
Две вещи неверны: 1) это должно быть T array[] и 2) вы должны передать длину массива, на который указывает указатель, вашей функции vector_from_array. - person Johannes Schaub - litb; 10.07.2009
comment
Спасибо литб. Java серьезно разрушает мои навыки C++. - person Tom; 10.07.2009

Вы можете использовать std::find_if() вместе с функтором, который поддерживает промежуточную сумму, и возвращать true только из функтора, когда вы нашли элемент, который ставит вас на первое место или выше.

Например:

#include <cstdlib>
#include <algorithm>
#include <functional>
#include <iostream>
#include <string>
using namespace std;

// functor returns true when the running total >= findVal
struct running_total : public unary_function<int, bool>
{
    running_total(int findVal) : findVal_(findVal), runningTtl_(0) {};
    bool operator()(int rhs) const
    {
        runningTtl_ += rhs;
        if( runningTtl_ >= findVal_ )
            return true;
        else
            return false;
    }
private:
    mutable int runningTtl_;
    const int findVal_;
};

int main()
{

    int nums[] = {1, 2, 3, 4, 5, 6};
    size_t count = sizeof(nums)/sizeof(nums[0]);

    const int scanTtl = 6;  // running total to scan to
    int * pos = find_if(&nums[0], &nums[0]+count, running_total(scanTtl));

    cout << "Elements Totaling " << scanTtl << " : ";
    copy(&nums[0], pos+1, ostream_iterator<int>(cout, ", "));

    return 0;
}
person John Dibling    schedule 12.05.2009