Question

J'ai un tableau de quelques millions de chiffres.

double* const data = new double (3600000);

Je dois parcourir le tableau et trouver la plage (la plus grande valeur du tableau moins la plus petite valeur). Cependant, il y a un hic. Je veux seulement trouver la plage dans laquelle les valeurs les plus petites et les plus grandes se trouvent à moins de 1 000 échantillons les unes des autres.

Il me faut donc trouver le maximum de: plage (données + 0, données + 1000), plage (données + 1, données + 1001), plage (données + 2, données + 1002), ...., plage (données + 3599000, données + 3600000).

J'espère que cela a du sens. En gros, je pourrais le faire comme ci-dessus, mais je cherche un algorithme plus efficace s'il en existe un. Je pense que l'algorithme ci-dessus est O (n), mais j'estime qu'il est possible d'optimiser. Une des idées avec laquelle je joue est de garder trace du maximum et du minimum les plus récents et de leur distance, puis de revenir en arrière si nécessaire.

Je vais coder cela en C ++, mais un algorithme sympa en pseudo-code conviendrait parfaitement. De plus, si ce numéro que j'essaie de trouver a un nom, j'aimerais savoir ce que c'est.

Merci.

Était-ce utile?

La solution

L’algorithme que vous décrivez est vraiment O (N), mais je pense que la constante est trop élevée. Une autre solution qui semble raisonnable consiste à utiliser l’algorithme O (N * log (N)) de la manière suivante:

* create sorted container (std::multiset) of first 1000 numbers
* in loop (j=1, j<(3600000-1000); ++j)
   - calculate range
   - remove from the set number which is now irrelevant (i.e. in index *j - 1* of the array)
   - add to set new relevant number  (i.e. in index *j+1000-1* of the array)

Je pense que cela devrait être plus rapide, car la constante est beaucoup plus basse.

Autres conseils

Ce type de question appartient à une branche d’algorithmes appelée algorithmes de transmission en continu. C’est l’étude des problèmes qui nécessitent non seulement une solution O (n), mais qui nécessitent également de travailler en un seul passage sur les données. les données sont entrées sous forme de flux dans l'algorithme, l'algorithme ne peut pas sauvegarder toutes les données et ensuite, elles sont définitivement perdues. l'algorithme doit obtenir des réponses sur les données, telles que le minimum ou la médiane, par exemple.

Plus précisément, vous recherchez un maximum (ou plus généralement dans la littérature - minimum) dans une fenêtre sur un flux.

Voici une présentation sur un

Je pense que les grandes lignes de la solution ressemblent à cela: maintenez la fenêtre par-dessus le flux. À chaque étape, un élément est inséré dans la fenêtre et un autre est supprimé (une fenêtre glissante). Les éléments que vous gardez réellement en mémoire ne sont pas tous les 1 000 éléments de la fenêtre, mais bien des représentants sélectionnés qui seront de bons candidats pour être le minimum (ou le maximum).

lisez l'article. c'est un peu complexe, mais après 2-3 lectures, vous pouvez vous y habituer.

C’est une bonne application de la mini-file d'attente - une file d'attente (premier entré, premier sorti = FIFO) qui peut simultanément garder la trace de l'élément minimum qu'elle contient, avec une constante amortie mises à jour de temps. Bien entendu, une file d'attente maximale est fondamentalement la même chose.

Une fois cette structure de données en place, vous pouvez considérer CurrentMax (des 1 000 derniers éléments) moins CurrentMin, la stocker en tant que BestSoFar, puis appliquer une nouvelle valeur et supprimer l'ancienne valeur, puis vérifiez à nouveau. De cette manière, continuez à mettre à jour BestSoFar jusqu'à ce que la valeur finale soit la solution à votre question. Chaque étape prend un temps constant amorti, l’ensemble est donc linéaire et l’implémentation que je connais possède une bonne constante scalaire (rapide).

