'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 |
