Concise, universal A* search in C# [closed] - c#

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I was looking for an A* search implementation in C#. Eventually wrote my own. Because it's universal, I hope it will be useful for others.

To use the algorithm, inherit a class from AStar<T> and override Neighbors, Cost and Heuristic functions to define your search domain. Type T defines a location in the search domain (for example Point when searching on a 2D grid). Then call FindPath on your class.
You may also optionally override StorageClear, StorageGet and StorageAdd to provide optimized per-location data storage for A*. The default implementation uses a Dictionary<T, object>, which is reasonably fast, but can be improved upon in many specific cases. If not overriding storage, make sure that type T is equatable (e.g. by implementing IEquatable<T>), otherwise dictionary lookups will be very slow.
See comments for more details.
public abstract class AStar<T>
{
#region Fields
private class Node
{
public T Position;
public T PreviousPosition;
public float F, G, H;
public bool IsClosed;
}
private int m_nodesCacheIndex;
private List<Node> m_nodesCache = new List<Node>();
private List<Node> m_openHeap = new List<Node>();
private List<T> m_neighbors = new List<T>();
private Dictionary<T, object> m_defaultStorage;
#endregion
#region Domain Definition
// User must override Neighbors, Cost and Heuristic functions to define search domain.
// It is optional to override StorageClear, StorageGet and StorageAdd functions.
// Default implementation of these storage functions uses a Dictionary<T, object>, this works for all possible search domains.
// A domain-specific storage algorihm may be significantly faster.
// For example if searching on a finite 2D or 3D grid, storage using fixed array with each element representing one world point benchmarks an order of magnitude faster.
/// <summary>
/// Return all neighbors of the given point.
/// Must be overridden.
/// </summary>
/// <param name="p">Point to return neighbors for</param>
/// <param name="neighbors">Empty collection to fill with neighbors</param>
protected abstract void Neighbors(T p, List<T> neighbors);
/// <summary>
/// Return cost of making a step from p1 to p2 (which are neighbors).
/// Cost equal to float.PositiveInfinity indicates that passage from p1 to p2 is impossible.
/// Must be overridden.
/// </summary>
/// <param name="p1">Start point</param>
/// <param name="p2">End point</param>
/// <returns>Cost value</returns>
protected abstract float Cost(T p1, T p2);
/// <summary>
/// Return an estimate of cost of moving from p to nearest goal.
/// Must return 0 when p is goal.
/// This is an estimate of sum of all costs along the best path between p and the nearest goal.
/// This should not overestimate the actual cost; if it does, the result of A* might not be an optimal path.
/// Underestimating the cost is allowed, but may cause the algorithm to check more positions.
/// Must be overridden.
/// </summary>
/// <param name="p">Point to estimate cost from</param>
/// <returns>Cost value</returns>
protected abstract float Heuristic(T p);
/// <summary>
/// Clear A* storage.
/// This will be called every time before a search starts and before any other user functions are called.
/// Optional override when using domain-optimized storage.
/// </summary>
protected virtual void StorageClear()
{
if (m_defaultStorage == null)
{
m_defaultStorage = new Dictionary<T, object>();
}
else
{
m_defaultStorage.Clear();
}
}
/// <summary>
/// Retrieve data from storage at given point.
/// Optional override when using domain-optimized storage.
/// </summary>
/// <param name="p">Point to retrieve data at</param>
/// <returns>Data stored for point p or null if nothing stored</returns>
protected virtual object StorageGet(T p)
{
object data;
m_defaultStorage.TryGetValue(p, out data);
return data;
}
/// <summary>
/// Add data to storage at given point.
/// There will never be any data already stored at that point.
/// Optional override when using domain-optimized storage.
/// </summary>
/// <param name="p">Point to add data at</param>
/// <param name="data">Data to add</param>
protected virtual void StorageAdd(T p, object data)
{
m_defaultStorage.Add(p, data);
}
#endregion
#region Public Interface
/// <summary>
/// Find best path from start to nearest goal.
/// Goal is any point for which Heuristic override returns 0.
/// If maxPositionsToCheck limit is reached, best path found so far is returned.
/// If there is no path to goal, path to a point nearest to goal is returned instead.
/// </summary>
/// <param name="path">Path will contain steps to reach goal from start in reverse order (first step at the end of collection)</param>
/// <param name="start">Starting point to search for path</param>
/// <param name="maxPositionsToCheck">Maximum number of positions to check</param>
/// <returns>True when path to goal was found, false if partial path only</returns>
public bool FindPath(ICollection<T> path, T start, int maxPositionsToCheck = int.MaxValue)
{
// Check arguments
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
// Reset cache and storage
path.Clear();
m_nodesCacheIndex = 0;
m_openHeap.Clear();
StorageClear();
// Put start node
Node startNode = NewNode(start, default(T), 0, 0);
StorageAdd(start, startNode);
HeapEnqueue(startNode);
// Astar loop
Node bestNode = null;
int checkedPositions = 0;
while (true)
{
// Get next node from heap
Node currentNode = m_openHeap.Count > 0 ? HeapDequeue() : null;
// Check end conditions
if (currentNode == null || checkedPositions >= maxPositionsToCheck)
{
// No more nodes or limit reached, path not found, return path to best node if possible
if (bestNode != null)
{
BuildPathFromEndNode(path, startNode, bestNode);
}
return false;
}
else if (Heuristic(currentNode.Position) <= 0)
{
// Node is goal, return path
BuildPathFromEndNode(path, startNode, currentNode);
return true;
}
// Remember node with best heuristic; ignore start node
if (currentNode != startNode && (bestNode == null || currentNode.H < bestNode.H))
{
bestNode = currentNode;
}
// Move current node from open to closed in the storage
currentNode.IsClosed = true;
++checkedPositions;
// Try all neighbors
m_neighbors.Clear();
Neighbors(currentNode.Position, m_neighbors);
for (int i = 0; i < m_neighbors.Count; ++i)
{
// Get position
T position = m_neighbors[i];
// Get existing node at position, if any
Node node = (Node)StorageGet(position);
// If position was already analyzed, ignore step
if (node != null && node.IsClosed == true)
{
continue;
}
// If position is not passable, ignore step
float cost = Cost(currentNode.Position, position);
if (cost == float.PositiveInfinity)
{
continue;
}
// Calculate A* values
float g = currentNode.G + cost;
float h = Heuristic(position);
// Update or create new node at position
if (node != null)
{
// Update existing node if better
if (g < node.G)
{
node.G = g;
node.F = g + node.H;
node.PreviousPosition = currentNode.Position;
HeapUpdate(node);
}
}
else
{
// Create new open node if not yet exists
node = NewNode(position, currentNode.Position, g, h);
StorageAdd(position, node);
HeapEnqueue(node);
}
}
}
}
#endregion
#region Internals
private void BuildPathFromEndNode(ICollection<T> path, Node startNode, Node endNode)
{
for (Node node = endNode; node != startNode; node = (Node)StorageGet(node.PreviousPosition))
{
path.Add(node.Position);
}
}
private void HeapEnqueue(Node node)
{
m_openHeap.Add(node);
HeapifyFromPosToStart(m_openHeap.Count - 1);
}
private Node HeapDequeue()
{
Node result = m_openHeap[0];
if (m_openHeap.Count <= 1)
{
m_openHeap.Clear();
}
else
{
m_openHeap[0] = m_openHeap[m_openHeap.Count - 1];
m_openHeap.RemoveAt(m_openHeap.Count - 1);
HeapifyFromPosToEnd(0);
}
return result;
}
private void HeapUpdate(Node node)
{
int pos = -1;
for (int i = 0; i < m_openHeap.Count; ++i)
{
if (m_openHeap[i] == node)
{
pos = i;
break;
}
}
HeapifyFromPosToStart(pos);
}
private void HeapifyFromPosToEnd(int pos)
{
while (true)
{
int smallest = pos;
int left = 2 * pos + 1;
int right = 2 * pos + 2;
if (left < m_openHeap.Count && m_openHeap[left].F < m_openHeap[smallest].F)
smallest = left;
if (right < m_openHeap.Count && m_openHeap[right].F < m_openHeap[smallest].F)
smallest = right;
if (smallest != pos)
{
Node tmp = m_openHeap[smallest];
m_openHeap[smallest] = m_openHeap[pos];
m_openHeap[pos] = tmp;
pos = smallest;
}
else
{
break;
}
}
}
private void HeapifyFromPosToStart(int pos)
{
int childPos = pos;
while (childPos > 0)
{
int parentPos = (childPos - 1) / 2;
Node parentNode = m_openHeap[parentPos];
Node childNode = m_openHeap[childPos];
if (parentNode.F > childNode.F)
{
m_openHeap[parentPos] = childNode;
m_openHeap[childPos] = parentNode;
childPos = parentPos;
}
else
{
break;
}
}
}
private Node NewNode(T position, T previousPosition, float g, float h)
{
while (m_nodesCacheIndex >= m_nodesCache.Count)
{
m_nodesCache.Add(new Node());
}
Node node = m_nodesCache[m_nodesCacheIndex++];
node.Position = position;
node.PreviousPosition = previousPosition;
node.F = g + h;
node.G = g;
node.H = h;
node.IsClosed = false;
return node;
}
#endregion
}

