how to fix IComparable<T> while creating a priority queue - c#

this is a priority queue that I found at GitHub
public class PriorityQueue<T> where T : IComparable<T>
{
private List<T> data;
public PriorityQueue()
{
this.data = new List<T>();
}
public void Enqueue(T item)
{
data.Add(item);
int ci = data.Count - 1; // child index; start at end
while (ci > 0)
{
int pi = (ci - 1) / 2; // parent index
if (data[ci].CompareTo(data[pi]) >= 0) break; // child item is larger than (or equal) parent so we're done
T tmp = data[ci]; data[ci] = data[pi]; data[pi] = tmp;
ci = pi;
}
}
public T Dequeue()
{
// assumes pq is not empty; up to calling code
int li = data.Count - 1; // last index (before removal)
T frontItem = data[0]; // fetch the front
data[0] = data[li];
data.RemoveAt(li);
--li; // last index (after removal)
int pi = 0; // parent index. start at front of pq
while (true)
{
int ci = pi * 2 + 1; // left child index of parent
if (ci > li) break; // no children so done
int rc = ci + 1; // right child
if (rc <= li && data[rc].CompareTo(data[ci]) < 0) // if there is a rc (ci + 1), and it is smaller than left child, use the rc instead
ci = rc;
if (data[pi].CompareTo(data[ci]) <= 0) break; // parent is smaller than (or equal to) smallest child so done
T tmp = data[pi]; data[pi] = data[ci]; data[ci] = tmp; // swap parent and child
pi = ci;
}
return frontItem;
}
public T Peek()
{
T frontItem = data[0];
return frontItem;
}
public int Count()
{
return data.Count;
}
public override string ToString()
{
string s = "";
for (int i = 0; i < data.Count; ++i)
s += data[i].ToString() + " ";
s += "count = " + data.Count;
return s;
}
public bool IsConsistent()
{
// is the heap property true for all data?
if (data.Count == 0) return true;
int li = data.Count - 1; // last index
for (int pi = 0; pi < data.Count; ++pi) // each parent index
{
int lci = 2 * pi + 1; // left child index
int rci = 2 * pi + 2; // right child index
if (lci <= li && data[pi].CompareTo(data[lci]) > 0) return false; // if lc exists and it's greater than parent then bad.
if (rci <= li && data[pi].CompareTo(data[rci]) > 0) return false; // check the right child too.
}
return true; // passed all checks
} // IsConsistent
I have create an adge class like this:
public class Node
{
long x;
long y;
public long parent;
public long rank;
public Node(long a, long b, long c)
{
x = a;
y = b;
parent = c;
rank = 0;
}
}
public class Edge : IComparable<Edge>
{
public long u;
public long v;
public double weight;
public Edge(long a, long b, double c)
{
u = a;
v = b;
weight = c;
}
public int CompareTo(Edge e1, Edge e2)
{
return e1.weight < e2.weight ? -1 : 1;
}
public int CompareTo(Edge other)
{
throw new NotImplementedException();
}
}
when I try to create an instance form priority queue class with edges I have an error like this:
PriorityQueue<Edge> edges = new PriorityQueue<Edge>();
The type 'A4.Edge' cannot be used as type parameter 'T' in the generic type or method 'PriorityQueue'. There is no implicit reference conversion from 'A4.Edge' to 'System.IComparable
how can I fix this?

As of the defintion of your PriorityQueue-class T must implement IComparable<T>. Your Edge-class does not implement that interface, so you get the compiler-error-
Your Edge-class has to provide some way in order to compare an instance of it with another instance. That's done by implementing the IComparable<T>-interface:
public class Edge : IComparable<Edge>
{
public long u;
public long v;
public double weight;
public Edge(long a, long b, double c)
{
u = a;
v = b;
weight = c;
}
public int CompareTo(Edge other)
{
// your comparison here
}
}
Now as your class already provide some mechanism to make it comparable you won´t need to provide a Comparer to your PriorityQueue-class at all. In fact you don´t even use it in your code, so omit the parameter from the constructor:
PriorityQueue<Edge> edges = new PriorityQueue<Edge>();

Related

Need advice on optimizing warehouse layout

I'm currently working for a distribution center with ~500 shelves with items for sale and ready to be shipped out to customers. I have decided to create a ranking for each aisle, where the best selling products will be stored at the front and the worst selling products will be stored at the back. I assumed that this would automatically improve efficience in my warehouse. However, after implementing this (see here). After using BFS to simulate a warehouse worker's route it's turned out that this new layout is actually taking more steps for a worker to complete than a regular non-optimized layout..
This is my code for BFS, however I think my problem lies in the way I decide where high demand items go, not in my BFS.
List<String> findPathBFS(magazijnObject[,] grid, bool[,] vis,
string start, string goal)
{
List<string> path = new List<string>();
// Stores indices of the matrix cells
Queue<pair> q = new Queue<pair>();
int row = 0; int col = 0;
// Mark the starting cell as visited
// and push it into the queue
for (int x = 0; x < grid.GetLength(0); x++)
for (int y = 0; y < grid.GetLength(1); y++)
{
if (grid[x, y].Locatie == start)
{
row = x; col = y;
x = grid.GetLength(0); y = grid.GetLength(1);
break;
}
}
q.Enqueue(new pair(row, col, null));
vis[row, col] = true;
// Iterate while the queue
// is not empty
while (q.Count != 0)
{
pair cell = q.Peek();
int x = cell.first;
int y = cell.second;
if (grid[x, y].Locatie == goal)
{
//Console.WriteLine(cell.parent.first + " " + cell.parent.second);
findPath(cell, path);
return path;
}
q.Dequeue();
// Go to the adjacent cells
for (int i = 0; i < 4; i++)
{
int adjx = x + dRow[i];
int adjy = y + dCol[i];
if (isValid(vis, adjx, adjy, grid))
{
if (grid[adjx, adjy].Loopbaar || grid[adjx, adjy].Locatie == goal)
#Locatie = Location of item in aisle, Loopbaar = whether or not a worker can walk here.
{
q.Enqueue(new pair(adjx, adjy, cell));
vis[adjx, adjy] = true;
}
}
}
}
return path;
}
And this is my pair Class which defines a cell and it's parent
class pair
{
public int first, second;
public pair parent;
public pair(int first, int second, pair parent)
{
this.first = first;
this.second = second;
this.parent = parent;
}
public String toString()
{
return (first + " " + second);
}
}
static int[] dRow = { -1, 0, 1, 0 };
static int[] dCol = { 0, 1, 0, -1 };
static bool isValid(bool[,] vis,
int row, int col, magazijnObject[,] test)
{
// If cell lies out of bounds
if (row < 0 || col < 0 ||
row >= test.GetLength(0) || col >= test.GetLength(1))
return false;
// If cell is already visited
if (vis[row, col])
return false;
// Otherwise
return true;
}
private static void findPath(pair node, List<String> path)
{
if (node != null)
{
findPath(node.parent, path);
path.Add(node.toString());
}
}
I'm curious to see whether you have any ideas on how to improve this layout in a way. As this obviously is not an improvement.
Kind regards,
D.

.NET Stops on sorting and Icompare()

I'm having a problem with sorting and IComparer(). My program stops on it. I'm a beginner with C#.
Here is the part of the code where it stops:
public ArrayList ModelSort()
{
IComparer sorter = new R2SortHelper();
InnerList.Sort(sorter);
return InnerList;
}
private class R2SortHelper : System.Collections.IComparer
{
public int Compare(object x, object y)
{
double m1 = ((Model)x).R2() + ((Model)x).Valid().GetHashCode();
double m2 = ((Model)y).R2() + ((Model)y).Valid().GetHashCode();
if (m1 > m2)
return -1;
else if (m1 < m2)
return 1;
else
return 0;
}
}
Here is an error from console:
Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'AMO.EnPI.AddIn.Utilities.ModelCollection+R2SortHelper'.
List of exceptions:
Exception Text
System.ArgumentException: Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparer: 'AMO.EnPI.AddIn.Utilities.ModelCollection+R2SortHelper'.
at System.Array.SorterObjectArray.DepthLimitedQuickSort(Int32 left, Int32 right, Int32 depthLimit)
at System.Array.SorterObjectArray.DepthLimitedQuickSort(Int32 left, Int32 right, Int32 depthLimit)
at System.Array.Sort(Array keys, Array items, Int32 index, Int32 length, IComparer comparer)
at System.Collections.ArrayList.Sort(Int32 index, Int32 count, IComparer comparer)
at System.Collections.ArrayList.Sort(IComparer comparer)
at AMO.EnPI.AddIn.Utilities.ModelCollection.ModelSort() in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.Utilities\Analytics.cs:line 961
at AMO.EnPI.AddIn.ModelSheet.WriteResultsTable(Int32 n, Boolean top) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ModelSheet.cs:line 146
at AMO.EnPI.AddIn.ModelSheet.Populate() in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ModelSheet.cs:line 60
at AMO.EnPI.AddIn.ThisAddIn.plotEnPI(ListObject LO) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\ThisAddIn.cs:line 318
at AMO.EnPI.AddIn.RegressionControl.runFunction(Object sender, EventArgs e) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\RegressionControl.cs:line 817
at AMO.EnPI.AddIn.CO2EmissionControl.btnCalculate_Click(Object sender, EventArgs e) in C:\ENPI\EnPI\AMO.EnPI-5.0\AMO.EnPI.AddIn\CO2EmissionControl.cs:line 148
My model class:
public class Model
{
public int ModelNumber { get; set; }
public double[] Ys { get; set; }
public double[,] Xs { get; set; }
public string[] VariableNames { get; set; }
public double RMSError { get; set; }
public double[] Coefficients { get; set; }
public Model()
{
ModelNumber = 0;
Ys = null;
Xs = null;
VariableNames = null;
RMSError = 0;
Coefficients = null;
}
public Model(int ModelNumber, double[] Ys, double[,] Xs, string[] VariableNames)
{
RMSError = 0;
Coefficients = null;
// run LLS
int info;
double[] c;
alglib.lsfitreport rep;
try
{
alglib.lsfitlinear(Ys, Xsplusone(), out info, out c, out rep);
}
catch
{
throw;
}
Coefficients = c;
RMSError = rep.rmserror;
}
public void Run() //double[] Ys, double[,] Xs, string[] VariableNames)
{
RMSError = 0;
Coefficients = null;
if (Ys != null && Xs != null)
{
// run LLS
int info;
double[] c;
alglib.lsfitreport rep;
try
{
alglib.lsfitlinear(Ys, Xsplusone(), out info, out c, out rep);
}
catch
{
throw;
}
Coefficients = c;
RMSError = rep.rmserror;
}
}
public int N()
{
return Ys.Count();
}
public int df()
{
return N() - k() - 1;
}
public int k()
{
return VariableNames.Count();
}
public double TotalSS()
{
// compute total sum of squares
double ybar = Ys.Average();
double sst = 0;
for (int i = Ys.GetLowerBound(0); i <= Ys.GetUpperBound(0); i++)
{
sst += Math.Pow(Ys[i] - ybar, 2);
}
return sst;
}
public double ResidualSS ()
{
return ( N() * Math.Pow( RMSError, 2));
}
public double R2()
{
return (1 - (ResidualSS() / TotalSS()));
}
public double AdjustedR2()
{
return (1 - (((1 - R2()) * (N() - 1)) / (N() - k() - 1)));
}
public double F()
{
return ( (R2() / k()) / ((1 - R2()) / (N() - k() - 1)));
}
public double ModelPValue()
{
double modelP = 0;
double modelF = F();
if (modelF < 0) modelF = 0;
try
{
modelP = alglib.fcdistribution(N() - df() - 1, df(), modelF);
}
catch (alglib.alglibexception e)
{
}
return modelP;
}
public bool Valid()
{
// Model validity criteria, from the SEP M&V protocol:
// The model p-value must be less than 0.1
// All variables must have p-values less than 0.2
// At least one variable must have a p-value of less than 0.1
// The R2 value must be greater than 0.5
double[] ps = PValues();
bool varsvalid = true;
bool varlowexists = false;
for (int i = 0; i < ps.Count(); i++)
{
if (ps[i] <= Constants.PVALUE_THRESHOLD)
varlowexists = true;
if (ps[i] > Constants.PVALUE_HIGH)
varsvalid = false;
}
if (!varlowexists)
return false;
if (!varsvalid)
return false;
if (ModelPValue() > Constants.PVALUE_THRESHOLD)
return false;
if (R2() < Constants.R2VALUE_MIN)
return false;
return true;
}
public string Formula()
{
string formula = "";
int offset = Coefficients.GetLowerBound(0) - VariableNames.GetLowerBound(0);
for (int i = Coefficients.GetLowerBound(0); i < Coefficients.GetUpperBound(0); i++)
{
formula += "(" + Coefficients[i].ToString("0.0000") + " * " + ExcelHelpers.CreateValidFormulaName(VariableNames[i - offset]) + ") + ";
// formula += "(" + Coefficients[i].ToString() + " * " + ExcelHelpers.CreateValidFormulaName(VariableNames[i - offset]) + ") + ";
}
formula += Coefficients[Coefficients.GetUpperBound(0)].ToString("0.00");
return formula;
}
public double[,] Xsplusone()
{
return DataHelper.arrayAddIdentity(Xs, 0, 1); // add on a column of ones for the intercept
}
public double[] PredictedYs()
{ // compute the predicted ys
double[] yhat = new double[N()];
double[,] xs = Xsplusone();
double[] c = Coefficients;
for (int i = 0; i < N(); i++)
{
yhat[i] = 0;
for (int j = 0; j < k() + 1; j++)
{
yhat[i] += xs[i, j] * c[j];
}
}
return yhat;
}
public double[,] CovarianceMatrix()
{
// compute the coefficient covariance matrix
double[,] twodYs = DataHelper.dbl2DArray(Ys);
double[,] XYs = DataHelper.dblarrayUnion(Xs, twodYs);
double[,] cov;
int info;
alglib.linearmodel lm;
alglib.lrreport rpt;
try
{
alglib.lrbuild(XYs, N(), k(), out info, out lm, out rpt);
cov = rpt.c;
}
catch
{
throw;
}
return cov;
}
public double[] StandardErrors()
{
// compute the x std errors and p-values
double[,] cov = CovarianceMatrix();
double[] se = new double[k()];
if (cov.GetLength(0) > 0 && cov.GetLength(1) > 0)
{
for (int j = 0; j < k(); j++)
{
se[j] = Math.Sqrt(cov[j, j]);
}
}
return se;
}
public double[] PValues()
{
double[] c = Coefficients;
double[,] cov = CovarianceMatrix();
double[] se = StandardErrors();
double[] pv = new double[k()];
if (cov.GetLength(0) > 0 && cov.GetLength(1) > 0)
{
for (int j = 0; j < k(); j++)
{
se[j] = Math.Sqrt(cov[j, j]);
try
{
pv[j] = 2 * (1 - alglib.studenttdistribution(df(), Math.Abs(c[j] / se[j])));
}
catch
{
}
}
}
return pv;
}
public string AICFormula()
{
return "";
}
//Added By Suman for SEP Validation changes
public string[] SEPValidationCheck()
{
string[] sepChk = new string[k()];
for (int cnt = 0; cnt < sepChk.Length; cnt++)
{
if (Valid() == true)
{
sepChk[cnt] = "Pass";
}
else
{
sepChk[cnt] = "Fail";
}
}
return sepChk;
}
}
Here is GetHashCode():
public override int GetHashCode()
{
return x.GetHashCode() ^ y.GetHashCode();
}
My R2():
public double R2()
{
return (1 - (ResidualSS() / TotalSS()));
}
For the majority of types, you can't sort by GetHashCode() since the hash code calculations aren't necessarily going to sort in the same order as the value. You can only sort on GetHashCode() reliably when it is the value itself (such as int), but at that point there isn't any point in getting the hash code since you already have the value. Also you didn't show what R2() and Valid() methods do, so who knows what kind of unholyness is going on in there.
EDIT (OP updated code):
Your GetHashCode() usage is definitely not sortable. Nor is adding a boolean to a double. 5 + 0 vs. 2 + 1 is not going to sort correctly (assuming you want the falses up front?)
Just use ((Model)x).R2() vs ((Model)y).R2() as the comparison, if you want the falses or trues up front, you can add something like:
if (x.Valid && !y.Valid)
return 1;
if (!x.Valid && y.Valid)
return -1;
//then do your R2 comparisons here.
That preamble will cause the non valids to always come first and then sort by R2s. You can reverse if you want to go the other way.
Your .Valid() method is returning boolean, and then you go to find hash code. Is this expected from your code?
Basically, you are doing some double value + boolean.GetHashCode(). Solution would be to not use .Valid() in your right side expression. Check that separately.
private class R2SortHelper : System.Collections.IComparer
{
public int Compare(object x, object y)
{
Model xModel = (Model)x;
Model yModel = (Model)y;
if((Model.Valid()==true) && (yModel.Valid()==true))
{
double m1 = xModel.R2() + xModel.GetHashCode();
double m2 = yModel.R2() + yModel.GetHashCode();
return (m1 > m2) ? -1 : ((m1 < m2) ? 1 :0);
}
return 0;
}
}

My single layer perceptrone is not working

Here is code.
public class Adaline
{
private int _layer;
public int Layer { get { return _layer; } }
private int _epoch;
public int Epoch { get { return _epoch; } }
private double _error;
public double Error { get { return _error; } }
private double[] _weights;
public Adaline(int layer)
{
_layer = layer;
_weights = new double[layer];
Reset();
}
public void Reset()
{
Random r = new Random();
for (int i = 0; i < _layer; i++)
_weights[i] = r.NextDouble() - 0.5;
_error = 1;
}
public void Train(BasicTrainSet<double> trainset, double learnRate)
{
double ers = 0;
for(int p = 0; p < trainset.DataCount; p++)
{
double result = Compute(trainset.Input[p], true);
double error = trainset.Output[p] - result;
for (int i = 0; i < _weights.Length; i++)
{
_weights[i] += error * trainset.Input[p][i] * learnRate;
}
ers += Math.Abs(error);
}
_epoch++;
_error = ers;
}
public double Compute(double[] input, bool quan)
{
double result = 0;
for (int i = 0; i < _layer; i++)
result += Math.Tanh(_weights[i] * input[i]);
//double result = _weights.Zip(input, (a, b) => Math.Tanh(a * b)).Sum();
return quan ? (result >= 0 ? 1 : 0) : result;
}
}
When I tried to train and gate like this, it works like this.
Up four results are from this code
This is pretty weird, because there is not any problem with algorithm.
Weights are getting bigger and bigger. Where did i mistake?
In your code to compute the output of each neuron, you aren't applying the activation function correctly. You need to find the dot product between the weights and the inputs into each neuron then apply the activation function after. You are applying the activation function after each weighted accumulation, which is not correct.
Accumulate, then apply the activation function:
public double Compute(double[] input, bool quan)
{
double result = 0;
for (int i = 0; i < _layer; i++)
result += _weights[i] * input[i]; // Change - accumulate first
result = Math.Tanh(result); // Change - now apply activation function
return quan ? (result >= 0 ? 1 : 0) : result;
}

What is wrong with this QuickSort algorithm in C#?

Sadly, I've got problems with Quicksort as well. I've discussed it with other students and tried the standard troubleshooting methods. But I can't find what I'm doing wrong...
static int partition(int[] A)
{
int pivot, i, j;
i = 0;
j = A.Length;
pivot = A[i];
do
{
do
{
i++;
}
while (A[i] < pivot || i < j);
do
{
j--;
}
while (A[j] > pivot);
swap(ref A[i], ref A[j]);
}
while (i < j);
if (i <= j)
{
swap(ref A[i], ref A[j]);
swap(ref A[0], ref A[j]);
}
return j;
}
static void swap(ref int a, ref int b)
{
int aCopy = a;
a = b;
b = aCopy;
}
static int[] QuickSort(int[] A)
{
int s;
int left = 0;
int right = A.Length;
int[] B, C;
if (A.Length == 0 || A.Length == 1)
return A;
else
{
s = partition(A);
B = new int[s];
C = new int[right - s];
for (int i = left; i < s; i++)
{
B[i - left] = A[i];
}
for (int i = s; i < right; i++)
{
C[i - s] = A[i];
}
QuickSort(B);
QuickSort(C);
return A;
}
}
The debugger constantly gives me an 'IndexOutOfRangeException' with the j variable. I've tried adjusting the
while (A[i] < pivot || i < j);
part. But that didn't do anything (well, it resulted in a StackOverflowException once).
You have j = A.Length, but it should really be j = A.Length - 1 (based on the loops you are using).
Well, there are too many mistakes, like you are swapping back the elements you have already swapped and so on. This is your solution with your way of doing it without mistakes that also includes what has been said in previous answers and comments. Nice coding!
static int partition(int[] A)
{
int pivot, i, j;
i = 0;
j = A.Length-1;
pivot = A[0];
while (i < j)
{
while (A[i] < pivot && i < j)
{
i++;
}
while (A[j] > pivot && j > i)
{
j--;
}
swap(ref A[i], ref A[j]);
}
return j;
}
static void swap(ref int a, ref int b)
{
int aCopy = a;
a = b;
b = aCopy;
}
static int[] QuickSort(int[] A)
{
int s;
int left = 0;
int right = A.Length;
int[] B, C;
if (A.Length == 0 || A.Length == 1)
return A;
else
{
s = partition(A);
B = new int[s];
C = new int[right - s - 1];
for (int i = left; i < s; i++)
{
B[i - left] = A[i];
}
for (int i = s + 1; i < right; i++)
{
C[i - s - 1] = A[i];
}
B = QuickSort(B);
C = QuickSort(C);
for(int i = 0; i < s;i++)
A[i] = B[i - left];
for (int i = s + 1; i < right; i++)
{
A[i] = C[i - s - 1];
}
return A;
}
}
You are always including the first item as one under the pivot without checking the value, then you are including all the other values even if they are above the pivot, just because i < j, as you use the 'or' operator (||) between the conditions.
This:
do
{
i++;
}
while (A[i] < pivot || i < j);
Should be:
while (i < j && A[i] < pivot);
{
i++;
}
I notice that you are still trying tio use Quicksort even when the segments are 2 or 3 elements long. I think you need to use an alternate sort technique when you have reduced the size of the input arrays to less than 4 elements. Remember, the quick sort requires that you break up the input array into a "pivot" element, and 2 separate arrays, one with all the elements less than the pivot, and the other with all the elements greater than the pivot. To do this the input array has to have at least 3 elements. This may be the source of your issue.
Here's a generic quicksort that uses this technique...
public delegate int CompareDlg<T>(T thisOne, T otherOne);
public class QuickSort<T>
{
#region private variable to sort inplace
readonly private IList<T> itms;
private CompareDlg<T> cmp;
#endregion private variable to sort inplace
#region properties
public CompareDlg<T> Comparer
{
set { cmp = value; }
get { return cmp; }
}
#endregion properties
#region ctor
private QuickSort() { } // Hide parameterless constructor
/// <summary>
/// Constructor, requires generic List of Ts
/// where T must implement CompareTo() method...
/// </summary>
/// <param name="list">List of T elements to sort</param>
public QuickSort(IList<T> list) : this(list, null) { }
/// <summary>
/// Constructor, requires generic List of Ts
/// where T must implement CompareTo() method,
/// And Compare method to use when sorting,
/// (Overrides default CompareTo() implemented by T) ...
/// </summary>
/// <param name="list">List of T elements to sort</param>
/// <param name="compareDelegate">Method to use to compare elements</param>
public QuickSort(IList<T> list, CompareDlg<T> compareDelegate)
: this()
{
if (list.Count == 0) throw new InvalidOperationException(
"Empty List passed to QuickSort.");
var first = default(T);
if (typeof(T).IsClass)
{
foreach (var t in list)
if (!((first = t).Equals(default(T))))
break;
if (first.Equals(default(T)))
throw new InvalidOperationException(
"List passed to QuickSort contains all nulls.");
}
if (compareDelegate == null && !(first is IComparable<T>))
throw new InvalidOperationException(string.Format(
"Type {0} does not implement IComparable<{0}>. " +
"Generic Type T must either implement IComparable " +
"or a comparison delegate must be provided.", typeof(T)));
itms = list;
cmp += compareDelegate ?? CompareDefault;
}
#endregion ctor
#region public sort method
public static void Sort(IList<T> itms) { (new QuickSort<T>(itms)).Sort(); }
public void Sort(bool asc) { Sort(0, itms.Count - 1, asc); }
public static void Sort(IList<T> itms, CompareDlg<T> compareDelegate)
{ (new QuickSort<T>(itms, compareDelegate)).Sort(); }
public void Sort() { Sort(0, itms.Count - 1, true); }
/// <summary>
/// Executes QuickSort algorithm
/// </summary>
/// <param name="L">Index of left-hand boundary of partition to sort</param>
/// <param name="R">Index of right-hand boundary of partition to sort</param>
/// <param name="asc"></param>
private void Sort(int L, int R, bool asc)
{
// Call iSort (insertion-sort)
if (R - L < 4) iSort(L, R);
//for partitions smaller than 4 elements
else
{
int i = (L + R) / 2, j = R - 1;
// Next three lines to set upper and lower bounds
if (Comparer(itms[L], itms[i]) > 0 == asc) Swap(L, i);
if (Comparer(itms[L], itms[R]) > 0 == asc) Swap(L, R);
if (Comparer(itms[i], itms[R]) > 0 == asc) Swap(i, R);
Swap(i, j);
// --------------------------------------------
var p = itms[j]; // p = itms[j] is pivot element
i = L;
while (true)
{
while (Comparer(itms[++i], p) < 0 == asc) { }
while (Comparer(itms[--j], p) > 0 == asc) { }
if (j < i) break;
Swap(i, j);
}
Swap(i, R - 1);
Sort(L, i, asc); // Sort Left partition
Sort(i + 1, R, asc); // Sort Right partition
}
}
private static int CompareDefault(T thisOne, T otherOne)
{
if(!(thisOne is IComparable<T>))
throw new InvalidCastException(
"Type must implement IComparable<T>");
return (thisOne as IComparable<T>).CompareTo(otherOne);
}
#endregion public sort method
#region private Helper methods
private void Swap(int L, int R)
{
var t = itms[L];
itms[L] = itms[R];
itms[R] = t;
}
private void iSort(int L, int R)
{
for (var i = L; i <= R; i++)
{
var t = itms[i];
var j = i;
while ((j > L) && Comparer(itms[j - 1], t) > 0)
{
itms[j] = itms[j - 1];
j--;
}
itms[j] = t;
}
}
#endregion private Helper methods
}

The order of my custom BinaryHeap is not always correct

I created a BinaryHeap<T> class.
public class BinaryHeap<T>
{
private List<T> _heap;
// Constructor
public BinaryHeap(int capacity, IComparer<T> comparer)
{
this._heap = new List<T>(capacity);
Comparer = comparer ?? Comparer<T>.Default;
}
// Constructor
public BinaryHeap(int capacity) : this(capacity, (IComparer<T>)null) { }
// Gets/sets IComparer
public IComparer<T> Comparer { get; private set; }
// Gets/sets the capacity
public int Capacity
{
get { return this._heap.Capacity; }
set { this._heap.Capacity = value; }
}
// Gets the number of elements
public int Count
{
get { return this._heap.Count; }
}
// Inserts the specified element within this BinaryHeap.
public void Insert(T element)
{
// Add the element as the last element.
this._heap.Add(element);
// Index of last added element.
int last = this._heap.Count - 1;
int parent;
T swap;
while (last > 0)
{
parent = (last - 1) >> 1; // parent(i) = (i-1)/2
if (Comparer.Compare(this._heap[parent], this._heap[last]) <= 0)
break; // exit while if the current node is lesser than its parent.
swap = this._heap[last]; // If the parent is greater
this._heap[last] = this._heap[parent]; // than the current node,
this._heap[parent] = swap; // then swap them.
last = parent; // Updates index of last added element.
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public T Remove()
{
if (this._heap.Count > 0)
{
// Save the element.
T result = this._heap[0];
int lastIndex = this._heap.Count - 1; // The last element moves
this._heap[0] = this._heap[lastIndex]; // moves to the root
this._heap.RemoveAt(lastIndex); // of this tree.
lastIndex--; // Update position of last element.
int i = 0; // Position of moved node.
while (i < lastIndex)
{
int minIndex = i;
int left = (i << 1) + 1; // left(i) = (i*2)+1
if (left < lastIndex && Comparer.Compare(this._heap[left], this._heap[minIndex]) < 0)
{
minIndex = left;
}
int right = left + 1; // right(i) = (i*2)+2
if (right < lastIndex && Comparer.Compare(this._heap[right], this._heap[minIndex]) < 0)
{
minIndex = right;
}
if (minIndex != i)
{
// If one of the two children is lesser than the parent,
// then swap the latter with the lesser child.
T temp = this._heap[i];
this._heap[i] = this._heap[minIndex];
this._heap[minIndex] = temp;
i = minIndex;
}
else break;
}
// Return the minimum element that has been removed.
return result;
}
else throw new InvalidOperationException("BinaryHeap is empty.");
}
}
In performed some test, for example the following test as simple queue.
BinaryHeap<int> q = new BinaryHeap<int>(0);
int count = 50;
for (int i = 0; i < count; i++)
q.Insert(i);
for (int i = 0; i < count; i++)
Console.WriteLine(q.Remove());
Console.ReadLine();
The returned elements should be in ascending order, but the third-last element and the penultimate element are almost always in wrong order. For example, with count = 50, the output is:
// previous element are in correct order
44
45
46
48 // wrong order
47 // wrong order
49
Testing several count values, this problem does not always occur.
Why?
How can I resolve this problem?
The symptom indicates a problem in the Remove() method.
Your lastIndex var is pointing at the last item, so in the 2 calculations of minIndex you should use <=
// if (left < lastIndex && ...)
if (left <= lastIndex && ...)
and the same for right.

Categories

Resources