I'm making an application whose job is to generate two lists and display them on demand. As well as update the values every second.
I need to update the list in such a way so that the oldest value in the list is replaced first. How would I do that? Below is my code in it's current state.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Data_Collector
{
//What is needed?
//Need to generate a list of both metric values, and imperial values.
//Need to be able to display a list
public class IMeasuringDevice_Helper
{
private int limit; //Limits the list size.
private float RNDouble;
public void MetricValueGenerator()
{
limit = limit + 1;
Console.WriteLine("Limit Level = " + limit);
if (limit <= 10)
{
List<float> MetricValueGenerated = new List<float>();
Random rnd = new Random();
float rndINT = rnd.Next(1, 10);
RNDouble = rndINT / 10;
Console.WriteLine(RNDouble);
MetricValueGenerated.Add(RNDouble);
}
else
{
Console.WriteLine("limit reached");
}
}
public void ImperialValueGenerator()
{
//TODO
}
}
}
You will need a Queue for this, but you will need to extend it. The default C# Queue is First-In, First-Out (exactly the semantic you want), but does not subscribe to limits the way your code currently handles them. It simply grows by a growth factor if full.
So you will want to extend the Queue object and override the Enqueue method to do what you want. It will probably look a little like this:
public class BoundedQueue<T> : Queue<T>
{
private readonly int _bound;
public BoundedQueue(int bound)
{
_bound = bound;
}
public new void Enqueue(T item)
{
if(Count >= _bound)
{
throw new IndexOutOfRangeException("limit reached");
// If simply throwing an exception isn't cool, you can also do the following to pop off the oldest item:
// base.Dequeue();
}
base.Enqueue(item);
}
}
The only thing to be aware of is when you turn this into some other kind of object for display, you may see it in the reverse order you expect, as the oldest item will be at the 'top' of the queue. You can sort this out by simply calling the Reverse() method that works with most LINQ-enabled objects.
If you don't want to do any class extensions as #YYY has suggested, pretty much replace List with Queue, replace .add() with .Enqueue() and instead of oldestValue = yourList[oldestIndex] use oldestValue = yourQueue.Dequeue().
Aside from your question, your variables should start with a lowercase letter, and RNDouble = rndINT / 10; is going to end up = 0 most of the time because you should divide by 10.0 instead of 10.
Ok so I was bored... (also I wouldn't go with this approach, but I'm guessing you're learning and haven't been taught about Queues, so this might help with Lists):
public class MeasuringDevice_Helper
{
private const int LIMIT = 10; // This is a constant value, so should be defined IN_CAPS in class definition
List<double> metricValues = new List<double>(LIMIT); // This needs to be at class level so it doesn't get lost
Random rnd = new Random(); // This is used frequently so define at class level
public void GenerateMetricValue() // This is now named like the action it performs
{
Console.WriteLine("Current metric values = " + metricValues.Count);
if (metricValues.Count < LIMIT) // This should just be < not <=
{
float rndInt = rnd.Next(1, 10);
double rndDouble = rndInt / 10.0; // An Int divided by Int will = Int
Console.WriteLine("Added " + rndDouble);
metricValues.Add(rndDouble);
}
else
{
Console.WriteLine("limit reached");
}
}
public double PopOldestMetricValue()
{
double value = metricValues[0];
metricValues.RemoveAt(0);
return value;
}
}
Related
this is the class
data members and getters and setters:
class Queue
{
int[] data; //array
int size; //size of array
int counter; //checker if array is fully or impty
int first; // first element of array
int last; // last element of arry
}
fixed size constructor
public Queue(int size) {
data = new int[this.size];
size = this.size;
counter = 0;
first = 0;
last = 0;
}
default constructor
public Queue() { //default constructor
data = new int[10];
size = 10;
counter = 0;
first = 0;
last = 0;
}
enqueing function "push"
public bool Enqueue(int num) {
bool result;
if (counter<size)
{
data[last] = num;
last++;
if(last == size)
last = 0;
counter++;
return result = true
}
else
{
return result = false;
}
return result;
}
dequeueing function "pop"
public int Dequeueing() {
int result=-1;
// I know that i should make this nullable function but (-1) good at this time
if (counter>0)
{
result = data[first];
first++;
if (first==size)
first = 0;
counter--;
}
return result;
}
on the main it is excute the enqueueing good but dequeueing its (first++)and (counter--) but it doesn't delete first input **
**why it doesn't delete (17)
static void Main(string[] args)
{
Queue q1 = new Queue();
q1.Enqueue(17);
q1.Enqueue(20);
q1.Enqueue(25);
q1.Enqueue(15);
q1.Enqueue(14);
q1.Enqueue(13);
q1.Dequeueing();
Console.ReadLine();
}
Your expectation that once you do first++; and counter--;, the data[first] will be deleted is misplaced. Frankly, if it will somehow cause data[first] to be deleted then rest of your code will not work. What that code does is to simply Dequeue. The Queue bounds are marked using first and last, with the help of counter, so only those things need to change and no array element deletion needs to happen
Let me help you understand your code a bit. Your Queue class is what is called a Circular Queue. In such an implementation, you have the benefit that the amount of storage allocated for the Queue is "fixed" and determined on construction. This also implies that when you Enqueue there will be no extra memory requested and when you Dequeue there will be no memory released.
If you wish to somehow identify an unoccupied slot in the array, you can use special values. For example, making int?[] data, you can store var item = data[front]; data[front] = null before Dequeueing. Or, you can use a routine like GetQueueData to return all the elements in the Queue as a separate IEnumerable
public IEnumerable<int> GetQueueData() {
for (int i = first, cnt = 0; cnt < counter; i = (i + 1) % size, ++cnt)
yield return data[i];
}
The array named data will remain in the same state as when you created it as you have not done anything to make this otherwise.
Ok so I made a code that includes string/int arrays with some for loops. In my code there is a part where it counts more than 1 of the string but how do you make the string plural when there is more than one? Here's a part of the code that I'm talking about:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VoidFunctions : MonoBehaviour
{
public string[] Enemies = {"Ghoul", "Skeleton", "Zombie"};
public int[] enemyCount = {1, 2, 2};
public virtual void Start()
{
for (int i = 0; i < Enemies.Length; i++)
{
Fight(Enemies[i], enemyCount[i]);
}
}
void Fight(string enemy, int amount)
{
print(this.name + " encountered a " + enemy);
print(this.name + " killed " + amount + " " + enemy);
}
}
So for the second string "Skeleton", there is 2 killed but it comes out "killed 2 Skeleton"...how do you make it plural?
As Ness Rosales stated, you could use pluralization software or conversion charts. Although for this type of project, I would consider the use of such software overkill if you have under 20 items.
What I would do is change the array of enemies to have both singular and plural forms of each noun:
public string[][2] Enemies =
{
{“Ghoul”, ”Ghouls”}, {“Skeleton”, “Skeletons”}, {“Zombie”, “Zombies”}
};
From here, you can make an if/else statement to get string 0 or 1 of each noun depending on the quantity.
There are multiple ways to achieve that:
a simple way that simply adds an s at the end (western language dependent)
a robust way with PluralizationService as pointed out by #Ness Rosales
Code:
using System;
using System.Data.Entity.Design.PluralizationServices;
using System.Globalization;
namespace WindowsFormsApp1
{
internal class MyClass
{
private static void CheckParameters(string word, int count)
{
if (string.IsNullOrWhiteSpace(word))
throw new ArgumentException("Value cannot be null or whitespace.", nameof(word));
if (count <= 0)
throw new ArgumentOutOfRangeException(nameof(count));
}
public static string GetLabel1(string word, int count)
{
CheckParameters(word, count);
var label = $"{word}{(count > 1 ? "s" : string.Empty)}";
return label;
}
public static string GetLabel2(string word, int count)
{
CheckParameters(word, count);
// TODO this should be a member instead of being instantiated every time
var service = PluralizationService.CreateService(CultureInfo.CurrentCulture);
var label = count > 1 ? service.Pluralize(word) : service.Singularize(word);
return label;
}
}
}
I got a strange C# programming problem. There is a data retrieval in groups of random lengths of number groups. The numbers should be all unique, like:
group[1]{1,2,15};
group[2]{3,4,7,33,22,100};
group[3]{11,12,9};
// Now there is a routine that adds a number to a group.
// For the example, just imagine the active group looks like:
// group[active]=(10,5,0)
group[active].add(new_number);
// Now if 100 were to be added to the active group
// then the active group should be merged to group[2]
// (as that one already contained 100)
// And then as a result it would like
group[1]{1,2,15};
group[2]{3,4,7,33,22,100,10,5,0}; // 10 5 0 added to group[2]
group[3]{11,12,9};
// 100 wasn't added to group[2] since it was already in there.
If the number to be added is already used (not unique) in a previous group.
Then I should merge all numbers in the active group towards that previous group, so I don’t get double numbers.
So in the above example if number 100 was added to the active
group, then all numbers in the group[active] should be merged into group[2].
And then the group[active] should start clean fresh again without any items. And since 100 was already in group[2] it should not be added double.
I am not entirely sure on how to deal with this in a proper way.
As an important criteria here is that it has to work fast.
I will have around minimal 30 groups (upper-bound unknown might be 2000 or more), and their length on average contains five integer numbers, but it could be much longer or only one number.
I kind of feel that I am reinventing the wheel here.
I wonder what this problem is called (does it go by a name, some sorting, or grouping math problem)?, with a name I might find some articles related to such problems.
But maybe it’s indeed something new, then what would be recommended? Should I use list of lists or a dictionary of lists.. or something else? Somehow the checking if the number is already present should be done fast.
I'm thinking along this path now and am not sure if it’s the best.
Instead of a single number, I use a struct now. It wasn't written in the original question as I was afraid, explaining that would make it too complex.
struct data{int ID; int additionalNumber}
Dictionary <int,List<data>> group =new Dictionary<int, List<data>>();
I can step aside from using a struct in here. A lookup list could connect the other data to the proper index. So this makes it again more close to the original description.
On a side note, great answers are given.
So far I don’t know yet what would work best for me in my situation.
Note on the selected answer
Several answers were given here, but I went for the pure dictionary solution.
Just as a note for people in similar problem scenarios: I'd still recommend testing, and maybe the others work better for you. It’s just that in my case currently it worked best. The code was also quite short which I liked, and a dictionary adds also other handy options for my future coding on this.
I would go with Dictionary<int, HashSet<int>>, since you want to avoid duplicates and want a fast way to check if given number already exists:
Usage example:
var groups = new Dictionary<int, HashSet<int>>();
// populate the groups
groups[1] = new HashSet<int>(new[] { 1,2,15 });
groups[2] = new HashSet<int>(new[] { 3,4,7,33,22,100 });
int number = 5;
int groupId = 4;
bool numberExists = groups.Values.Any(x => x.Contains(number));
// if there is already a group that contains the number
// merge it with the current group and add the new number
if (numberExists)
{
var group = groups.First(kvp => kvp.Value.Contains(number));
groups[group.Key].UnionWith(groups[groupId]);
groups[groupId] = new HashSet<int>();
}
// otherwise just add the new number
else
{
groups[groupId].Add(number);
}
From what I gather you want to iteratively assign numbers to groups satisfying these conditions:
Each number can be contained in only one of the groups
Groups are sets (numbers can occur only once in given group)
If number n exists in group g and we try to add it to group g', all numbers from g' should be transferred to g instead (avoiding repetitions in g)
Although approaches utilizing Dictionary<int, HashSet<int>> are correct, here's another one (more mathematically based).
You could simply maintain a Dictionary<int, int>, in which the key would be the number, and the corresponding value would indicate the group, to which that number belongs (this stems from condition 1.). And here's the add routine:
//let's assume dict is a reference to the dictionary
//k is a number, and g is a group
void AddNumber(int k, int g)
{
//if k already has assigned a group, we assign all numbers from g
//to k's group (which should be O(n))
if(dict.ContainsKey(k) && dict[k] != g)
{
foreach(var keyValuePair in dict.Where(kvp => kvp.Value == g).ToList())
dict[keyValuePair.Key] = dict[k];
}
//otherwise simply assign number k to group g (which should be O(1))
else
{
dict[k] = g;
}
}
Notice that from a mathematical point of view what you want to model is a function from a set of numbers to a set of groups.
I have kept it as easy to follow as I can, trying not to impact the speed or deviate from the spec.
Create a class called Groups.cs and copy and paste this code into it:
using System;
using System.Collections.Generic;
namespace XXXNAMESPACEXXX
{
public static class Groups
{
public static List<List<int>> group { get; set; }
public static int active { get; set; }
public static void AddNumberToGroup(int numberToAdd, int groupToAddItTo)
{
try
{
if (group == null)
{
group = new List<List<int>>();
}
while (group.Count < groupToAddItTo)
{
group.Add(new List<int>());
}
int IndexOfListToRefresh = -1;
List<int> NumbersToMove = new List<int>();
foreach (List<int> Numbers in group)
{
if (Numbers.Contains(numberToAdd) && (group.IndexOf(Numbers) + 1) != groupToAddItTo)
{
active = group.IndexOf(Numbers) + 1;
IndexOfListToRefresh = group.IndexOf(Numbers);
foreach (int Number in Numbers)
{
NumbersToMove.Add(Number);
}
}
}
foreach (int Number in NumbersToMove)
{
if (!group[groupToAddItTo - 1].Contains(Number))
{
group[groupToAddItTo - 1].Add(Number);
}
}
if (!group[groupToAddItTo - 1].Contains(numberToAdd))
{
group[groupToAddItTo - 1].Add(numberToAdd);
}
if (IndexOfListToRefresh != -1)
{
group[IndexOfListToRefresh] = new List<int>();
}
}
catch//(Exception ex)
{
//Exception handling here
}
}
public static string GetString()
{
string MethodResult = "";
try
{
string Working = "";
bool FirstPass = true;
foreach (List<int> Numbers in group)
{
if (!FirstPass)
{
Working += "\r\n";
}
else
{
FirstPass = false;
}
Working += "group[" + (group.IndexOf(Numbers) + 1) + "]{";
bool InnerFirstPass = true;
foreach (int Number in Numbers)
{
if (!InnerFirstPass)
{
Working += ", ";
}
else
{
InnerFirstPass = false;
}
Working += Number.ToString();
}
Working += "};";
if ((active - 1) == group.IndexOf(Numbers))
{
Working += " //<active>";
}
}
MethodResult = Working;
}
catch//(Exception ex)
{
//Exception handling here
}
return MethodResult;
}
}
}
I don't know if foreach is more or less efficient than standard for loops, so I have made an alternative version that uses standard for loops:
using System;
using System.Collections.Generic;
namespace XXXNAMESPACEXXX
{
public static class Groups
{
public static List<List<int>> group { get; set; }
public static int active { get; set; }
public static void AddNumberToGroup(int numberToAdd, int groupToAddItTo)
{
try
{
if (group == null)
{
group = new List<List<int>>();
}
while (group.Count < groupToAddItTo)
{
group.Add(new List<int>());
}
int IndexOfListToRefresh = -1;
List<int> NumbersToMove = new List<int>();
for(int i = 0; i < group.Count; i++)
{
List<int> Numbers = group[i];
int IndexOfNumbers = group.IndexOf(Numbers) + 1;
if (Numbers.Contains(numberToAdd) && IndexOfNumbers != groupToAddItTo)
{
active = IndexOfNumbers;
IndexOfListToRefresh = IndexOfNumbers - 1;
for (int j = 0; j < Numbers.Count; j++)
{
int Number = NumbersToMove[j];
NumbersToMove.Add(Number);
}
}
}
for(int i = 0; i < NumbersToMove.Count; i++)
{
int Number = NumbersToMove[i];
if (!group[groupToAddItTo - 1].Contains(Number))
{
group[groupToAddItTo - 1].Add(Number);
}
}
if (!group[groupToAddItTo - 1].Contains(numberToAdd))
{
group[groupToAddItTo - 1].Add(numberToAdd);
}
if (IndexOfListToRefresh != -1)
{
group[IndexOfListToRefresh] = new List<int>();
}
}
catch//(Exception ex)
{
//Exception handling here
}
}
public static string GetString()
{
string MethodResult = "";
try
{
string Working = "";
bool FirstPass = true;
for(int i = 0; i < group.Count; i++)
{
List<int> Numbers = group[i];
if (!FirstPass)
{
Working += "\r\n";
}
else
{
FirstPass = false;
}
Working += "group[" + (group.IndexOf(Numbers) + 1) + "]{";
bool InnerFirstPass = true;
for(int j = 0; j < Numbers.Count; j++)
{
int Number = Numbers[j];
if (!InnerFirstPass)
{
Working += ", ";
}
else
{
InnerFirstPass = false;
}
Working += Number.ToString();
}
Working += "};";
if ((active - 1) == group.IndexOf(Numbers))
{
Working += " //<active>";
}
}
MethodResult = Working;
}
catch//(Exception ex)
{
//Exception handling here
}
return MethodResult;
}
}
}
Both implimentations contain the group variable and two methods, which are; AddNumberToGroup and GetString, where GetString is used to check the current status of the group variable.
Note: You'll need to replace XXXNAMESPACEXXX with the Namespace of your project. Hint: Take this from another class.
When adding an item to your List, do this:
int NumberToAdd = 10;
int GroupToAddItTo = 2;
AddNumberToGroup(NumberToAdd, GroupToAddItTo);
...or...
AddNumberToGroup(10, 2);
In the example above, I am adding the number 10 to group 2.
Test the speed with the following:
DateTime StartTime = DateTime.Now;
int NumberOfTimesToRepeatTest = 1000;
for (int i = 0; i < NumberOfTimesToRepeatTest; i++)
{
Groups.AddNumberToGroup(4, 1);
Groups.AddNumberToGroup(3, 1);
Groups.AddNumberToGroup(8, 2);
Groups.AddNumberToGroup(5, 2);
Groups.AddNumberToGroup(7, 3);
Groups.AddNumberToGroup(3, 3);
Groups.AddNumberToGroup(8, 4);
Groups.AddNumberToGroup(43, 4);
Groups.AddNumberToGroup(100, 5);
Groups.AddNumberToGroup(1, 5);
Groups.AddNumberToGroup(5, 6);
Groups.AddNumberToGroup(78, 6);
Groups.AddNumberToGroup(34, 7);
Groups.AddNumberToGroup(456, 7);
Groups.AddNumberToGroup(456, 8);
Groups.AddNumberToGroup(7, 8);
Groups.AddNumberToGroup(7, 9);
}
long MillisecondsTaken = DateTime.Now.Ticks - StartTime.Ticks;
Console.WriteLine(Groups.GetString());
Console.WriteLine("Process took: " + MillisecondsTaken);
I think this is what you need. Let me know if I misunderstood anything in the question.
As far as I can tell it's brilliant, it's fast and it's tested.
Enjoy!
...and one more thing:
For the little windows interface app, I just created a simple winforms app with three textboxes (one set to multiline) and a button.
Then, after adding the Groups class above, in the button-click event I wrote the following:
private void BtnAdd_Click(object sender, EventArgs e)
{
try
{
int Group = int.Parse(TxtGroup.Text);
int Number = int.Parse(TxtNumber.Text);
Groups.AddNumberToGroup(Number, Group);
TxtOutput.Text = Groups.GetString();
}
catch//(Exception ex)
{
//Exception handling here
}
}
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I am doing an app that uses Random() class and the method that fills the numbers in the array has a bug. It still adds the same values in the array. It is supposed to check if the random generated value is already in the array and if it is not in there to add it. Else it should leave the loop and generate another integer.
Code:
/*
* Method to fill in the array with numbers
*/
public void FillArrayWithRandomNumbers()
{
bool unique = false;
for (int i = 0; i < NrOfWantedNumbers; i++)
{
_randomNumber = rndm.Next(1, MaxValue) + 1;
while (unique == false)
{
foreach (var item in storeLottoNum)
{
if (item == _randomNumber)
unique = false;
else
{
unique = true;
}
}
}
if (unique)
storeLottoNum[storeNextIndex] = _randomNumber;
++storeNextIndex;
unique = false;
}
}
Your code probably would work if you had added braces. You indented the code under your if statement, but didn't add any braces. This isn't Python.
if (unique == true) // By the way, can be shortened to if (unique)
{ // <-- You were missing this
storeLottoNum[storeNextIndex] = _randomNumber;
++storeNextIndex;
unique = false;
} // <-- You were missing this too
An easier way to do this though:
unique = !storeLottoNum.Contains(item)
That one line can complete eliminate your while loop and everything in it. This will set your unique variable automatically.
assuming that :
storeLottoNum is type int[];
storeLottoNum has size = NrOfWantedNumbers
storeNextIndex is initialized with 0
EDIT: Added comments to the code
then try:
/*
* Method to fill in the array with numbers
*/
public void FillArrayWithRandomNumbers()
{
for (int i = 0; i < NrOfWantedNumbers; i++) // for each number you want
{
do
{
unique = true; // Assume new value is unique
_randomNumber = rndm.Next(1, MaxValue) + 1; // get the random number
for (int item = 0; item < storeNextIndex; item++) // Check each element with a value in your array
{
if (storeLottoNum[item] == _randomNumber) // if the value is the same
{
unique = false; // It is not unique
break; // break the loop, we don't care about the other values
}
}
} while (unique == false); // if it was not unique, try again
storeLottoNum[storeNextIndex++] = _randomNumber; // with unique value, save in the array and increment the 'storeNextIndex'
}
}
Another option is to use the default characteristics of a Dictionary<> or HashSet<>.
These type of collections do not allow duplicate keys by default. When one is encountered in a dictionary, an exception is thrown. When one is encountered in a hashset, the duplicate is simply discarded. By wrapping the process of adding a KeyValuePair to a dictionary collection type, in a try/catch block and simply absorbing the exception when it's thrown, you can very easily build a list of random unique values. See the example below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LottoSample
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
// Dictionary to store list of random numbers
Dictionary<int, bool> _lottoNumbers = new Dictionary<int, bool>();
// Total amount of numbers
int count = 50;
while (count > 0)
{
// Attempt to add a new random number into the Dictionary of lotto numbers and reduce count by 1
try
{
_lottoNumbers.Add(random.Next(1, 100), true);
count--;
}
catch
{
// Dictionaries and HashSets don't allow duplicate keys by default.
// When an attempt to add a duplicate to a dictionary is encountered, an exception is thrown.
// We simply ignore it. Because we still want X number of random numbers,
// we don't bother reducing the counter unless it succeeds.
}
}
// Print the result
foreach (var num in _lottoNumbers)
Console.WriteLine(num);
// Prevent the console window from closing
Console.ReadLine();
}
}
}
And here is the solution using a HashSet<>. This eliminates the need of having to use a Try/Catch block altogether. (since it is typically considered to be bad practice in a professional setting):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LottoSample
{
class Program
{
static void Main(string[] args)
{
Random _random = new Random();
// HashSet to store list of random numbers
// When an attempt to add a duplicate is
// encountered. They simply ignore the attempt silently.
HashSet<int> _lottoNumbers = new HashSet<int>();
// Total amount of numbers
int _count = 50;
int _randomNumber = -1;
while (_count > 0)
{
// Get a random number
_randomNumber = _random.Next(1, 100);
_lottoNumbers.Add(_randomNumber);
_count--;
}
// Print the result
foreach (var num in _lottoNumbers)
Console.WriteLine(num);
// Prevent the console window from closing
Console.ReadLine();
}
}
}
I know, I got a little carried away. Here is a more object oriented solution:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LottoSample
{
class Program
{
const int QTY = 10;
static void Main(string[] args)
{
// A lotto numbers object which is initialized with the total amount of random numbers.
var _lottoNumbers = new LottoNumbers(QTY);
Random _random = new Random();
int _count = QTY; // Iterator
int _randomNumber = -1; // Stores the random number
// As long as there is still slots available, add a new random number
while (_count > 0)
{
// Get a random number
_randomNumber = _random.Next(1, 100);
// Attempt to add the number to the lotto numbers
if (_lottoNumbers.Add(_randomNumber) == true)
{
// It was successful, reduce the count by 1
_count--;
}
// Otherwise, try again until a unique value is found.
}
// Display the result
Console.WriteLine(_lottoNumbers.ToString());
Console.ReadLine();
}
public class LottoNumbers
{
private HashSet<int> _numbers;
private int _total;
public LottoNumbers()
{
this._numbers = new HashSet<int>();
}
public LottoNumbers(int total)
: this()
{
_total = total;
}
public bool Add(int number)
{
// Check to see if the count of numbers is greater than the limit
// Throw an exception if so.
if (this.Count() >= _total)
throw new ArgumentOutOfRangeException();
// Check to see if the number already exists and return true if so.
if (this._numbers.Contains(number))
return false;
// Add the number otherwise and return true.
this._numbers.Add(number);
return true;
}
// Overriden ToString() that displays a formatted list of numbers
public override string ToString()
{
var sb = new StringBuilder();
foreach (var n in _numbers)
{
sb.AppendFormat("{0}", n);
// If the number isn't the last in the list, append a comma
if (n != _numbers.Last())
{
sb.Append(",");
}
}
return sb.ToString();
}
// Get the count of numbers
private int Count()
{
return this._numbers.Count();
}
}
}
}
I am given a string of characters, in which every consequent pair of characters comprises an edge. What I mean by that is this is the string: ABBCAD. Edges of the string are:
A->B
B->C
A->D
Shortest path distance is A->D
The task at hand is to build up a Directed Acyclic Graph in memory from the string using the above rule and find the shortest path staring at the root node(in the example given it's A label) ending at terminal node.
NJKUUGHBNNJHYAPOYJHNRMNIKAIILFGJSNAICZQRNM
I gather one of the approaches that suites the task is to use the Depth First Search algo.
This is not homework...
This is a job for Djikstra's Algorithm. Once you build a representation of your graph it should be easy enough to produce the lowest cost traversal ... since in your case it seems that all paths have an equal cost (1).
You can look here on CodeProject for a reasonably decent implementation of Djikstra in C#.
could you present me with a pseudo code of your version of the graph representation for this case?
There are multiple ways to represent a graph in such a problem. If the number of vertices in your graph are likely to be small - it would be sufficient to use an adjacency matrix for the representation of edges. In which case, you could just use a .NET multidimensional array. The trick here is that you need to convert vertices labelled with characters to ordinal values. One approach would be to write a wrapper class that maps character codes to indices into the adjacency matrix:
class AdjacencyMatrix
{
// representation of the adjacency matrix (AM)
private readonly int[,] m_Matrix;
// mapping of character codes to indices in the AM
private readonly Dictionary<char,int> m_Mapping;
public AdjacencyMatrix( string edgeVector )
{
// using LINQ we can identify and order the distinct characters
char[] distinctChars = edgeVector.Distinct().OrderBy(x => x);
m_Mapping = distinctChars.Select((c,i)=>new { Vertex = c, Index = i })
.ToDictionary(x=>x.Vertex, x=>x.Index);
// build the adjacency cost matrix here...
// TODO: I leave this up to the reader to complete ... :-D
}
// retrieves an entry from within the adjacency matrix given two characters
public int this[char v1, char v2]
{
get { return m_Matrix[m_Mapping[v1], m_Mapping[v2]];
private set { m_Matrix[m_Mapping[v1], m_Mapping[v2]] = value; }
}
}
For this particular case, Dijkstra's could be greatly simplified. I imagine something like this would do the trick.
class Node {
public object Value;
// Connected nodes (directed)
public Node[] Connections;
// Path back to the start
public Node Route;
}
Node BreadthFirstRoute(Node[] theStarts, Node[] theEnds) {
Set<Node> visited = new Set<Node>();
Set<Node> frontier = new Set<Node>();
frontier.AddRange(theStarts);
Set<Node> ends = new Set<Node>();
ends.AddRange(theEnds);
// Visit nodes one frontier at a time - Breadth first.
while (frontier.Count > 0) {
// Move frontier into visiting, reset frontier.
Set<Node> visiting = frontier;
frontier = new Set<Node>();
// Prevent adding nodes being visited to frontier
visited.AddRange(visiting);
// Add all connected nodes to frontier.
foreach (Node node in visiting) {
foreach (Node other in node.Connections) {
if (!visited.Contains(other)) {
other.Route = other;
if (ends.Contains(other)) {
return other;
}
frontier.Add(other);
}
}
}
}
return null;
}
Just for the record. This is your graph example:
Where the shortest path from A to M is marked in blue.
It is a very short program in Mathematica:
a = StringSplit["NJKUUGHBNNJHYAPOYJHNRMNIKAIILFGJSNAICZQRNM", ""]
b = Table[a[[i]] -> a[[i + 1]], {i, Length#a - 1}]
vertexNumber[g_, vName_] := Position[ Vertices[g, All], vName][[1, 1]];
Needs["Combinatorica`"]
c = ToCombinatoricaGraph[b]
sp = ShortestPath[c, vertexNumber[c, "A"], vertexNumber[c, "M"]]
vlsp = GetVertexLabels[c, sp]
vlspt = Table[{vlsp[[i]], vlsp[[i + 1]]}, {i, Length#vlsp - 1}]
GraphPlot[b, VertexLabeling -> True, ImageSize -> 250,
DirectedEdges -> True, Method -> {"SpringEmbedding"},
EdgeRenderingFunction ->
(If[Cases[vlspt, {First[#2], Last[#2]}] == {},
{Red, Arrowheads[Medium], Arrow[#1, .1]},
{Blue, Arrowheads[Medium], Arrow[#1, .1]}] &)]
LBushkin's answer is correct. Eric Lippert has a series about implementing the A* algorithm in C#. A* is a more general case of Dijkstra's algorithm : if your cost estimation function always returns 0, it is exactly equivalent to Dijkstra.
Another option would be to use a graph library that has various shortest path algorithms implemented. One I have used in the past and found to be good is QuickGraph. The documentation is quite good. For example, here is a page in the documentation that explains how to use Dijkstra's algorithm.
Because your graph is acyclic the Viterbi algorithm can be used, visit the states in topological order and update the costs in preceding vertices (states).
The below code implements the search algorithm and data structure. I wasn't 100% sure of the definition of the valid based on the original question. But it should be straight forward to modify the code to construct other topologies and solve various dynamic programming task using a DAG.
Changing the outer for loop when computing the state potentials to while loop with a queue will allow for different shortestpath algorithms to used easily changed by changing the queue discipline. For example a binary heap based queue will give Dijkstra algorithm or a FIFO queue will Bellman-Ford algorithm.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DAGShortestPath
{
class Arc
{
public Arc(int nextstate, float cost)
{
Nextstate = nextstate;
Cost = cost;
}
public int Nextstate { get; set; }
public float Cost { get; set; }
};
class State
{
public bool Final { get; set; }
public List<Arc> Arcs { get; set; }
public void AddArc(int nextstate, float cost)
{
if (Arcs == null)
Arcs = new List<Arc>();
Arcs.Add(new Arc(nextstate, cost));
}
}
class Graph
{
List< State > _states = new List< State >();
int _start = -1;
public void AddArc(int state, int nextstate, float cost)
{
while (_states.Count <= state)
AddState();
while (_states.Count <= nextstate)
AddState();
_states[state].AddArc(nextstate, cost);
}
public List<Arc> Arcs(int state)
{
return _states[state].Arcs;
}
public int AddState()
{
_states.Add(new State());
return _states.Count - 1;
}
public bool IsFinal(int state)
{
return _states[state].Final;
}
public void SetFinal(int state)
{
_states[state].Final = true;
}
public void SetStart(int start)
{
_start = -1;
}
public int NumStates { get { return _states.Count; } }
public void Print()
{
for (int i = 0; i < _states.Count; i++)
{
var arcs = _states[i].Arcs;
for (int j = 0; j < arcs.Count; j++)
{
var arc = arcs[j];
Console.WriteLine("{0}\t{1}\t{2}", i, j, arc.Cost);
}
}
}
}
class Program
{
static List<int> ShortertPath(Graph graph)
{
float[] d = new float[graph.NumStates];
int[] tb = new int[graph.NumStates];
for (int i = 0; i < d.Length; i++)
{
d[i] = float.PositiveInfinity;
tb[i] = -1;
}
d[0] = 0.0f;
float bestscore = float.PositiveInfinity;
int beststate = -1;
for (int i = 0; i < graph.NumStates; i++)
{
if (graph.Arcs(i) != null)
{
foreach (var arc in graph.Arcs(i))
{
if (arc.Nextstate < i)
throw new Exception("Graph is not topologically sorted");
float e = d[i] + arc.Cost;
if (e < d[arc.Nextstate])
{
d[arc.Nextstate] = e;
tb[arc.Nextstate] = i;
if (graph.IsFinal(arc.Nextstate) && e < bestscore)
{
bestscore = e;
beststate = arc.Nextstate;
}
}
}
}
}
//Traceback and recover the best final sequence
if (bestscore == float.NegativeInfinity)
throw new Exception("No valid terminal state found");
Console.WriteLine("Best state {0} and score {1}", beststate, bestscore);
List<int> best = new List<int>();
while (beststate != -1)
{
best.Add(beststate);
beststate = tb[beststate];
}
return best;
}
static void Main(string[] args)
{
Graph g = new Graph();
String input = "ABBCAD";
for (int i = 0; i < input.Length - 1; i++)
for (int j = i + 1; j < input.Length; j++)
{
//Modify this of different constraints on-the arcs
//or graph topologies
//if (input[i] < input[j])
g.AddArc(i, j, 1.0f);
}
g.SetStart(0);
//Modify this to make all states final for example
//To compute longest sub-sequences and so on...
g.SetFinal(g.NumStates - 1);
var bestpath = ShortertPath(g);
//Print the best state sequence in reverse
foreach (var v in bestpath)
{
Console.WriteLine(v);
}
}
}
}