Je ne connais pas de documentation sur min-queue - c'est une structure de données que j'ai créée en collaboration avec un collègue. Vous pouvez l'implémenter en effectuant un suivi interne d'une arborescence binaire contenant le moins d'éléments dans chaque sous-séquence contiguë de vos données. Cela simplifie le problème de ne copier que les données d’un seul côté de la structure.

Si vous souhaitez plus de détails, je peux essayer de vous les fournir. Je pensais écrire cette structure de données comme un document pour arxiv. Notez également que Tarjan et d’autres sont déjà parvenus à une structure plus puissante qui fonctionnerait ici, mais la mise en œuvre est beaucoup plus complexe. Vous pouvez rechercher sur Google "mindeque" pour en savoir plus sur le travail de Tarjan et al.

std::multiset<double> range;
double currentmax = 0.0;
for (int i = 0;  i < 3600000;  ++i)
{
    if (i >= 1000)
        range.erase(range.find(data[i-1000]));
    range.insert(data[i]);
    if (i >= 999)
        currentmax = max(currentmax, *range.rbegin());
}

Notez le code non testé.

Modifier: : correction d'une erreur par une.

  1. lisez les 1 000 premiers nombres.
  2. crée une liste chaînée de 1000 éléments qui suit le nombre actuel de 1000.
  3. créer un tableau de 1000 éléments de pointeurs vers les nœuds de la liste liée, mappage 1-1.
  4. trie le tableau de pointeurs en fonction des valeurs du nœud de la liste liée. Cela va réorganiser le tableau mais garder la liste liée intacte.
  5. vous pouvez maintenant calculer la plage des 1000 premiers nombres en examinant le premier et le dernier élément du tableau de pointeurs.
  6. supprime le premier élément inséré, qui correspond à la tête ou à la queue, selon la manière dont vous avez créé votre liste liée. En utilisant la valeur du nœud, effectuez une recherche binaire sur le tableau de pointeurs pour trouver le pointeur du nœud à supprimer, puis déplacez-le d'un rang pour le supprimer.
  7. ajoutez le 1001ème élément à la liste liée et insérez-y un pointeur à la position correcte dans le tableau en effectuant une étape du tri par insertion. Cela gardera le tableau trié.
  8. maintenant vous avez le min. et max. valeur des nombres compris entre 1 et 1001, et peut calculer la plage en utilisant le premier et le dernier élément du tableau de pointeurs.
  9. il devrait maintenant être évident de savoir ce que vous devez faire pour le reste du tableau.

L'algorithme doit être O (n) car la suppression et l'insertion sont délimitées par log (1e3) et tout le reste prend un temps constant.

