2010-04-08 9 views
22

Estoy buscando una implementación .Net de un multiset. ¿Alguien puede recomendar una buena?¿Hay implementaciones de multiset para .Net?

(Un multiset, o bolsa, es un conjunto que puede tener valores duplicados, y en el que puede realizar operaciones de conjunto: intersección, diferencia, etc. Un carrito de compras, por ejemplo, podría considerarse un multiset porque puede tener múltiples ocurrencias del mismo producto.)

+0

favor ver: [? Colección # conjunto C] (http://stackoverflow.com/questions/183685/c-set-collection) –

+0

Gracias. Un par de carteles mencionaron Wintellect Power Collections, que tiene un tipo de bolsa . Se ve bastante bien. –

+0

También está el C5, pero no creo que implemente operaciones de conjunto. –

Respuesta

4

No conozco ninguno, sin embargo puede usar un Dictionary para eso, en el cual el valor es la cantidad del artículo. Y cuando el artículo se agrega por segunda vez, debe aumentar el valor en el diccionario.

Otra posibilidad sería simplemente utilizar un List de elementos, en los que podría poner duplicados. Este podría ser un mejor enfoque para un carrito de compras.

+0

Buena idea. Pero necesito ser capaz de encontrar la diferencia entre dos conjuntos de manera eficiente. Si yo rodé el mío, tendría que esforzarme mucho para asegurarme de que tuviera esa propiedad. Así que preferiría no hacer eso. –

+0

Tu idea de diccionarios con conteos ha crecido en mí. Creo que funcionaría bien si sus artículos tienen una propiedad Count (que tienen en mi caso) en lugar de ser valores discretos. La diferencia establecida debe ser O (N). Un multiset sería mejor si tiene valores discretos. –

1
public class Multiset<T>: ICollection<T> 
{ 
    private readonly Dictionary<T, int> data; 

    public Multiset() 
    { 
     data = new Dictionary<T, int>(); 
    } 

    private Multiset(Dictionary<T, int> data) 
    { 
     this.data = data; 
    } 

    public void Add(T item) 
    { 
     int count = 0; 
     data.TryGetValue(item, out count); 
     count++; 
     data[item] = count; 
    } 

    public void Clear() 
    { 
     data.Clear(); 
    } 

    public Multiset<T> Except(Multiset<T> another) 
    { 
     Multiset<T> copy = new Multiset<T>(new Dictionary<T, int>(data)); 
     foreach (KeyValuePair<T, int> kvp in another.data) 
     { 
      int count; 
      if (copy.data.TryGetValue(kvp.Key, out count)) 
      { 
       if (count > kvp.Value) 
       { 
        copy.data[kvp.Key] = count - kvp.Value; 
       } 
       else 
       { 
        copy.data.Remove(kvp.Key); 
       } 
      } 
     } 
     return copy; 
    } 

    public Multiset<T> Intersection(Multiset<T> another) 
    { 
     Dictionary<T, int> newData = new Dictionary<T, int>(); 
     foreach (T t in data.Keys.Intersect(another.data.Keys)) 
     { 
      newData[t] = Math.Min(data[t], another.data[t]); 
     } 
     return new Multiset<T>(newData); 
    } 

    public bool Contains(T item) 
    { 
     return data.ContainsKey(item); 
    } 

    public void CopyTo(T[] array, int arrayIndex) 
    { 
     foreach (KeyValuePair<T, int> kvp in data) 
     { 
      for (int i = 0; i < kvp.Value; i++) 
      { 
       array[arrayIndex] = kvp.Key; 
       arrayIndex++; 
      } 
     } 
    } 

    public IEnumerable<T> Mode() 
    { 
     if (!data.Any()) 
     { 
      return Enumerable.Empty<T>(); 
     } 
     int modalFrequency = data.Values.Max(); 
     return data.Where(kvp => kvp.Value == modalFrequency).Select(kvp => kvp.Key); 
    } 

    public int Count 
    { 
     get 
     { 
      return data.Values.Sum(); 
     } 
    } 

    public bool IsReadOnly 
    { 
     get 
     { 
      return false; 
     } 
    } 

    public bool Remove(T item) 
    { 
     int count; 
     if (!data.TryGetValue(item, out count)) 
     { 
      return false; 
     } 
     count--; 
     if (count == 0) 
     { 
      data.Remove(item); 
     } 
     else 
     { 
      data[item] = count; 
     } 
     return true; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     return new MultisetEnumerator<T>(this); 
    } 

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 
    { 
     return new MultisetEnumerator<T>(this); 
    } 

    private class MultisetEnumerator<T> : IEnumerator<T> 
    { 
     public MultisetEnumerator(Multiset<T> multiset) 
     { 
      this.multiset = multiset; 
      baseEnumerator = multiset.data.GetEnumerator(); 
      index = 0; 
     } 

     private readonly Multiset<T> multiset; 
     private readonly IEnumerator<KeyValuePair<T, int>> baseEnumerator; 
     private int index; 

     public T Current 
     { 
      get 
      { 
       return baseEnumerator.Current.Key; 
      } 
     } 

     public void Dispose() 
     { 
      baseEnumerator.Dispose(); 
     } 

     object System.Collections.IEnumerator.Current 
     { 
      get 
      { 
       return baseEnumerator.Current.Key; 
      } 
     } 

     public bool MoveNext() 
     { 
      KeyValuePair<T, int> kvp = baseEnumerator.Current; 
      if (index < (kvp.Value - 1)) 
      { 
       index++; 
       return true; 
      } 
      else 
      { 
       bool result = baseEnumerator.MoveNext(); 
       index = 0; 
       return result; 
      } 
     } 

     public void Reset() 
     { 
      baseEnumerator.Reset(); 
     } 
    } 
} 
+0

Tema viejo, respuesta anterior, sí, sí, lo sé. De todos modos, tienes un argumento de plantilla anidada en tu clase de enumerador. No necesitas eso. Puede usar 'clase privada MultisetEnumerator: IEnumerator ', ya que T ya está definido en el alcance de la clase interna. – Arshia001

3

Cualquier cosa que se llame a sí misma una implementación de C# de un multiset no se debe basar internamente en un diccionario. Los diccionarios son tablas hash, colecciones desordenadas. Se ordenan los conjuntos, multisedes, mapas y multimapas de C++. Internamente, cada uno se representa como un sabor de un árbol de búsqueda binaria autoequilibrante.

En C# deberíamos utilizar un SortedDictionary como base de nuestra implementación, ya que según la propia documentación de Microsoft, SortedDictionary "is a binary search tree with O(log n) retrieval". Un conjunto múltiple básica se puede implementar de la siguiente manera:

public class SortedMultiSet<T> : IEnumerable<T> 
{ 
    private SortedDictionary<T, int> _dict; 

    public SortedMultiSet() 
    { 
     _dict = new SortedDictionary<T, int>(); 
    } 

    public SortedMultiSet(IEnumerable<T> items) : this() 
    { 
     Add(items); 
    } 

    public bool Contains(T item) 
    { 
     return _dict.ContainsKey(item); 
    } 

    public void Add(T item) 
    { 
     if (_dict.ContainsKey(item)) 
      _dict[item]++; 
     else 
      _dict[item] = 1; 
    } 

    public void Add(IEnumerable<T> items) 
    { 
     foreach (var item in items) 
      Add(item); 
    } 

    public void Remove(T item) 
    { 
     if (!_dict.ContainsKey(item)) 
      throw new ArgumentException(); 
     if (--_dict[item] == 0) 
      _dict.Remove(item); 
    } 

    // Return the last value in the multiset 
    public T Peek() 
    { 
     if (!_dict.Any()) 
      throw new NullReferenceException(); 
     return _dict.Last().Key; 
    } 

    // Return the last value in the multiset and remove it. 
    public T Pop() 
    { 
     T item = Peek(); 
     Remove(item); 
     return item; 
    } 

    public IEnumerator<T> GetEnumerator() 
    { 
     foreach(var kvp in _dict) 
      for(int i = 0; i < kvp.Value; i++) 
       yield return kvp.Key; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 
+1

Excepto que no puede ir al elemento siguiente/anterior después de encontrar uno, y no hay ['equal_range'] (http://en.cppreference.com/w/cpp/algorithm/equal_range). Aquí es donde C++ '(multi_) set' y' (multi_) map' brillan, puede encontrar rápidamente el principio y el final de un rango y procesar todo lo que esté en el rango. – doug65536

+0

¿Cuál es la motivación para ordenar un multiset? El concepto matemático no está ordenado. Un 'Diccionario' no está ordenado, pero ¿y qué? –

+0

la motivación para ordenar un multiset es que la estructura de datos de la biblioteca estándar de C++ std :: multiset está ordenada, de modo que cuando muchas personas escuchan que alguien está buscando una implementación .Net de "multiset", ese nombre exacto es sonidos como si estuvieran pidiendo una implementación de .Net de std :: multiset que debería pedirse. – jwezorek

Cuestiones relacionadas