'C# Boolean Window Most Common [closed]

I'm looking to create a moving window that stores boolean values and outputs the most common boolean value.

I want to be able to add new values on the fly, for example:

bool[] window = { false, false, true, true, true };

New 'false' value added, array shifted:

bool[] window = { false, true, true, true, false };

Expected output would be 'true'.

What is the best/most efficient way to go about this? should I use LINQ? any examples would be much appreciated. Thank you.



Solution 1:[1]

Here's a version which has O(1) insertion and read, unlike Joel's which has O(N) read.

public class BoolWindowedMode
{
    private readonly bool[] history;
    private int used;
    private int count;
    private int rotation;

    public BoolWindow(int width)
    { history = new bool[width]; }

    public void Insert(bool newValue)
    {
        // remove old entry from count
        if (history[rotation]) --count;
        // count new entry
        if (newValue) ++count;
        // replace old entry in history, shifting
        history[rotation] = newValue;
        if (++rotation >= history.Length) rotation = 0;
        if (used < history.Length) ++used;
    }

    public int CountOfTrue => count;
    public int CountOfFalse => used - count;
    public bool Mode => count > used - count;
}

If you only need "correct" results once enough values are inserted to fill the window, then you can eliminate the used variable.

Solution 2:[2]

public class BoolWindow : IEnumerable<bool>
{
    private static int MaxSize = 5;
    private Queue<bool> data = new Queue<bool>(MaxSize);

    public void Add(bool newValue)
    {
        if (data.Count >= MaxSize) data.Dequeue();
        data.Enqueue(newValue);
    }

    public bool MostFrequentValue()
    {
        //What do you want if the size is even and both true and false are the same?
        return data.Select(b => b?1:-1).Sum() > 0;

        // Also: we could optimize this by looping manually until one value
        // is > Count/2, but that's probably more trouble than it's worth
    }    

    public IEnumerator<bool> GetEnumerator()
    {
       return data.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

Solution 3:[3]

You can indeed use a Queue<bool> like already suggested. But it might cause a performance issue in some cases. After the 5 first insertions, every insertion will cost a Dequeue and an Enqueue.

Efficiency depends on the implementation of .NET which I am not an expert in, but it could be sub-optimal. Assuming the Queue is at its capacity - Dequeue can just increment the head index, but then Enqueue will require a reallocation. If Dequeue is not just incrementing the index it might require to copy the whole Queue.

Since Queue is not limited to a predefined window size, its implementation might be inferior relative to a specific cyclic queue implementation.

Therefore - if efficiency is critical, you can try to use a fixed size array, and manage a cyclic queue on top of it.

BTW - If the number if elements is even (e.g. before we got 5 entries) and we have the same number of true and false, it is not clear what the most common value should be. I arbitrarily chose false.

The implementation:

public class CyclicQueueBool 
{
    public CyclicQueueBool(int maxSize)
    {
        int internalSize = maxSize + 1;
        m_Queue_ = new bool[internalSize];
        m_Head_ = 0;
        m_Tail_ = 0;
    }

    // Get the actual number of elements in the queue (smaller than MaxSize() in case it is not full)
    public int NumberOfActualElements()
    {
        if (m_Head_ >= m_Tail_)
            return (int)m_Head_ - (int)m_Tail_;
        int maxSize = m_Queue_.Length - 1;
        return (int)(maxSize - m_Tail_ + m_Head_ + 1);
    }

    // Check if the queue is empty or full:
    public bool IsEmpty() { return (m_Head_ == m_Tail_); }
    public bool IsFull() { return (_Next(m_Head_) == m_Tail_); }

    // Push a new element to the queue. If the queue is full the oldest element is discarded to keep the size.
    public void Push(bool elem)
    {
        if (IsFull())
        {
            m_Tail_ = _Next(m_Tail_);
        }
        m_Queue_[(int)(m_Head_)] = elem;
        m_Head_ = _Next(m_Head_);
    }

    // Access element by index:
    // NOTE:    Q[0]                is Tail() (i.e. the oldest)
    //          Q[NumElements-1]    is Head() (i.e. the newest)
    public bool this[int index]
    {
        get { return m_Queue_[(int)((index + m_Tail_) % m_Queue_.Length)]; }
        set { m_Queue_[(int)((index + m_Tail_) % m_Queue_.Length)] = value; }
    }

    // Get common value:
    public bool GetCommonValue()
    {
        int numTrue = 0;
        int numFalse = 0;
        int numElems = this.NumberOfActualElements();
        for (int i = 0; i < numElems; ++i)
        {
            if (this[i])
            {
                numTrue++;
            }
            else
            {
                numFalse++;
            }
        }
        return (numTrue > numFalse);
    }
    

    protected int _Next(int i) { return (i + 1) % m_Queue_.Length; }
    protected int _Prev(int i) { return (i + m_Queue_.Length - 1) % m_Queue_.Length; }

    protected bool[] m_Queue_;
    protected int m_Head_;
    protected int m_Tail_;
}



class Program
{
    static void Main(string[] args)
    {
        CyclicQueueBool q = new CyclicQueueBool(5);
        q.Push(false);
        q.Push(true);
        q.Push(false);
        q.Push(false);
        q.Push(true);
        Console.WriteLine("GetCommonValue: " + q.GetCommonValue());
    }
}

UPDATE: based on @Ben Voigt's comment I replaced List<bool> in the implementation with bool[].

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3