J'ai décidé de voir quel était l'algorithme le plus efficace auquel je pouvais penser pour résoudre ce problème: utiliser le code et les temps réels. J'ai d'abord créé une solution simple, une solution permettant de suivre le min / max pour les n entrées précédentes à l'aide d'un tampon circulaire et un test harnais pour mesurer la vitesse. Dans la solution simple, chaque valeur de données est comparée à l'ensemble de valeurs min / max. Il s'agit donc de tests window_size * count (où la taille de la fenêtre dans la question d'origine est 1000 et le nombre est 3600000).

J'ai ensuite réfléchi à la manière de le rendre plus rapide. Tout d'abord, j'ai créé une solution qui utilisait une file d'attente fifo pour stocker les valeurs window_size et une liste liée pour stocker les valeurs dans l'ordre croissant, chaque nœud de la liste liée étant également un nœud de la file d'attente. Pour traiter une valeur de données, l'élément à la fin du fifo a été supprimé de la liste liée et de la file d'attente. La nouvelle valeur a été ajoutée au début de la file d'attente et une recherche linéaire a été utilisée pour trouver la position dans la liste liée. Les valeurs min et max pourraient alors être lues à partir du début et de la fin de la liste liée. C’était rapide, mais ne permettait pas une augmentation de window_size (linéairement).

J'ai donc décidé d'ajouter un arbre binaire au système pour tenter d'accélérer la partie recherche de l'algorithme. Les timings finaux pour window_size = 1000 et count = 3600000 étaient:

Simple: 106875
Quite Complex: 1218
Complex: 1219

qui était à la fois attendu et inattendu. On s’attendait à ce que l’utilisation d’une liste chaînée triée soit utile, inattendu en ce que la surcharge d’un arbre à auto-équilibrage ne compensait pas l’avantage d’une recherche plus rapide. J'ai essayé les deux derniers avec une taille de fenêtre accrue et j'ai constaté qu'ils étaient toujours presque identiques jusqu'à une taille de window de 100 000.

Ce qui prouve que la théorie des algorithmes est une chose, les implémenter en est une autre.

En tout cas, pour ceux qui sont intéressés, voici le code que j'ai écrit (il y en a un peu!):

Plage.h:

#include <algorithm>
#include <iostream>
#include <ctime>

using namespace std;

//  Callback types.
typedef void (*OutputCallback) (int min, int max);
typedef int (*GeneratorCallback) ();

//  Declarations of the test functions.
clock_t Simple (int, int, GeneratorCallback, OutputCallback);
clock_t QuiteComplex (int, int, GeneratorCallback, OutputCallback);
clock_t Complex (int, int, GeneratorCallback, OutputCallback);

main.cpp:

#include "Range.h"

int
  checksum;

//  This callback is used to get data.
int CreateData ()
{
  return rand ();
}

//  This callback is used to output the results.
void OutputResults (int min, int max)
{
  //cout << min << " - " << max << endl;
  checksum += max - min;
}

//  The program entry point.
void main ()
{
  int
    count = 3600000,
    window = 1000;

  srand (0);
  checksum = 0;
  std::cout << "Simple: Ticks = " << Simple (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
  srand (0);
  checksum = 0;
  std::cout << "Quite Complex: Ticks = " << QuiteComplex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
  srand (0);
  checksum = 0;
  std::cout << "Complex: Ticks = " << Complex (count, window, CreateData, OutputResults) << ", checksum = " << checksum << std::endl;
}

Simple.cpp:

#include "Range.h"

//  Function to actually process the data.
//  A circular buffer of min/max values for the current window is filled
//  and once full, the oldest min/max pair is sent to the output callback
//  and replaced with the newest input value. Each value inputted is 
//  compared against all min/max pairs.
void ProcessData
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output,
  int *min_buffer,
  int *max_buffer
)
{
  int
    i;

  for (i = 0 ; i < window ; ++i)
  {
    int
      value = input ();

    min_buffer [i] = max_buffer [i] = value;

    for (int j = 0 ; j < i ; ++j)
    {
      min_buffer [j] = min (min_buffer [j], value);
      max_buffer [j] = max (max_buffer [j], value);
    }
  }

  for ( ; i < count ; ++i)
  {
    int
      index = i % window;

    output (min_buffer [index], max_buffer [index]);

    int
      value = input ();

    min_buffer [index] = max_buffer [index] = value;

    for (int k = (i + 1) % window ; k != index ; k = (k + 1) % window)
    {
      min_buffer [k] = min (min_buffer [k], value);
      max_buffer [k] = max (max_buffer [k], value);
    }
  }

  output (min_buffer [count % window], max_buffer [count % window]);
}

//  A simple method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t Simple
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  int
    *min_buffer = new int [window],
    *max_buffer = new int [window];

  clock_t
    start = clock ();

  ProcessData (count, window, input, output, min_buffer, max_buffer);

  clock_t
    end = clock ();

  delete [] max_buffer;
  delete [] min_buffer;

  return end - start;
}

QuiteComplex.cpp:

#include "Range.h"

template <class T>
class Range
{
private:
  //  Class Types

  //  Node Data
  //  Stores a value and its position in various lists.
  struct Node
  {
    Node
      *m_queue_next,
      *m_list_greater,
      *m_list_lower;

