Алгоритм стандартной библиотеки для усреднения разницы между соседними элементами

У меня есть std::vector<std:::chrono::system_clock::time_point> ценности.

Я хочу усреднить разницу во времени в миллисекундах между соседними элементами. Для контейнера требуется как минимум 2 элемента, и у меня возникают проблемы с функцией усреднения. Я использовал std::accumulate, но я не думаю, что смогу заставить это работать, так как получаю массу ошибок компилятора. Лямбда ниже, которую я использую в качестве функции уменьшения с алгоритмом накопления

    auto gLambda = [&](
        system_clock::time_point t1, system_clock::time_point t2) {
            return duration_cast<milliseconds>(t2 - t1).count();
    };

хотя работает нормально

Демо live coliru

int main()
{
    // lambda called with adjacent samples from the deque
    auto gLambda = [&](
        system_clock::time_point t1, system_clock::time_point t2) {
            return duration_cast<milliseconds>(t2 - t1).count();
    };
    
    // this line compiles and the lambda seems to do the right thing
    const auto t1 = system_clock::now();
    std::this_thread::sleep_for(55ms);
    const auto t2 = system_clock::now();
    // result is 55 as expected
    auto result = gLambda(t1, t2);    
    std::cout << result << std::endl;    
    // create a vector of 10 timestamps
    std::vector<system_clock::time_point> timeStamps;    
    for (auto i=0; i<10; i++) {
        timeStamps.emplace_back(system_clock::now());
        std::this_thread::sleep_for(15ms);
    }
    // THIS LINE CAUSES THE COMPILATION ERROR
    auto result1 = std::accumulate(timeStamps.cbegin(), timeStamps.cend(), 0.0, gLambda);
    std::cout << result1 << std::endl;
}

приводит к следующим ошибкам компиляции, которые я не понимаю.

In file included from /usr/local/include/c++/10.2.0/numeric:62,
                 from main.cpp:4:
/usr/local/include/c++/10.2.0/bits/stl_numeric.h: In instantiation of '_Tp std::accumulate(_InputIterator, _InputIterator, _Tp, _BinaryOperation) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >*, std::vector<std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > > > >; _Tp = double; _BinaryOperation = main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>]':
main.cpp:30:88:   required from here
/usr/local/include/c++/10.2.0/bits/stl_numeric.h:169:22: error: no match for call to '(main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>) (double&, const std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >&)'
  169 |  __init = __binary_op(_GLIBCXX_MOVE_IF_20(__init), *__first);
      |           ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:12:20: note: candidate: 'main()::<lambda(std::chrono::_V2::system_clock::time_point, std::chrono::_V2::system_clock::time_point)>'
   12 |     auto gLambda = [&](
      |                    ^
main.cpp:12:20: note:   no known conversion for argument 1 from 'double' to 'std::chrono::_V2::system_clock::time_point' {aka 'std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >'}

Разве это не будет просто равно (last - first) / length? Все остальное отменяется, это телескопическая сумма.

Nate Eldredge 10.12.2020 07:40

Третий аргумент (начальное значение) accumulate — это 0.0, то есть double. Это трансформируется в time_point? Именно таким окажется первый аргумент лямбды.

PaulMcKenzie 10.12.2020 07:45

@PaulMcKenzie Я не уверен, как преобразовать двойное значение в момент времени, возможно, я попытался установить начальное значение, явно установленное для момента времени сейчас() вместо 0,0, и это вызвало столько же ошибок, как «неизвестное преобразование для аргумента». 1 с «long int» на «const std::chrono::time_point»

johnco3 10.12.2020 08:04

@johnco3 Вероятно, мне нужно еще немного поиграть с вашим примером. Но определенно ошибка является классической std::accumulate, когда аргумент начального значения не соответствует (или не может быть преобразован) первому аргументу в функторе / лямбда / и т. д. Последняя строка в списке ошибок в основном утверждает это.

PaulMcKenzie 10.12.2020 08:06

@PaulMcKenzie спасибо, я думаю так же - буду признателен, если вы поможете мне с примером. Я потратил около часа на эксперименты безрезультатно.

johnco3 10.12.2020 08:09

Я не думаю, что накапливать - правильный инструмент для работы, он суммирует все элементы, а не соседние пары, руководство по циклу, вероятно, будет лучшим решением.

Alan Birtles 10.12.2020 08:28

@ johnco3 Я думаю, что обычный цикл тоже был бы немного проще. В любом случае, я не эксперт в манипулировании различными типами времени навскидку (стыдно) в C++, но это компилируется. Справится ли он со своей задачей — это отдельная история.

PaulMcKenzie 10.12.2020 08:33

@PaulMcKenzie - забавно, ты должен это сделать, я сам пробовал что-то подобное coliru.stacked-crooked.com/a/fb9c1f10c8e5fd62 :) похоже, у нас была такая же идея. Я думаю, что мне нужно выбрать немного другой алгоритм, как указывает Алан Бертлз. Я использовал смежный_найти раньше - возможно, это даст правильный результат, если я аккумулирую различия между соседними элементами, а затем, наконец, разделю на size-1 вектора