Related

My Quicksort Algorithm only works with First Index as the Pivot

I've started working on Sort Algorithms and got Quicksort working with the Activation Method as the first index for the pivot.
Now, if i try to use either a random index or the the last one it either just doesn't do anything or it just sorts the last half. A Video Showing what i mean.
My Code:
class Quicksort : Sortalgorithm
{
int ActivationMethod = -1;
Random rand = new Random();
/// <summary>
/// The Constructor for the Quicksort Algorithm
/// </summary>
/// <param name="arr">The Array to sort</param>
/// <param name="Actv">Activation Method: 0=First; 1=Last; 2=Random</param>
/// <param name="del">The Delay for the Algorithm between each sort</param>
/// <param name="sound">Wether Sound should be played</param>
/// <param name="gui">The Instance of the GUI</param>
public Quicksort(int[] arr, int Actv, int del, bool sound, gui gui) : base(del, sound, gui)
{
if (arr.Length < 5)
{
throw new Exception("Array has too few Entries");
}
ActivationMethod = Actv;
toSort = arr; // Is A Global Array from the inherited Class
}
protected override int sort()
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
return qsort(getPivot(), toSort.Length-1);
}
/// <summary>
/// The Quicksort Recursive Logic. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int qsort(int start, int end)
{
if (start < end)
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
int pivot = partition(start, end); //get the pivot
qsort(start, pivot); // do the same for the frist part (smaller or equal to pivot)
qsort(pivot + 1, end); //and now for the second part (the largen than pivot)
}
return 1;
}
/// <summary>
/// The Partitioning Logic for The Array. Both Params are needed for the "sub-array"
/// </summary>
/// <param name="start">StartIndex for the Current Array</param>
/// <param name="end">EndIndex for the Current Array</param>
/// <returns></returns>
private int partition(int start, int end)
{
int pivot = toSort[start]; //Depending on Activiation Method
int swapIndex = start;
for (int i = start + 1; i <= end; i++)
{
if (toSort[i] < pivot) //Sort the Index Accrodingly
{
if (token.IsCancellationRequested) return 2; // To Properly Cancel a Sort
swapIndex++;
swap(i, swapIndex); // Is A Method inherited from a Class
}
}
swap(start, swapIndex);
return swapIndex;
}
/// <summary>
/// Get The Pivot returning on the Declared Activation Method in the Construcor
/// </summary>
/// <returns>PivotIndex</returns>
private int getPivot()
{
switch (ActivationMethod)
{
case 0:
return 0;
case 1:
return toSort.Length - 1;
case 2:
return rand.Next(0, toSort.Length - 1);
default:
throw new Exception("No Activationmethod Defined");
};
}
}
I've been Stuck for quite a while with this problem, any help would be appreciated
There's a flaw in your logic.
You do qsort(getPivot(), toSort.Length-1);
at the top level to select the pivot, and then in partition() you do
int pivot = toSort[start];
That's not going to work: the first argument to qsort is where to start sorting, so anything but zero here at the top level will prevent parts of the input from participating in the sort.
Instead the pivot needs to be selected for each partition, so that must take place inside partition():
private int partition(int start, int end)
{
int pivot = toSort[getPivot(start, end)]; //Depending on Activiation Method
And then getPivot() must be modified to take the start and end arguments into account when calculating the pivot index to be within the proper range, based on the desired pivot selection method.

Knuth Morris Pratt search algorithm for search & replace on a stream

I want to implement a search and replace algorithm on a stream, where I would create a stream class that would take a source stream as input, and who's read method would read from the source stream as needed and perform a search/replace as the stream is being read. This would mitigate the entire set of data from being loaded into memory at once and enable search & replace on very large data sets.
To do this I thought I would start with an existing search algorithm, adapt it to a stream approach, then adapt the replace functionality.
Below is a Knuth Morris Pratt implementation I adapted from an online example.
Has anybody adapted something like this to a stream approach? I would need to take into account searching across read boundaries, which I'm not sure how I would do yet.
/// <summary>
/// The KMP matching algorithm uses degenerating property (SearchPattern having same sub-SearchPatterns appearing more than once in the SearchPattern)
/// of the SearchPattern and improves the worst case complexity to O(n). The basic idea behind KMP’s algorithm is: whenever we
/// detect a mismatch (after some matches), we already know some of the characters in the text of the next window. We take
/// advantage of this information to avoid matching the characters that we know will anyway match.
/// </summary>
/// <seealso cref="https://www.geeksforgeeks.org/kmp-algorithm-for-pattern-searching/"/>
public class KMPSearch
{
/// <summary>
/// Pattern we are looking for
/// </summary>
readonly string sSearchPattern_m;
/// <summary>
/// Text we are looking in
/// </summary>
readonly string sData_m;
/// <summary>
/// Index for text
/// </summary>
private int iDataPosition_m;
private int iDataLength_m;
/// <summary>
/// Index for search pattern
/// </summary>
private int iSearchPatternPosition_m;
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// </summary>
readonly int[] lstLongestProperPrefix_m;
public KMPSearch(string sSearchPattern, string sData)
{
this.sSearchPattern_m = sSearchPattern;
this.sData_m = sData;
this.iDataLength_m = sData.Length;
// create lps that will hold the longest prefix suffix values for SearchPattern
this.lstLongestProperPrefix_m = new int[sSearchPattern.Length];
// Pre-process the SearchPattern (calculate lps array)
this.ComputeLPSArray();
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
}
/// <summary>
/// Find next match
/// </summary>
/// <returns></returns>
public int Next()
{
int iMatchIndex = -1;
//We start comparison of pat[iSearchPatternPosition_m] with iSearchPatternPosition_m = 0 with characters of current window of text.
//We keep matching characters txt[iDataPosition_m] and pat[iSearchPatternPosition_m] and keep incrementing iDataPosition_m and iSearchPatternPosition_m while
//pat[iSearchPatternPosition_m] and txt[iDataPosition_m] keep matching.
while (iDataPosition_m < this.iDataLength_m)
{
if (this.sSearchPattern_m[iSearchPatternPosition_m] == this.sData_m[iDataPosition_m])
{
iSearchPatternPosition_m++;
iDataPosition_m++;
}
if (iSearchPatternPosition_m == sSearchPattern_m.Length)
{
//Console.WriteLine("Found SearchPattern at index %d ", iDataPosition_m - iSearchPatternPosition_m);
iMatchIndex = iDataPosition_m - iSearchPatternPosition_m;
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
// mismatch after j matches
else if (iDataPosition_m < this.iDataLength_m && this.sSearchPattern_m[iSearchPatternPosition_m] != this.sData_m[iDataPosition_m])
{
//When we see a mismatch
//* We know that characters pat[0..iSearchPatternPosition_m - 1] match with txt[iDataPosition_m-iSearchPatternPosition_m..iDataPosition_m - 1]
// (Note that iSearchPatternPosition_m starts with 0 and increment it only when there is a match).
//* We also know (from above definition) that lps[iSearchPatternPosition_m - 1] is count of characters of pat[0…iSearchPatternPosition_m - 1]
// that are both proper prefix and suffix.
//* From above two points, we can conclude that we do not need to match these lps[iSearchPatternPosition_m - 1] characters with
// txt[iDataPosition_m -iSearchPatternPosition_m..iDataPosition_m - 1] because we know that
// these characters will anyway match. Let us consider above example to understand this.
// Do not match lps[0..lps[iSearchPatternPosition_m - 1]] characters,
// they will match anyway
if (iSearchPatternPosition_m != 0)
{
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
else
{
iDataPosition_m = iDataPosition_m + 1;
}
}
if (iMatchIndex > -1)
{
return iMatchIndex;
}
}
return iMatchIndex;
}
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// Fills lps for given pattern pat[0..M-1]
/// lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
/// </summary>
private void ComputeLPSArray()
{
// length of the previous longest prefix suffix
int len = 0;
this.lstLongestProperPrefix_m[0] = 0; // lps[0] is always 0
// the loop calculates lps[i] for i = 1 to M-1
int i = 1;
while (i < this.sSearchPattern_m.Length)
{
if (this.sSearchPattern_m[i] == this.sSearchPattern_m[len])
{
len++;
this.lstLongestProperPrefix_m[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
// This is tricky. Consider the example.
// AAACAAAA and i = 7. The idea is similar
// to search step.
if (len != 0)
{
len = this.lstLongestProperPrefix_m[len - 1];
// Also, note that we do not increment
// i here
}
else // if (len == 0)
{
this.lstLongestProperPrefix_m[i] = 0;
i++;
}
}
}
}
}
Then I took the above algorithm and retrofitted it into a Stream. As a proof of concept, the stream will raise an event during it's read method every time it finds the search pattern. It currently has a limitation of not being able to search across the read boundaries. So if 1024 bytes are read at a time, and the length of the source stream is 2048 bytes, two reads are executed to read the entire stream. The issue is that if the search pattern starts at index 1000 and is 40 bytes long, it will not be found. I think once this issue is solved, the actual replace functionality won't be that difficult. I'm looking for suggestions on how to implement the search across read boundaries. It probably involves caching part of the previous read. Is anybody aware of a streaming implementation similar to this or suggestions?
public class KMPSearchStream : Stream
{
public override bool CanRead { get { return true; } }
public override bool CanSeek => throw new NotImplementedException();
public override bool CanWrite => throw new NotSupportedException();
public override long Length => throw new NotSupportedException();
public override long Position { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public class PatternFoundEventArgs
{
public int Index { get; internal set; }
}
public delegate void PatternFoundEvent(PatternFoundEventArgs e);
private Stream strSource_m;
/// <summary>
/// Pattern we are looking for
/// </summary>
private byte[] searchPattern_m;
/// <summary>
/// Text we are looking in
/// </summary>
private byte[] data_m;
/// <summary>
/// Index for text
/// </summary>
private int iDataPosition_m;
private int iDataLength_m;
/// <summary>
/// Index for search pattern
/// </summary>
private int iSearchPatternPosition_m;
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// </summary>
readonly int[] lstLongestProperPrefix_m;
public KMPSearchStream(Stream strSource, byte[] searchPattern)
{
if (strSource == null)
{
throw new ArgumentNullException(nameof(strSource), "Source stream is null.");
}
if (searchPattern == null || searchPattern.Length == 0)
{
throw new ArgumentNullException(nameof(searchPattern), "Pattern to find is null or empty.");
}
this.strSource_m = strSource;
this.searchPattern_m = searchPattern;
// create lps that will hold the longest prefix suffix values for SearchPattern
this.lstLongestProperPrefix_m = new int[searchPattern.Length];
// Pre-process the SearchPattern (calculate lps array)
this.ComputeLPSArray();
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
}
public event PatternFoundEvent OnPatternFound;
public override void Flush()
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
int iRead = this.strSource_m.Read(buffer, offset, count);
this.iDataPosition_m = 0; // index for txt
this.iSearchPatternPosition_m = 0; // index for pat
this.data_m = buffer;
this.iDataPosition_m = offset;
this.iDataLength_m = iRead;
int iIndex;
while ((iIndex = this.Next()) > -1)
{
this.OnPatternFound(new PatternFoundEventArgs()
{
Index = iIndex
});
}
return iRead;
}
/// <summary>
/// A proper prefix is prefix with whole string not allowed. For example, prefixes of “ABC” are “”, “A”, “AB” and “ABC”.
/// Proper prefixes are “”, “A” and “AB”. Suffixes of the string are “”, “C”, “BC” and “ABC”.
/// Fills lps for given pattern pat[0..M-1]
/// lps[i] = the longest proper prefix of pat[0..i] which is also a suffix of pat[0..i].
/// </summary>
private void ComputeLPSArray()
{
// length of the previous longest prefix suffix
int len = 0;
this.lstLongestProperPrefix_m[0] = 0; // lps[0] is always 0
// the loop calculates lps[i] for i = 1 to M-1
int i = 1;
while (i < this.searchPattern_m.Length)
{
if (this.searchPattern_m[i] == this.searchPattern_m[len])
{
len++;
this.lstLongestProperPrefix_m[i] = len;
i++;
}
else // (pat[i] != pat[len])
{
// This is tricky. Consider the example.
// AAACAAAA and i = 7. The idea is similar
// to search step.
if (len != 0)
{
len = this.lstLongestProperPrefix_m[len - 1];
// Also, note that we do not increment
// i here
}
else // if (len == 0)
{
this.lstLongestProperPrefix_m[i] = 0;
i++;
}
}
}
}
/// <summary>
/// Find next match
/// </summary>
/// <returns></returns>
public int Next()
{
int iMatchIndex = -1;
//We start comparison of pat[iSearchPatternPosition_m] with iSearchPatternPosition_m = 0 with characters of current window of text.
//We keep matching characters txt[iDataPosition_m] and pat[iSearchPatternPosition_m] and keep incrementing iDataPosition_m and iSearchPatternPosition_m while
//pat[iSearchPatternPosition_m] and txt[iDataPosition_m] keep matching.
while (iDataPosition_m < this.iDataLength_m)
{
if (this.searchPattern_m[iSearchPatternPosition_m] == this.data_m[iDataPosition_m])
{
iSearchPatternPosition_m++;
iDataPosition_m++;
}
if (iSearchPatternPosition_m == searchPattern_m.Length)
{
//Console.WriteLine("Found SearchPattern at index %d ", iDataPosition_m - iSearchPatternPosition_m);
iMatchIndex = iDataPosition_m - iSearchPatternPosition_m;
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
// mismatch after j matches
else if (iDataPosition_m < this.iDataLength_m && this.searchPattern_m[iSearchPatternPosition_m] != this.data_m[iDataPosition_m])
{
//When we see a mismatch
//* We know that characters pat[0..iSearchPatternPosition_m - 1] match with txt[iDataPosition_m-iSearchPatternPosition_m..iDataPosition_m - 1]
// (Note that iSearchPatternPosition_m starts with 0 and increment it only when there is a match).
//* We also know (from above definition) that lps[iSearchPatternPosition_m - 1] is count of characters of pat[0…iSearchPatternPosition_m - 1]
// that are both proper prefix and suffix.
//* From above two points, we can conclude that we do not need to match these lps[iSearchPatternPosition_m - 1] characters with
// txt[iDataPosition_m -iSearchPatternPosition_m..iDataPosition_m - 1] because we know that
// these characters will anyway match. Let us consider above example to understand this.
// Do not match lps[0..lps[iSearchPatternPosition_m - 1]] characters,
// they will match anyway
if (iSearchPatternPosition_m != 0)
{
iSearchPatternPosition_m = this.lstLongestProperPrefix_m[iSearchPatternPosition_m - 1];
}
else
{
iDataPosition_m = iDataPosition_m + 1;
}
}
if (iMatchIndex > -1)
{
return iMatchIndex;
}
}
return iMatchIndex;
}
}
If I understand you correctly, the goal is to scan an ongoing stream and replace all occurences of a mask with a replacement value before forwarding it to an output stream.
If so, there are a few requirements to the system:
Only data may be forwarded for which can be proven that it is not part of a complete mask
Data must be forwarded as soon as possible to keep congestion to a minimum
Abstract:
In essence, you buffer all the data before you forward it.
After each character, you test whether the buffer could be part of the mask.
If it couldn't be part of the mask, you release (write to output) one character after the other from the buffer until either the buffer is empty, or the buffer could match the mask again.
Code:
I made a helper class that does essentially that.
I don't know whether it implements your algorithm, this is just something I cooked up.
According to my tests, it works, but I can't guarantee anything.
You can use it in your stream class and just forward the calls and recive the callbacks.
Forward input data via the Write([...]) methods, get output via the output callback and define the mask and replacement string. It supports flushing and reseting.
using System;
using System.Collections.Generic;
/// <summary>
/// Class that handles replacement of string occurrences that match a mask in a string stream.
/// </summary>
public class StreamReplacement
{
private Action<char> output;
private string mask;
private string replacement;
// Buffer for unverified characters
private readonly List<char> buffer;
// Current index on the mask for the next character added
private int maskPosition;
/// <summary>
/// All verified characters will be passed to the output
/// </summary>
/// <exception cref="System.ArgumentNullException">The output cannot be null.</exception>
public Action<char> Output
{
get => output;
set => output = value ?? throw new ArgumentNullException();
}
/// <summary>
/// Gets or sets the mask to test for.
/// </summary>
/// <exception cref="System.ArgumentException">The mask must contain at least one character.</exception>
public string Mask
{
get => this.mask;
set
{
if (string.IsNullOrEmpty(mask))
{
throw new ArgumentException("The mask must contain at least one character.");
}
this.mask = value;
// Clean buffer and throw out characters that can't be part of the mask anymore.
WriteVerifiedCharactersFromBuffer();
}
}
/// <summary>
/// Gets or sets the replacement string to output when the mask has been encountered.
/// </summary>
public string Replacement
{
get => replacement;
set => replacement = value ?? string.Empty;
}
/// <summary>
/// Initializes a new instance of the <see cref="StreamReplacement"/> class.
/// </summary>
/// <param name="output">The output.</param>
/// <param name="mask">The mask.</param>
/// <param name="replacement">The replacement string for mask encounters.</param>
/// <exception cref="System.ArgumentNullException">Output cannot be null.</exception>
/// <exception cref="System.ArgumentException">The mask must contain at least one character.</exception>
public StreamReplacement(Action<char> output, string mask, string replacement)
{
this.output = output ?? throw new ArgumentNullException(nameof(output));
this.mask = string.IsNullOrEmpty(mask) ? throw new ArgumentException("The mask must contain at least one character.") : mask;
this.replacement = replacement ?? string.Empty;
this.buffer = new List<char>(mask.Length);
}
/// <summary>
/// Writes a single character to the stream.
/// </summary>
public void Write(char c)
{
WriteCharacter(c);
}
/// <summary>
/// Writes the specified text to the stream.
/// </summary>
public void Write(string text)
{
if (string.IsNullOrEmpty(text))
{
return;
}
foreach (var c in text)
{
WriteCharacter(c);
}
}
/// <summary>
/// Writes the specified characters to the stream.
/// </summary>
/// <param name="characters">The characters.</param>
public void Write(params char[] characters)
{
if (characters == null)
{
return;
}
foreach (var c in characters)
{
WriteCharacter(c);
}
}
/// <summary>
/// Flushes all buffered characters, even if they could be part of the mask.<br/>
/// Starts from scratch after flushing.
/// </summary>
public void Flush()
{
foreach (var c in buffer)
{
output(c);
}
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Clears the buffer without writing any buffered data to the output stream.
/// </summary>
public void Reset()
{
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Writes the character either to the buffer or output stream and handles masking.
/// </summary>
private void WriteCharacter(char c)
{
if (mask[maskPosition] == c)
{
AddUnverifiedCharacter(c);
}
else
{
buffer.Add(c);
WriteVerifiedCharactersFromBuffer();
}
}
/// <summary>
/// Stores a character in the buffer that could be part of the mask.
/// </summary>
private void AddUnverifiedCharacter(char c)
{
buffer.Add(c);
maskPosition++;
if (maskPosition == mask.Length)
{
buffer.Clear();
maskPosition = 0;
foreach (var repChar in replacement)
{
output(repChar);
}
}
}
/// <summary>
/// Writes characters that cannot be part of the mask to the output.
/// </summary>
private void WriteVerifiedCharactersFromBuffer()
{
// Find possible new start position in buffer
var newStart = 0;
for (; newStart < buffer.Count; newStart++)
{
if (IsBufferPartOfMask(newStart))
{
WritePartOfBuffer(newStart);
buffer.RemoveRange(0, newStart);
maskPosition = buffer.Count;
return;
}
}
WritePartOfBuffer(buffer.Count);
buffer.Clear();
maskPosition = 0;
}
/// <summary>
/// Determines whether the buffer content can be part of the mask, starting at the given index.
/// </summary>
private bool IsBufferPartOfMask(int offset)
{
for (var i = 0; i < buffer.Count - offset; i++)
{
var c = buffer[offset + i];
if (c != mask[i])
{
return false;
}
}
return true;
}
/// <summary>
/// Writes the beginning part of buffer to the output
/// </summary>
private void WritePartOfBuffer(int count)
{
for (var i = 0; i < count; i++)
{
output(buffer[i]);
}
}
}

Writing a removeLast method

Situation:
I'm new to c# and currently learning the ropes whilst also polishing up on my data structures, I decided to make a class that'd perform multiple functions to a linear array, since It was suggested to me that I should start with linear arrays then work on a circular one.
The methods my class currently provides are:
adding an item to the front-most position of the array,
adding an item to the back-most position of the array,
removing the first item in the array,
removing the last item in the array, //todo
clearing the current array list of it's values,
displaying the array list contents to the user.
Problem:
I'm having difficulty constructing a method that removes the last item of the array, I've looked online and the pre-writen methods seem complex and I can't get my head around them due to my in-experience. I realize it must be easy for others to write a method like this, but I'm truly stumped.
I'd like to learn how to write a method that removes the last value in the array, in the most simpliest to understand way. Here is my current code for my LinearArrayList class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace arraydatastructuresactual
{
public class LinearArrayList
{
private int count; //how many numbers currently stored
private int[] values; //array to hold values entered into list
//constructors
/// <summary>
/// //creates a linear array that can hold max values
/// </summary>
/// <param name="max"></param>
public LinearArrayList(int max)
{
count = 0;
values = new int[max]; //makes the linear array as big as the max value
}
/// <summary>
/// //default constructor sets capacity to 10
/// </summary>
public LinearArrayList()
{
count = 0;
values = new int[10];
}
/// <summary>
/// returns true if list is empty
/// otherwise turns false
/// </summary>
/// <returns>true if empty otherwise false</returns>
public bool isEmpty()
{
return (count == 0);
}
/// <summary>
/// returns true if list is full
/// otherwise turns false
/// </summary>
/// <returns>true if full otherwise false</returns>
public bool isFull()
{
return (values.Length <= count);
}
/// <summary>
/// if not full adds value to the end of list
/// throws exception if list is fulll
/// </summary>
/// <param name="value">value to add to end of list</param>
public void addLast(int value) //adds an item to the last position in the array.
{
if (isFull())
{
throw new Exception("List Full");
}
else
{
values[count++] = value;
}
}
public void addFirst(int value) //Adds an item to the first position in the array.
{
if (isFull())
{
throw new Exception("List Full");
}
else
{
for (int i = count; i > 0; i--)
{
values[i] = values[i--];
}
values[0] = value;
count++;
}
}
public int removeFirst() //removes the first item from the array
{
if (isEmpty())
throw new Exception("List is Empty");
int value = values[0];
count--;
for (int i = 0; i < count; i++)
{
values[i] = values[i + 1];
}
return value;
}
public int removeLast() //todo //removes the last item from the array
{
if (isEmpty())
throw new Exception("List is Empty");
int value = values[0];
count--;
for (int i = count; i < count; i++)
{
values[i] = values[i + 1];
}
return value;
}
public void displayUI()//displays contents of list
{
}
public void destroy() //Empties The List
{
}
}
}
If someone could share their experience on how I'd go about achieving this, then many thanks, I tried to repurpose my removeFirst method but I messed up, tried to do this a few times and I'm completely stumped now.
You just need to write
public int removeLast()
{
if (isEmpty())
throw new Exception("List is Empty");
count--;
return values[count];
}
this will return the last item in the values array WITHOUT changing its size but decrementing the variable that keeps track of the item actually inserted in the array.
Notice that I don't try to change the value in the location pointed by the count variable. It is still there until you overwrite it adding another value
So you could still write this
// Creates an array with space for 10 ints
LinearArrayList la = new LinearArrayList();
la.addLast(34);
la.addLast(23);
la.addLast(56);
la.addLast(467);
la.addLast(43);
la.addLast(666);
la.addLast(7989);
la.addLast(82);
la.addLast(569);
la.addLast(100);
int value = la.removeLast();
// This will work because you still have a slot free in the 10 ints array
la.addLast(1110);
// While this will fail because the array has now all slots filled
la.addLast(9435);
An alternative to Sybren's answer:
int lastValue = values[values.Length - 1];
int[] newValues = new int[values.Length - 1];
Array.Copy(values, newValues, newValues.Length);
values = newValues;
return lastValue;
or
int lastValue = values[values.Length - 1];
Array.Resize(values, values.Length - 1);
return lastValue;
If you don't want to use any existing methods of Array class, you can also:
int lastValue = values[values.Length - 1];
int[] newValues = new int[values.Length - 1];
for (int i = 0; i < newValues.Length; i++)
{
newValues[i] = values[i];
}
values = newValues;
return lastValue;
Edit
Forget this, do what #Steve said.
Here's a way to do it. You're converting your array to a list, getting the last element from the list, removing the last element from the list, and convert it back to an array.
var numbersList = values.ToList();
var last = numbersList.Last();
numbersList.Remove(last);
values = numbersList.ToArray();
return last;

Split String Array[index] > constant returning True when it should be false

First things first, I'm brand new here and I'm hoping that this adheres to your guidelines for questions. I don't believe other questions/threads here would be applicable (at least they didn't appear to be).
Anyways, I'm a programming novice taking a C# class in college. The assignment I'm working on has to do with the Split() method. I'm supposed to ask the user for names and (bowling) scores in a textbox, split it into a temporary array, then take the appropriate index values from the temporary Array and place them in Name and Score arrays. It should handle up to 10 players, but it should work for fewer than that (partially filled array).
It will calculate and output the high score + who has it, the low score + who has it, and the average score, as well as outputting all the names and scores. I want to reject any entered scores that aren't between 0 and 300 before they enter the array, but when I enter a value outside of that range and debug, it says that the greater than/less than/ equal to is true. Obviously, if I enter 9001 or -3 etc. as a score I'd want it to be rejected. Here is that part of the code.
public void GetSplit(string _stg)
{
string[] tempArray;
//Split the temp array
tempArray = _stg.Split();
//Professor said a for loop would be unnecessary since it's a GUI
//LOW = 300
if(score[index] <= LOW && score[index] >= 0)
{//this part here^^ is returning true when it should be false
//1st score is in the 2nd slot in tempArray
//if input is not between 0-300 and is NOT an integer, yell at them
if (!int.TryParse(tempArray[1], out score[index]))
{
MessageBox.Show(YELL_INT);
return;
}
bool status = (int.TryParse(tempArray[1], out score[index]) ? true :false);
//1st name is in 1st slot in tempArray
name[index++] = tempArray[0];
}
}
If anyone has a solution as to how or why this isn't working, as well as an example on how to get it to work (even a redirect to another answered question that's similar to the one I'm asking that I missed), that'd awesome. Thanks!
I don't know why it would be necessary for me to add this, but here's all of the code from the program:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Proj_08
{
public partial class FrmMain : Form
{
private BowlingScores bs;//reference to bowlingscores class
/// <summary>
/// Purpose: Use the BowlingScores class to display names of players and scores
/// as well as high score + name, low score + name, and average score
/// </summary>
public FrmMain()
{
InitializeComponent();
}
/// <summary>
/// Purpose: Initialize BowlingScores and _tempArray
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Form1_Load(object sender, EventArgs e)
{
bs = new BowlingScores();
}
/// <summary>
/// Purpose: Close out of program
/// </summary>
/// <param name="sender">Button Clear Click Event</param>
/// <param name="e">EventArgs Object</param>
public void MSpExit_Click(object sender, EventArgs e)
{
Close();
}
/// <summary>
/// Purpose: Send the contents of the textbox to the GetScore method in BowlingScores class
/// </summary>
/// <param name="sender">Entry Enter Key Press Event</param>
/// <param name="e">KeyPressEventArgs Object</param>
private void TxtEntry_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == (char)Keys.Enter)
{
bs.GetSplit(TxtEntry.Text);
TxtEntry.Text = string.Empty;
}
}
/// <summary>
/// Purpose: show everything in RichTextBox
/// </summary>
/// <param name="sender">Button Calc Click Event</param>
/// <param name="e">EventArgs Object</param>
public void BtnCalc_Click(object sender, EventArgs e)
{
//Output returned string from GetAll method
RTbOutput.Text = bs.GetAll();
}
/// <summary>
/// Purpose: Clear the textboxes and reset all arrays and references and the index
/// </summary>
/// <param name="sender">Button Clear Click Event</param>
/// <param name="e">EventArgs Object</param>
private void BtnClear_Click(object sender, EventArgs e)
{
bs = new BowlingScores();
TxtEntry.Text = string.Empty;
RTbOutput.Text = string.Empty;
}
}
//class BowlScores
public class BowlingScores
{
private const int LOW = 300;
private const int ASIZE = 10;
private const string YELL_INT = "Invalid Score";
private const string HEADER = "ERROR";//still have to add this
private int[] score;
private string[] name;
private int index;
/// <summary>
/// Purpose: Constructor for BowlingScores Class
/// </summary>
public BowlingScores()
{
index = 0;
score = new int[ASIZE];
name = new string[ASIZE];
}
/// <summary>
/// Purpose: Getter/Setter for name array
/// </summary>
public string[] Name
{
get { return name; }
set { name = value; }
}
/// <summary>
/// Purpose: Getter/Setter for score array
/// </summary>
public int[] Score
{
get { return score; }
set { score = value; }
}
/// <summary>
/// Purpose: Capture text from textbox and split into name and score arrays
/// </summary>
/// <param name="_stg"></param>
public void GetSplit(string _stg)
{
//int index = 0;
string[] tempArray;
//Split the temp array
tempArray = _stg.Split();
if(score[index] <= LOW && score[index] >= 0)
{
//1st score is in the 2nd slot in tempArray
//if input is not between 0-300 and is NOT an integer, yell at them
if (!int.TryParse(tempArray[1], out score[index]))
{
MessageBox.Show(YELL_INT, HEADER, MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
bool status = (int.TryParse(tempArray[1], out score[index]) ? true : false);
//1st name is in 1st slot in tempArray
name[index++] = tempArray[0];
//increment index to continue filling respective arrays
}
}
/// <summary>
/// Purpose: Calculate High, Low, Average
/// </summary>
/// <returns></returns>
public string CalcData()
{
int high = 0;//to figure high score
string holdHigh = "";
int low = LOW;//to figure low score
string holdLow = "";
double sum = 0.0;
double avg = 0.0;
double count = 0.0;//to calculate average,
//
for (int index = 0; index < score.Length && name[index] != null; index++)
{
//calculate high score
//if an entered score is greater than 0, replace high with that entered score
if (score[index] > high && score[index] <= LOW && score[index] >= 0)
{
high = score[index];
holdHigh = name[index];
}
//calculate the low score
//if an entered score is less than 300, replace low with that entered score
if (score[index] < low && score[index] <= LOW && score[index] >= 0)
{
low = score[index];
holdLow = name[index];
}
//calculate sum and average
if (score[index] <= LOW && score[index] >= 0)
sum += score[index];
count = index + 1;
avg = (sum / count);
}
return string.Format("Congratulations {0}, you got the high score of {1}!\nBetter luck next time, {2}, you got the lowest score of {3}\nThe average score is {4:F2}", holdHigh, high, holdLow, low, avg);
}
/// <summary>
/// Purpose: Get all the names and scores and output them as a string
/// </summary>
/// <returns></returns>
public string GetAll()
{
string outputStg = "";
//as long as entry isn't null and index is less than Length
for(int index = 0; index < score.Length && name[index] != null ; index++)
{
//if the score is above 300 or below 0, don't return those values
if (score[index] <= LOW && score[index] >= 0 )
outputStg += name[index] + "\t\t" + score[index] + "\n";
}
return outputStg += "\n" + CalcData();
}
}
}
So from looking at your code, one flaw I saw so far is that you are checking score when nothing has been added to it yet! if your tempArray contains the data the user has input, you should be checking that first.
So like
// I'm assuming your incoming string is formatted like "Name Score Name Score"
// E.G. "Ryan 140 Tim 400"
int result;
string[] tempArray = _stg.Split();
if (int.TryParse(tempArray[1], out result))
{
if (result <= LOW && result >= 0)
// within bounds, add it to score array, etc.
}

Fraction comparison issue in C#

There is something wrong with the comparison of >= method:
public static bool operator >=(Fraction left, Fraction right)
{
return left.CompareTo(right) >= 0;
}
It doesn't seem to work?!
For example:
25 >= 6/5 returns FALSE
Can anyone shed some light into this?
Below is the requested code. I hope somebody can spot what's wrong with it.
/// <summary>
/// Compares an object to this Fraction
/// </summary>
/// <param name="obj">The object to compare against (null is less than everything)</param>
/// <returns>-1 if this is less than <paramref name="obj"></paramref>,
/// 0 if they are equal,
/// 1 if this is greater than <paramref name="obj"></paramref></returns>
/// <remarks>Will convert an object from longs, doubles, and strings as this is a value-type.</remarks>
public int CompareTo(object obj)
{
if (obj == null)
return 1; // null is less than anything
Fraction right;
if (obj is Fraction)
right = (Fraction)obj;
else if (obj is long)
right = (long)obj;
else if (obj is double)
right = (double)obj;
else if (obj is string)
right = (string)obj;
else
throw new ArgumentException("Must be convertible to Fraction", "obj");
return this.CompareTo(right);
}
/// <summary>
/// Compares this Fraction to another Fraction
/// </summary>
/// <param name="right">The Fraction to compare against</param>
/// <returns>-1 if this is less than <paramref name="right"></paramref>,
/// 0 if they are equal,
/// 1 if this is greater than <paramref name="right"></paramref></returns>
public int CompareTo(Fraction right)
{
// if left is an indeterminate, punt to the helper...
if (this.m_Denominator == 0)
{
return IndeterminantCompare(NormalizeIndeterminate(this.m_Numerator), right);
}
// if right is an indeterminate, punt to the helper...
if (right.m_Denominator == 0)
{
// note sign-flip...
return -IndeterminantCompare(NormalizeIndeterminate(right.m_Numerator), this);
}
// they're both normal Fractions
CrossReducePair(ref this, ref right);
try
{
checked
{
long leftScale = this.m_Numerator * right.m_Denominator;
long rightScale = this.m_Denominator * right.m_Numerator;
if (leftScale < rightScale)
return -1;
else if (leftScale > rightScale)
return 1;
else
return 0;
}
}
catch (Exception e)
{
throw new FractionException(string.Format("CompareTo({0}, {1}) error", this, right), e);
}
}
/// <summary>
/// Cross-reduces a pair of Fractions so that we have the best GCD-reduced values for multiplication
/// </summary>
/// <param name="frac1">The first Fraction [WILL BE MODIFIED IN PLACE]</param>
/// <param name="frac2">The second Fraction [WILL BE MODIFIED IN PLACE]</param>
/// <remarks>Modifies the input arguments in-place!</remarks>
/// <example>(3/4, 5/9) = (1/4, 5/3)</example>
public static void CrossReducePair(ref Fraction frac1, ref Fraction frac2)
{
// leave the indeterminates alone!
if (frac1.m_Denominator == 0 || frac2.m_Denominator == 0)
return;
long gcdTop = GCD(frac1.m_Numerator, frac2.m_Denominator);
frac1.m_Numerator = frac1.m_Numerator / gcdTop;
frac2.m_Denominator = frac2.m_Denominator / gcdTop;
long gcdBottom = GCD(frac1.m_Denominator, frac2.m_Numerator);
frac2.m_Numerator = frac2.m_Numerator / gcdBottom;
frac1.m_Denominator = frac1.m_Denominator / gcdBottom;
}
CrossReducePair changes the relationship between the numbers. Your example of (3/4, 5/9) = (1/4, 5/3) makes it very obvious. Cross reducing makes sense if you're multiplying, but not if you're just comparing them.
The CrossReducePair method doesn't seem to make much sense. As noted in the comments, it transforms (3/4, 5/9) to (1/4, 5/3). Note: 3/4 > 5/9, but 1/4 < 5/3.
Also, it's a very bad idea to have a CompareTo method which modifies the object.

Categories

Resources