    T
      m_value;
  };

public:
  //  Constructor
  //  Allocates memory for the node data and adds all the allocated
  //  nodes to the unused/free list of nodes.
  Range
  (
    int window_size
  ) :
    m_nodes (new Node [window_size]),
    m_queue_tail (m_nodes),
    m_queue_head (0),
    m_list_min (0),
    m_list_max (0),
    m_free_list (m_nodes)
  {
    for (int i = 0 ; i < window_size - 1 ; ++i)
    {
      m_nodes [i].m_list_lower = &m_nodes [i + 1];
    }

    m_nodes [window_size - 1].m_list_lower = 0;
  }

  //  Destructor
  //  Tidy up allocated data.
  ~Range ()
  {
    delete [] m_nodes;
  }

  //  Function to add a new value into the data structure.
  void AddValue
  (
    T value
  )
  {
    Node
      *node = GetNode ();

    //  clear links
    node->m_queue_next = 0;

    //  set value of node
    node->m_value = value;

    //  find place to add node into linked list
    Node
      *search;

    for (search = m_list_max ; search ; search = search->m_list_lower)
    {
      if (search->m_value < value)
      {
        if (search->m_list_greater)
        {
          node->m_list_greater = search->m_list_greater;
          search->m_list_greater->m_list_lower = node;
        }
        else
        {
          m_list_max = node;
        }

        node->m_list_lower = search;
        search->m_list_greater = node;
      }
    }

    if (!search)
    {
      m_list_min->m_list_lower = node;
      node->m_list_greater = m_list_min;
      m_list_min = node;
    }
  }

  //  Accessor to determine if the first output value is ready for use.
  bool RangeAvailable ()
  {
    return !m_free_list;
  }

  //  Accessor to get the minimum value of all values in the current window.
  T Min ()
  {
    return m_list_min->m_value;
  }

  //  Accessor to get the maximum value of all values in the current window.
  T Max ()
  {
    return m_list_max->m_value;
  }

private:
  //  Function to get a node to store a value into.
  //  This function gets nodes from one of two places:
  //    1. From the unused/free list
  //    2. From the end of the fifo queue, this requires removing the node from the list and tree
  Node *GetNode ()
  {
    Node
      *node;

    if (m_free_list)
    {
      //  get new node from unused/free list and place at head
      node = m_free_list;

      m_free_list = node->m_list_lower;

      if (m_queue_head)
      {
        m_queue_head->m_queue_next = node;
      }

      m_queue_head = node;
    }
    else
    {
      //  get node from tail of queue and place at head
      node = m_queue_tail;

      m_queue_tail = node->m_queue_next;
      m_queue_head->m_queue_next = node;
      m_queue_head = node;

      //  remove node from linked list
      if (node->m_list_lower)
      {
        node->m_list_lower->m_list_greater = node->m_list_greater;
      }
      else
      {
        m_list_min = node->m_list_greater;
      }

      if (node->m_list_greater)
      {
        node->m_list_greater->m_list_lower = node->m_list_lower;
      }
      else
      {
        m_list_max = node->m_list_lower;
      }
    }

    return node;
  }

  //  Member Data.
  Node
    *m_nodes,
    *m_queue_tail,
    *m_queue_head,
    *m_list_min,
    *m_list_max,
    *m_free_list;
};

//  A reasonable complex but more efficent method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t QuiteComplex
(
  int size,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  Range <int>
    range (window);

  clock_t
    start = clock ();

  for (int i = 0 ; i < size ; ++i)
  {   
    range.AddValue (input ());

    if (range.RangeAvailable ())
    {
      output (range.Min (), range.Max ());
    }
  }

  clock_t
    end = clock ();

  return end - start;
}

Complex.cpp:

#include "Range.h"

template <class T>
class Range
{
private:
  //  Class Types

  //  Red/Black tree node colours.
  enum NodeColour
  {
    Red,
    Black
  };

