fork download
  1. #include <array>
  2. #include <deque>
  3. #include <cmath>
  4. #include <stdexcept>
  5.  
  6. // ── Configuration ────────────────────────────────────────────────
  7. static constexpr int NUM_SENSORS = 4;
  8.  
  9. // ── Types ────────────────────────────────────────────────────────
  10.  
  11. // One timestep: one scalar reading per sensor
  12. struct SensorFrame {
  13. std::array<double, NUM_SENSORS> values;
  14. };
  15.  
  16. // Sliding window of the N most recent frames
  17. using SensorWindow = std::deque<SensorFrame>;
  18.  
  19. // ── Core: push a new frame, keeping window size <= N ─────────────
  20.  
  21. void push_frame(SensorWindow& window, const SensorFrame& frame, int N) {
  22. if (N <= 0) throw std::invalid_argument("N must be positive");
  23. window.push_back(frame);
  24. while (static_cast<int>(window.size()) > N)
  25. window.pop_front();
  26. }
  27.  
  28. // ── Geometric mean of all values across the entire window ────────
  29. //
  30. // Treats every scalar in the window (all sensors × all timesteps)
  31. // as one flat list and returns the Nth-root product, where
  32. // N = window_size × NUM_SENSORS.
  33. //
  34. // Uses the log-sum trick to avoid overflow/underflow.
  35.  
  36. double geometric_mean_all(const SensorWindow& window) {
  37. if (window.empty()) throw std::runtime_error("Window is empty");
  38.  
  39. double log_sum = 0.0;
  40. int count = 0;
  41.  
  42. for (const auto& frame : window) {
  43. for (double v : frame.values) {
  44. if (v <= 0.0)
  45. throw std::domain_error("Geometric mean requires positive values");
  46. log_sum += std::log(v);
  47. ++count;
  48. }
  49. }
  50.  
  51. return std::exp(log_sum / count);
  52. }
  53.  
  54. // ── Per-sensor geometric mean across the window ──────────────────
  55. //
  56. // Returns one geometric mean per sensor channel, computed over
  57. // the N timesteps in the window.
  58.  
  59. std::array<double, NUM_SENSORS>
  60. geometric_mean_per_sensor(const SensorWindow& window) {
  61. if (window.empty()) throw std::runtime_error("Window is empty");
  62.  
  63. std::array<double, NUM_SENSORS> log_sums{};
  64. log_sums.fill(0.0);
  65.  
  66. for (const auto& frame : window) {
  67. for (int s = 0; s < NUM_SENSORS; ++s) {
  68. if (frame.values[s] <= 0.0)
  69. throw std::domain_error("Geometric mean requires positive values");
  70. log_sums[s] += std::log(frame.values[s]);
  71. }
  72. }
  73.  
  74. std::array<double, NUM_SENSORS> result{};
  75. double n = static_cast<double>(window.size());
  76. for (int s = 0; s < NUM_SENSORS; ++s)
  77. result[s] = std::exp(log_sums[s] / n);
  78.  
  79. return result;
  80. }
  81.  
  82. int main() {
  83. const int N = 5; // keep last 5 measurements
  84. SensorWindow window;
  85.  
  86. // Simulate incoming sensor readings
  87. push_frame(window, {{ 1.2, 3.4, 2.1, 0.9 }}, N);
  88. push_frame(window, {{ 1.5, 3.1, 2.4, 1.0 }}, N);
  89. push_frame(window, {{ 1.1, 3.6, 2.0, 1.2 }}, N);
  90.  
  91. // Global geometric mean (single pseudo-fusion scalar)
  92. double global = geometric_mean_all(window);
  93.  
  94. // Per-sensor geometric mean (one value per sensor)
  95. auto per_sensor = geometric_mean_per_sensor(window);
  96.  
  97. return 0;
  98. }
Success #stdin #stdout 0.01s 5288KB
stdin
Standard input is empty
stdout
Standard output is empty