johnco3 10.12.2020 08:39

Да, adjacent_find, который возвращает false в лямбде, будет работать. Меня ругали за то, что использование adjacent_find для такой «парной работы» является злоупотреблением функцией, но если это сработает...

PaulMcKenzie 10.12.2020 08:47

Пользователь @PaulMcKenzie sehe предупреждает о злоупотреблении смежным_find и предпочитает std::transform с бинарными предикатами ... хммм, нужно посмотреть, о чем идет речь - спасибо за вашу помощь. Я ценю это

johnco3 10.12.2020 09:01

Взгляните также на std::inner_product, это может быть применимо к вашей проблеме.

cigien 10.12.2020 15:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
11
265
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как предложил cigien в комментариях, вот как это сделать с inner_product:

auto result = inner_product(
                  timeStamps.begin() + 1, timeStamps.end(),
                  timeStamps.begin(), 0ms,
                  [](auto x, auto y) {return x + y;},
                  [](auto x, auto y) {return round<milliseconds>(x - y);})
              / (timeStamps.size() - 1);

Спасибо за реализацию, какое действительно элегантное злоупотребление std::inner_product - спасибо @cigien. Намного более интуитивным, чем злоупотребление std::adjacent_find или std::transform_reduce, что я думал об использовании.

johnco3 10.12.2020 19:38

Одной из неинтуитивных вещей, которые вы закодировали (о которых, я думаю, следует упомянуть), было использование инициализатора 0 мс. Я боролся с этим, так как мне нужно было иметь начальное значение, которое можно было бы преобразовать в момент времени (начальное значение для аккумулятора). Первоначально я использовал двойное значение 0.0, что привело к примерно 20 страницам ошибок, а фактическая ошибка была спрятана где-то посередине :). Хороший и отличный ответ - который работает !!

johnco3 10.12.2020 19:44

теперь, когда я злоупотребляю inner_product, мне было интересно, можете ли вы определить, где что-то идет не так, я пытаюсь рассчитать стандартное отклонение (джиттера пакетов), используя временные метки и частичные результаты (сумма квадратов времени отдельных пакетов - среднее время пакета) посмотрите далеко, я ожидал, что стандартное отклонение около 0 в моей живой демонстрации coliru coliru.stacked-crooked.com/a/4823684c18c05680

johnco3 11.12.2020 01:24

Похоже на первую операцию ты (t[i]-t[i] - mean)^2, когда захочешь (t[i]-t[i-1] - mean)^2. Я считаю, что первый диапазон должен начинаться с timeStamps.begin()+1. И вам не хватает деления на N (т.е. (timeStamps.size() - 1).

Howard Hinnant 11.12.2020 03:04

Другие вопросы по теме