  //  Node Data
  //  Stores a value and its position in various lists and trees.
  struct Node
  {
    //  Function to get the sibling of a node.
    //  Because leaves are stored as null pointers, it must be possible
    //  to get the sibling of a null pointer. If the object is a null pointer
    //  then the parent pointer is used to determine the sibling.
    Node *Sibling
    (
      Node *parent
    )
    {
      Node
        *sibling;

      if (this)
      {
        sibling = m_tree_parent->m_tree_less == this ? m_tree_parent->m_tree_more : m_tree_parent->m_tree_less;
      }
      else
      {
        sibling = parent->m_tree_less ? parent->m_tree_less : parent->m_tree_more;
      }

      return sibling;
    }

    //  Node Members
    Node
      *m_queue_next,
      *m_tree_less,
      *m_tree_more,
      *m_tree_parent,
      *m_list_greater,
      *m_list_lower;

    NodeColour
      m_colour;

    T
      m_value;
  };

public:
  //  Constructor
  //  Allocates memory for the node data and adds all the allocated
  //  nodes to the unused/free list of nodes.
  Range
  (
    int window_size
  ) :
    m_nodes (new Node [window_size]),
    m_queue_tail (m_nodes),
    m_queue_head (0),
    m_tree_root (0),
    m_list_min (0),
    m_list_max (0),
    m_free_list (m_nodes)
  {
    for (int i = 0 ; i < window_size - 1 ; ++i)
    {
      m_nodes [i].m_list_lower = &m_nodes [i + 1];
    }

    m_nodes [window_size - 1].m_list_lower = 0;
  }

  //  Destructor
  //  Tidy up allocated data.
  ~Range ()
  {
    delete [] m_nodes;
  }

  //  Function to add a new value into the data structure.
  void AddValue
  (
    T value
  )
  {
    Node
      *node = GetNode ();

    //  clear links
    node->m_queue_next = node->m_tree_more = node->m_tree_less = node->m_tree_parent = 0;

    //  set value of node
    node->m_value = value;

    //  insert node into tree
    if (m_tree_root)
    {
      InsertNodeIntoTree (node);
      BalanceTreeAfterInsertion (node);
    }
    else
    {
      m_tree_root = m_list_max = m_list_min = node;
      node->m_tree_parent = node->m_list_greater = node->m_list_lower = 0;
    }

    m_tree_root->m_colour = Black;
  }

  //  Accessor to determine if the first output value is ready for use.
  bool RangeAvailable ()
  {
    return !m_free_list;
  }

  //  Accessor to get the minimum value of all values in the current window.
  T Min ()
  {
    return m_list_min->m_value;
  }

  //  Accessor to get the maximum value of all values in the current window.
  T Max ()
  {
    return m_list_max->m_value;
  }

private:
  //  Function to get a node to store a value into.
  //  This function gets nodes from one of two places:
  //    1. From the unused/free list
  //    2. From the end of the fifo queue, this requires removing the node from the list and tree
  Node *GetNode ()
  {
    Node
      *node;

    if (m_free_list)
    {
      //  get new node from unused/free list and place at head
      node = m_free_list;

      m_free_list = node->m_list_lower;

      if (m_queue_head)
      {
        m_queue_head->m_queue_next = node;
      }

      m_queue_head = node;
    }
    else
    {
      //  get node from tail of queue and place at head
      node = m_queue_tail;

      m_queue_tail = node->m_queue_next;
      m_queue_head->m_queue_next = node;
      m_queue_head = node;

      //  remove node from tree
      node = RemoveNodeFromTree (node);
      RebalanceTreeAfterDeletion (node);

      //  remove node from linked list
      if (node->m_list_lower)
      {
        node->m_list_lower->m_list_greater = node->m_list_greater;
      }
      else
      {
        m_list_min = node->m_list_greater;
      }

      if (node->m_list_greater)
      {
        node->m_list_greater->m_list_lower = node->m_list_lower;
      }
      else
      {
        m_list_max = node->m_list_lower;
      }
    }

    return node;
  }

  //  Rebalances the tree after insertion
  void BalanceTreeAfterInsertion
  (
    Node *node
  )
  {
    node->m_colour = Red;

    while (node != m_tree_root && node->m_tree_parent->m_colour == Red)
    {
      if (node->m_tree_parent == node->m_tree_parent->m_tree_parent->m_tree_more)
      {
        Node
          *uncle = node->m_tree_parent->m_tree_parent->m_tree_less;

        if (uncle && uncle->m_colour == Red)
        {
          node->m_tree_parent->m_colour = Black;
          uncle->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          node = node->m_tree_parent->m_tree_parent;
        }
        else
        {
          if (node == node->m_tree_parent->m_tree_less)
          {
            node = node->m_tree_parent;
            LeftRotate (node);
          }

          node->m_tree_parent->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          RightRotate (node->m_tree_parent->m_tree_parent);
        }
      }
      else
      {
        Node
          *uncle = node->m_tree_parent->m_tree_parent->m_tree_more;

        if (uncle && uncle->m_colour == Red)
        {
          node->m_tree_parent->m_colour = Black;
          uncle->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          node = node->m_tree_parent->m_tree_parent;
        }
        else
        {
          if (node == node->m_tree_parent->m_tree_more)
          {
            node = node->m_tree_parent;
            RightRotate (node);
          }

          node->m_tree_parent->m_colour = Black;
          node->m_tree_parent->m_tree_parent->m_colour = Red;
          LeftRotate (node->m_tree_parent->m_tree_parent);
        }
      }
    }
  }

  //  Adds a node into the tree and sorted linked list
  void InsertNodeIntoTree
  (
    Node *node
  )
  {
    Node
      *parent = 0,
      *child = m_tree_root;

    bool
      greater;

    while (child)
    {
      parent = child;
      child = (greater = node->m_value > child->m_value) ? child->m_tree_more : child->m_tree_less;
    }

    node->m_tree_parent = parent;

    if (greater)
    {
      parent->m_tree_more = node;

      //  insert node into linked list
      if (parent->m_list_greater)
      {
        parent->m_list_greater->m_list_lower = node;
      }
      else
      {
        m_list_max = node;
      }

      node->m_list_greater = parent->m_list_greater;
      node->m_list_lower = parent;
      parent->m_list_greater = node;
    }
    else
    {
      parent->m_tree_less = node;

      //  insert node into linked list
      if (parent->m_list_lower)
      {
        parent->m_list_lower->m_list_greater = node;
      }
      else
      {
        m_list_min = node;
      }

      node->m_list_lower = parent->m_list_lower;
      node->m_list_greater = parent;
      parent->m_list_lower = node;
    }
  }

  //  Red/Black tree manipulation routine, used for removing a node
  Node *RemoveNodeFromTree
  (
    Node *node
  )
  {
    if (node->m_tree_less && node->m_tree_more)
    {
      //  the complex case, swap node with a child node
      Node
        *child;

      if (node->m_tree_less)
      {
        // find largest value in lesser half (node with no greater pointer)
        for (child = node->m_tree_less ; child->m_tree_more ; child = child->m_tree_more)
        {
        }
      }
      else
      {
        // find smallest value in greater half (node with no lesser pointer)
        for (child = node->m_tree_more ; child->m_tree_less ; child = child->m_tree_less)
        {
        }
      }

      swap (child->m_colour, node->m_colour);

      if (child->m_tree_parent != node)
      {
        swap (child->m_tree_less, node->m_tree_less);
        swap (child->m_tree_more, node->m_tree_more);
        swap (child->m_tree_parent, node->m_tree_parent);

        if (!child->m_tree_parent)
        {
          m_tree_root = child;
        }
        else
        {
          if (child->m_tree_parent->m_tree_less == node)
          {
            child->m_tree_parent->m_tree_less = child;
          }
          else
          {
            child->m_tree_parent->m_tree_more = child;
          }
        }

        if (node->m_tree_parent->m_tree_less == child)
        {
          node->m_tree_parent->m_tree_less = node;
        }
        else
        {
          node->m_tree_parent->m_tree_more = node;
        }
      }
      else
      {
        child->m_tree_parent = node->m_tree_parent;
        node->m_tree_parent = child;

        Node
          *child_less = child->m_tree_less,
          *child_more = child->m_tree_more;

        if (node->m_tree_less == child)
        {
          child->m_tree_less = node;
          child->m_tree_more = node->m_tree_more;
          node->m_tree_less = child_less;
          node->m_tree_more = child_more;
        }
        else
        {
          child->m_tree_less = node->m_tree_less;
          child->m_tree_more = node;
          node->m_tree_less = child_less;
          node->m_tree_more = child_more;
        }

        if (!child->m_tree_parent)
        {
          m_tree_root = child;
        }
        else
        {
          if (child->m_tree_parent->m_tree_less == node)
          {
            child->m_tree_parent->m_tree_less = child;
          }
          else
          {
            child->m_tree_parent->m_tree_more = child;
          }
        }
      }

      if (child->m_tree_less)
      {
        child->m_tree_less->m_tree_parent = child;
      }

      if (child->m_tree_more)
      {
        child->m_tree_more->m_tree_parent = child;
      }

      if (node->m_tree_less)
      {
        node->m_tree_less->m_tree_parent = node;
      }

      if (node->m_tree_more)
      {
        node->m_tree_more->m_tree_parent = node;
      }
    }

    Node
      *child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;

    if (node->m_tree_parent->m_tree_less == node)
    {
      node->m_tree_parent->m_tree_less = child;
    }
    else
    {
      node->m_tree_parent->m_tree_more = child;
    }

    if (child)
    {
      child->m_tree_parent = node->m_tree_parent;
    }

    return node;
  }

  //  Red/Black tree manipulation routine, used for rebalancing a tree after a deletion
  void RebalanceTreeAfterDeletion
  (
    Node *node
  )
  {
    Node
      *child = node->m_tree_less ? node->m_tree_less : node->m_tree_more;

    if (node->m_colour == Black)
    {
      if (child && child->m_colour == Red)
      {
        child->m_colour = Black;
      }
      else
      {
        Node
          *parent = node->m_tree_parent,
          *n = child;

        while (parent)
        {
          Node
            *sibling = n->Sibling (parent);

          if (sibling && sibling->m_colour == Red)
          {
            parent->m_colour = Red;
            sibling->m_colour = Black;

            if (n == parent->m_tree_more)
            {
              LeftRotate (parent);
            }
            else
            {
              RightRotate (parent);
            }
          }

          sibling = n->Sibling (parent);

          if (parent->m_colour == Black &&
            sibling->m_colour == Black &&
            (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
            (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
          {
            sibling->m_colour = Red;
            n = parent;
            parent = n->m_tree_parent;
            continue;
          }
          else
          {
            if (parent->m_colour == Red &&
              sibling->m_colour == Black &&
              (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
              (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
            {
              sibling->m_colour = Red;
              parent->m_colour = Black;
              break;
            }
            else
            {
              if (n == parent->m_tree_more &&
                sibling->m_colour == Black &&
                (sibling->m_tree_more && sibling->m_tree_more->m_colour == Red) &&
                (!sibling->m_tree_less || sibling->m_tree_less->m_colour == Black))
              {
                sibling->m_colour = Red;
                sibling->m_tree_more->m_colour = Black;
                RightRotate (sibling);
              }
              else
              {
                if (n == parent->m_tree_less &&
                  sibling->m_colour == Black &&
                  (!sibling->m_tree_more || sibling->m_tree_more->m_colour == Black) &&
                  (sibling->m_tree_less && sibling->m_tree_less->m_colour == Red))
                {
                  sibling->m_colour = Red;
                  sibling->m_tree_less->m_colour = Black;
                  LeftRotate (sibling);
                }
              }

              sibling = n->Sibling (parent);
              sibling->m_colour = parent->m_colour;
              parent->m_colour = Black;

              if (n == parent->m_tree_more)
              {
                sibling->m_tree_less->m_colour = Black;
                LeftRotate (parent);
              }
              else
              {
                sibling->m_tree_more->m_colour = Black;
                RightRotate (parent);
              }
              break;
            }
          }
        }
      }
    }
  }

  //  Red/Black tree manipulation routine, used for balancing the tree
  void LeftRotate
  (
    Node *node
  )
  {
    Node
      *less = node->m_tree_less;

    node->m_tree_less = less->m_tree_more;

    if (less->m_tree_more)
    {
      less->m_tree_more->m_tree_parent = node;
    }

    less->m_tree_parent = node->m_tree_parent;

    if (!node->m_tree_parent)
    {
      m_tree_root = less;
    }
    else
    {
      if (node == node->m_tree_parent->m_tree_more)
      {
        node->m_tree_parent->m_tree_more = less;
      }
      else
      {
        node->m_tree_parent->m_tree_less = less;
      }
    }

    less->m_tree_more = node;
    node->m_tree_parent = less;
  }

  //  Red/Black tree manipulation routine, used for balancing the tree
  void RightRotate
  (
    Node *node
  )
  {
    Node
      *more = node->m_tree_more;

    node->m_tree_more = more->m_tree_less;

    if (more->m_tree_less)
    {
      more->m_tree_less->m_tree_parent = node;
    }

    more->m_tree_parent = node->m_tree_parent;

    if (!node->m_tree_parent)
    {
      m_tree_root = more;
    }
    else
    {
      if (node == node->m_tree_parent->m_tree_less)
      {
        node->m_tree_parent->m_tree_less = more;
      }
      else
      {
        node->m_tree_parent->m_tree_more = more;
      }
    }

    more->m_tree_less = node;
    node->m_tree_parent = more;
  }

  //  Member Data.
  Node
    *m_nodes,
    *m_queue_tail,
    *m_queue_head,
    *m_tree_root,
    *m_list_min,
    *m_list_max,
    *m_free_list;
};

//  A complex but more efficent method of calculating the results.
//  Memory management is done here outside of the timing portion.
clock_t Complex
(
  int count,
  int window,
  GeneratorCallback input,
  OutputCallback output
)
{
  Range <int>
    range (window);

  clock_t
    start = clock ();

  for (int i = 0 ; i < count ; ++i)
  {   
    range.AddValue (input ());

    if (range.RangeAvailable ())
    {
      output (range.Min (), range.Max ());
    }
  }

  clock_t
    end = clock ();

  return end - start;
}

Idée de l'algorithme:

Prenez les 1000 premières valeurs de données et triez-les
Le dernier dans le tri - le premier est la plage (données + 0, données + 999).
Retirez ensuite de la pile de tri le premier élément contenant les données de valeur [0]
et ajoutez les données d'élément [1000]
Maintenant, le dernier dans le tri - le premier est la plage (données + 1, données + 1000).
Répéter jusqu'à la fin

// This should run in (DATA_LEN - RANGE_WIDTH)log(RANGE_WIDTH)
#include <set>
#include <algorithm>
using namespace std;

const int DATA_LEN = 3600000;
double* const data = new double (DATA_LEN);

....
....

const int RANGE_WIDTH = 1000;
double range = new double(DATA_LEN - RANGE_WIDTH);
multiset<double> data_set;
data_set.insert(data[i], data[RANGE_WIDTH]);

for (int i = 0 ; i < DATA_LEN - RANGE_WIDTH - 1 ; i++)
{
   range[i] = *data_set.end() - *data_set.begin();
   multiset<double>::iterator iter = data_set.find(data[i]);
   data_set.erase(iter);
   data_set.insert(data[i+1]);
}
range[i] = *data_set.end() - *data_set.begin();

// range now holds the values you seek

Vous devriez probablement vérifier ceci avec une erreur, mais l’idée est là.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top