The program runs slowly when there are more than 10 characters, I would like to do it quickly. I do not understand how I can improve and ask for your help.
And still need to count and number those combinations that do not occur in their original positions, I think if I add this logic, then the program will work even longer, and this also requires your advice
P.S. sorry for my English)
private void btnGo_Click(object sender, EventArgs e)
{
this.Size = new Size(632, 430);
button1.Visible = true;
// Get the items.
string[] items = txtItems.Text.Split(' ');
// string[] items = txtItems.Text.ToString;
// Generate the permutations.
List<List<string>> results =
GeneratePermutations<string>(items.ToList());
// results.Where(x => !HasCharacterAtSamePositionAsInOriginal(items, x));
List<string> list = new List<string>();
// Display the results.
lstPermutations.Items.Clear();
foreach (List<string> combination in results.Where(x => !HasCharacterAtSamePositionAsInOriginal(items, x)))
{
if (checkBox1.Checked == true)
{
lstPermutations.Items.Add(string.Join("", combination.ToArray()));
}
else
{
lstPermutations.Items.Add(string.Join(" ", combination.ToArray()));
}
}
// Calculate the number of permutations.
long num_permutations = Factorial(items.Length);
txtNumPermutations.Text = num_permutations.ToString();
// Check the result.
// Debug.Assert(lstPermutations.Items.Count == num_permutations);
}
private List<List<T>> GeneratePermutations<T>(List<T> items)
{
// Make an array to hold the
// permutation we are building.
T[] current_permutation = new T[items.Count];
// Make an array to tell whether
// an item is in the current selection.
bool[] in_selection = new bool[items.Count];
// Make a result list.
List<List<T>> results = new List<List<T>>();
// Build the combinations recursively.
PermuteItems<T>(items, in_selection,
current_permutation, results, 0);
// Return the results.
return results;
}
// Recursively permute the items that are
// not yet in the current selection.
private void PermuteItems<T>(List<T> items, bool[] in_selection,
T[] current_permutation, List<List<T>> results, int next_position)
{
// See if all of the positions are filled.
if (next_position == items.Count)
{
// All of the positioned are filled.
// Save this permutation.
results.Add(current_permutation.ToList());
}
else
{
// Try options for the next position.
for (int i = 0; i < items.Count; i++)
{
if (!in_selection[i])
{
// Add this item to the current permutation.
in_selection[i] = true;
current_permutation[next_position] = items[i];
// Recursively fill the remaining positions.
PermuteItems<T>(items, in_selection,
current_permutation, results, next_position + 1);
// Remove the item from the current permutation.
in_selection[i] = false;
}
}
}
}
// Return n!
private long Factorial(long n)
{
long result = 1;
for (int i = 2; i <= n; i++) result *= i;
return result;
}
bool HasCharacterAtSamePositionAsInOriginal(string [] originalWord, List<string> anagram) //check the symbol occurs in its original location or not
{
for (var i = 0; i < originalWord.Length; i++)
{
if (originalWord[i].Equals(anagram[i]))
{
return true;
}
}
return false;
}
Related
I'm new to C# and we need to create an array that can hold 100 scores. But it needs to also accept something smaller (txt file with numbers between 0-100). My issue is that even tho I add a txt file that has 50 scores, it will think there is another 50 'blank scores' and it will count those as 0. So when my code does the average, total, and lowest score, it will mess it up. My textbook isn't being very help with the answer.
private double Total(double[] iArray)
{
double total = 0;
total = iArray.Length;
return total;
}
//Average test score
private double Average(double[] iArray)
{
double total = 0;
double average;
for (int index = 0; index < iArray.Length; index++)
{
total += iArray[index];//look into later
}
average = (double)total / iArray.Length;
return average;
} //done
//for hightest test score
private double Highest(double[] iArray)
{
double highest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] > highest)
{
highest = iArray[index];
}
}
return highest;
} //done
private double Lowest(double[] iArray)
{
double lowest = iArray[0];
for (int index = 1; index < iArray.Length; index++)
{
if (iArray[index] < lowest)
{
lowest = iArray[index];
}
}
return lowest;
}//done
private void addFileButton_Click(object sender, EventArgs e)
{
try
{
const int SIZE = 100; //number of test
double[] scores = new Double[SIZE]; //array of the test scores
int index = 0; //loop counter
int count = 0;
double highestScore;
double lowestScore;
double averageScore;
double totalScore;
//Asking user to open a file
StreamReader inputFile;
if (openFile.ShowDialog() == DialogResult.OK)
{
inputFile = File.OpenText(openFile.FileName);
while (!inputFile.EndOfStream && count < scores.Length)//switching index to count
{
scores[count] = int.Parse(inputFile.ReadLine());
count++;
}
inputFile.Close();
}
else
{
MessageBox.Show("Operation Canceled.");
}
//Display test scores
for (index = 0; index < count; index++)
{
scoreListBox.Items.Add(scores[index].ToString());
}
//grabbing information
highestScore = Highest(scores);
lowestScore = Lowest(scores);
averageScore = Average(scores);
totalScore = Total(scores);
//display values
highesScoreLabel.Text = highestScore.ToString();
lowestScoreLabel.Text = lowestScore.ToString();
averageTestScoreLabel.Text = averageScore.ToString();
totalTestScoresLabel.Text = totalScore.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
You might just have to use Array.Resize then:
while (!inputFile.EndOfStream && scores.Count <= SIZE)
{
scores[count] = int.Parse(inputFile.ReadLine());
count++;
}
Array.Resize(scores, --count);
See if that works.
This was suggested before the OP's comment clarifying the should specifically use an array:
What if you used a List<double> when collecting the scores, then .ToArray() before you pass it into your other methods?
const int SIZE = 100;
List<double> scores = new List<double>();
[...]
if (openFile.ShowDialog() == DialogResult.OK)
{
inputFile = File.OpenText(openFile.FileName);
while (!inputFile.EndOfStream && scores.Count <= SIZE)
{
scores.Add(int.Parse(inputFile.ReadLine()));
count++;
}
[...]
var arrScores = scores.ToArray();
//grabbing information
highestScore = Highest(arrScores);
lowestScore = Lowest(arrScores);
averageScore = Average(arrScores);
totalScore = Total(arrScores);
Notice that the while loop has the condition changed for scores.Count <= SIZE to still only allow up to 100 scores.
Instead of constructing the Array yourself and implementing all these methods, I suggest, you read up in LINQ. LINQ stands for Language INtegrated Query and is essentially a bunch of extension methods on IEnumerable<T> that provide all of the functionality you need here.
I rewrote your event handler to use LINQ and it became much simpler.
private void addFileButton_Click(object sender, EventArgs e)
{
try
{
// Easier to follow if we just exit early
if (openFile.ShowDialog() != DialogResult.OK)
{
MessageBox.Show("Operation Canceled.");
return;
}
var scores = File.ReadAllLines(openFile.FileName)
// Always use TryParse to avoid exceptions
.Select(l => int.TryParse(l, out var score) ? score : -1)
// Filter out everything that is no valid score
.Where(s => s >= 0)
// You could also use ToList() here for a possible minimal performance gain and the possibility to add items later.
.ToArray();
// Display test scores
// We are using foreach instead of for here because we do not care about indexes. We simply want to add each item.
// We also do not need to add the ToString(), just adding an int is fine.
foreach (var score in scores)
scoreListBox.Items.Add(score);
// grabbing information
// We use LINQ methods here. Microsoft was nice enough di implement a bunch of often used methods on collections for us.
var highestScore = scores.Max();
var lowestScore = scores.Min();
var averageScore = scores.Average();
var totalScore = scores.Sum();
//display values
highesScoreLabel.Text = highestScore.ToString();
lowestScoreLabel.Text = lowestScore.ToString();
averageTestScoreLabel.Text = averageScore.ToString();
totalTestScoresLabel.Text = totalScore.ToString();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I want skip my in foreach. For example:
foreach(Times t in timeList)
{
if(t.Time == 20)
{
timeList.Skip(3);
}
}
I want "jump" 3 positions in my list.. If, in my if block t.Id = 10 after skip I want get t.Id = 13
How about this? If you use a for loop then you can just step the index forward as needed:
for (var x = 0; x < timeList.Length; x++)
{
if (timeList[x].Time == 20)
{
// option 1
x += 2; // 'x++' in the for loop will +1,
// we are adding +2 more to make it 3?
// option 2
// x += 3; // just add 3!
}
}
You can't modify an enumerable in-flight, as it were, like you could the index of a for loop; you must account for it up front. Fortunately there are several way to do this.
Here's one:
foreach(Times t in timeList.Where(t => t.Time < 20 || t.Time > 22))
{
}
There's also the .Skip() option, but to use it you must break the list into two separate enumerables and then rejoin them:
var times1 = timeList.TakeWhile(t => t.Time != 20);
var times2 = timeList.SkipeWhile(t => t.Time != 20).Skip(3);
foreach(var t in times1.Concat(times2))
{
}
But that's not exactly efficient, as it requires iterating over the first part of the sequence twice (and won't work at all for Read Once -style sequences). To fix this, you can make a custom enumerator:
public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> items, Predicate<T> SkipTrigger, int SkipCount)
{
bool triggered = false;
int SkipsRemaining = 0;
var e = items.GetEnumerator();
while (e.MoveNext())
{
if (!triggered && SkipTrigger(e.Current))
{
triggered = true;
SkipsRemaining = SkipCount;
}
if (triggered)
{
SkipsRemaining--;
if (SkipsRemaining == 0) triggered = false;
}
else
{
yield return e.Current;
}
}
}
Then you could use it like this:
foreach(Times t in timeList.SkipAt(t => t.Times == 20, 3))
{
}
But again: you still need to decide about this up front, rather than inside the loop body.
For fun, I felt like adding an overload that uses another predicate to tell the enumerator when to resume:
public static IEnumerable<T> SkipAt<T>(this IEnumerable<T> items, Predicate<T> SkipTrigger, Predicate<T> ResumeTrigger)
{
bool triggered = false;
var e = items.GetEnumerator();
while (e.MoveNext())
{
if (!triggered && SkipTrigger(e.Current))
{
triggered = true;
}
if (triggered)
{
if (ResumeTrigger(e.Current)) triggered = false;
}
else
{
yield return e.Current;
}
}
}
You can use continue with some simple variables.
int skipCount = 0;
bool skip = false;
foreach (var x in myList)
{
if (skipCount == 3)
{
skip = false;
skipCount = 0;
}
if (x.time == 20)
{
skip = true;
skipCount = 0;
}
if (skip)
{
skipCount++;
continue;
}
// here you do whatever you don't want to skip
}
Or if you can use a for-loop, increase the index like this:
for (int i = 0; i < times.Count)
{
if (times[i].time == 20)
{
i += 2; // 2 + 1 loop increment
continue;
}
// here you do whatever you don't want to skip
}
I have a System.Collection.Generic.Queue<int> with following sample code
Queue<int> iq = new Queue<int>();
iq.Enqueue(1); // 1
iq.Enqueue(2); // 1 2
iq.Enqueue(3); // 1 2 3
//move 1 to the end of the line here
int i = iq.Dequeue(); // 2 3
I want to move the value (access by value) 1 back to the end of the line so that the result is 2 and 1 would be the last dequeueable value.
Any idea? Is there something like iq.MoveToLast(1) ?
If you want to Remove / Add item by its value, you can use List<T> instead of Queue<T>:
List<int> id = ...
int itemToMove = 2;
int index = id.IndexOf(itemToMove);
// If we have item found we should put it at the end
if (index >= 0) {
id.Add(id[index]);
id.RemoveAt(index);
}
If you have to use Queue<T> you can create a temporal List<T>:
Queue<int> iq = ...
int itemToMove = 2;
// Create temporal list
var list = iq.ToList();
// process items in it
int index = list.IndexOf(itemToMove);
if (index >= 0) {
list.Add(list[index]);
list.RemoveAt(index);
}
// enqueue items back into queue in the desired order
iq.Clear();
foreach (var item in list)
iq.Enqueue(item);
Finally, you can implement an extension method:
public static partial class QueueExtensions {
public static void MoveToLast<T>(this Queue<int> queue, T itemToMove) {
if (null == queue)
throw new ArgumentNullException(nameof(queue));
var list = queue.ToList();
int index = list.IndexOf(itemToMove);
if (index < 0)
return; // Nothing to do
list.Add(list[index]);
list.RemoveAt(index);
queue.Clear();
foreach (var item in list)
queue.Enqueue(item);
}
}
Then you can put
iq.MoveToLast(1);
Just try:
queue.Enqueue(queue.Dequeue());
You can't Remove elements from Queue by using methods other than Dequeue.
Here's an approach which just manipulates the queue:
public static void MoveElementToBack<T>(Queue<T> queue, T elementToMove)
{
T item = default;
bool found = false;
for (int i = 0, n = queue.Count; i < n; ++i)
{
var current = queue.Dequeue();
if (!found && current.Equals(elementToMove))
{
item = current;
found = true;
}
else
{
queue.Enqueue(current);
}
}
if (found)
queue.Enqueue(item);
}
This is always an O(N) operation, but it only makes one pass through the queue.
I am keeping a rolling accumulator for a graphing application, that one of the features is providing a running average of the sample.
The size of the accumulator is variable, but essentially the end goal is achieved like this.
The accumulator class (ugly but functional)
public class accumulator<t>
{
private int trim;
List<t> _points = new List<t>();
List<string> _labels = new List<string>();
List<t> _runAvg = new List<t>();
List<t> _high = new List<t>();
List<t> _low = new List<t>();
public List<t> points { get { return _points; } }
public List<string> labels { get { return _labels; } }
public List<t> runAvg { get { return _runAvg; } }
public List<t> high { get { return _high; } }
public List<t> low { get { return _low; } }
public delegate void onChangeHandler(accumulator<t> sender, EventArgs e);
public event onChangeHandler onChange;
public accumulator(int trim)
{
this.trim = trim;
}
public void add(t point, string label)
{
if (_points.Count == trim)
{
_points.RemoveAt(0);
_labels.RemoveAt(0);
_runAvg.RemoveAt(0);
_high.RemoveAt(0);
_low.RemoveAt(0);
}
_points.Add(point);
_labels.Add(label);
if (typeof(t) == typeof(System.Int32))
{
int avg = 0;
if (_high.Count == 0)
{
_high.Add(point);
}
else
{
t v = (Convert.ToInt32(point) > Convert.ToInt32(_high[0])) ? point : _high[0];
_high.Clear();
for (int i = 0; i < _points.Count; i++) _high.Add(v);
}
if (_low.Count == 0)
{
_low.Add(point);
}
else
{
t v = (Convert.ToInt32(point) < Convert.ToInt32(_low[0])) ? point : _low[0];
_low.Clear();
for (int i = 0; i < _points.Count; i++) _low.Add(v);
}
foreach (t item in _points) avg += Convert.ToInt32(item);
avg = (avg / _points.Count);
_runAvg.Add((t)(object)avg);
//_runAvg.Add((t)(object)_points.Average(a => Convert.ToInt32(a)));
}
if (typeof(t) == typeof(System.Double))
{
double avg = 0;
if (_high.Count == 0)
{
_high.Add(point);
}
else
{
t v = (Convert.ToDouble(point) > Convert.ToDouble(_high[0])) ? point : _high[0];
_high.Clear();
for (int i = 0; i < _points.Count; i++) _high.Add(v);
}
if (_low.Count == 0)
{
_low.Add(point);
}
else
{
t v = (Convert.ToDouble(point) < Convert.ToDouble(_low[0])) ? point : _low[0];
_low.Clear();
for (int i = 0; i < _points.Count; i++) _low.Add(v);
}
foreach (t item in _points) avg += Convert.ToDouble(item);
avg = (avg / _points.Count);
_runAvg.Add((t)(object)avg);
//_runAvg.Add((t)(object)_points.Average(a => Convert.ToDouble(a)));
}
onChangeHappen();
}
private void onChangeHappen()
{
if (onChange != null) onChange(this, EventArgs.Empty);
}
}
As you can see essentially I want to keep a running average, a High/Low mark (with the same count of data points so it binds directly to the chart control in an extended series class)
The average is I think what is killing me, to have to add every element of the list to a sum / divide by the count is of course how an average is achieved, but is the loop the most efficient way to do this?
I took a stab at a lambda expression (commented out) but figured on some level it had to be doing the same. (Sort of like using the generic list VS array, I figure it has to be re declaring an array somewhere and moving elements around, but it is likely being done as or more efficient than I would have, so let it do it for convenience's sake)
The ultimate goal and final question being really, given a list of generic values...
The most efficient way to average the whole list.
*Note: I DO realize the pedantic nature of typing the int/floats, this is because of type checking in the consumer of this class over which I have no control, it actually looks to see if it is double/int otherwise I would have treated it ALL as floats ;)
Thanks in advance...
Keeping a running average is actually really easy. You don't need to sum the whole list every time, because you already have it!
Once you have a current average (from your loop), you just do the following:
((oldAverage * oldCount) + newValue) / newCount
This will give you the average of the old set with the new value(s) included.
To get the initial average value, consider using the Average function from LINQ:
double average = listOfInts.Average();
I have a problem that I really cannot get my head around. I know how to sort data in general but this one is taxing me!
I have a list of values in an array. The values look like this:
[03;02HTransactions
[03;16HPost Transactions
[04:02HDividends
[04;16HPostDividends
[01:01H-----------------------------------------------------
[05:01H-----------------------------------------------------
[02:16HDate: Today
[02:02HTrades
So its essentially ANSI formatting from a terminal screen which i'm trying to re-construct into a list so that I can print it on our test logs so it at least looks vaguely readable.
So this is how it works within the first 6 characters: [XX:YY where XX is the row number and YY is the column number. The H doesn't matter its just formatting.
Here is what I've got so far:
List<string> rows = new List<string>();
for (int i = 0; i <= filteredLines.Count - 1; i++)
{
int rowIndex = Convert.ToInt32(filteredLines[i].Substring(1, 2));
Dictionary<int, string> columns = new Dictionary<int, string>();
foreach (string row in filteredLines)
{
int innerRowIndex = Convert.ToInt32(row.Substring(1, 2));
if (innerRowIndex == rowIndex)
{
int columnIndex = Convert.ToInt32(filteredLines[i].Substring(4, 2));
string value = filteredLines[i].Remove(0, 7);
columns.Add(columnIndex, value);
}
}
string columnConcatenated = "";
for (int j = 0; j <= columns.Count; j ++ )
{
columnConcatenated = columnConcatenated + columns[j];
}
rows.Add(columnConcatenated);
}
What I essentially want to do is to to build up the lines and sort them into a list based on the row number so it looks like:
--------------------------------------------
Trades Date: Today
Transactions Post Transactions
Dividends Post Dividends
--------------------------------------------
my example isn't 100% accurate as its hard to count the exact columns, but you get the idea. They just need to be on the same line in the correct order.
I cant help but feel i'm probably not going about this the best way. So is there an ideal way for me to achieve this?
Okay, I would implement it like so:
Create a simple POCO/class to represent a log entry with the properties row, column and text
Implement the IComparable interface, so these items can be sorted, on row # first and on column # second
Parse every log line and create a simple POCO Entry object for each
Use a simple List<Entry> and sort it afterwards
Use a StringBuilder to build up the final output
Loop over every Entry in the list, checking it's row # and perhaps entering some newlines for our StringBuilder if there are gaps
If we get an Entry with a row number which is the same as a previous one (which you can use a temp variable for), don't output a newline, but append the Entry.text to this line instead, at the column you want
You already have code to parse each line, extracting its row, column, and displayed text. If the lines of text are not sparse, you could represent this as basically a 2D dynamic array of characters that automatically pads itself out with spaces or empty lines, like so:
public class StringBuilderList : IList<string>
{
readonly List<StringBuilder> list = new List<StringBuilder>();
readonly char pad = ' ';
const char DefaultPad = ' ';
public StringBuilderList(char pad)
{
this.pad = pad;
}
public StringBuilderList() : this(DefaultPad) {}
public void SetString(int iLine, int iChar, string text)
{
list.EnsureCount(iLine + 1);
if (list[iLine] == null)
list[iLine] = new StringBuilder(iChar + text.Length);
var sb = list[iLine];
sb.OverwriteAt(iChar, text, pad);
}
#region IList<string> Members
public int IndexOf(string item)
{
for (int i = 0; i < Count; i++)
if (this[i] == item) // this is not memory-efficient.
return i;
return -1;
}
public void Insert(int index, string item)
{
var sb = new StringBuilder(item);
list.Insert(index, sb);
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public string this[int index]
{
get
{
// Hide the nulls from the caller!
var sb = list[index];
if (sb == null)
return string.Empty;
return sb.ToString();
}
set
{
if (string.IsNullOrEmpty(value))
{
if (list[index] != null)
list[index].Length = 0;
}
else if (list[index] == null)
{
list[index] = new StringBuilder(value);
}
else
{
list[index].Length = 0;
list[index].Append(value);
}
}
}
#endregion
#region ICollection<string> Members
public void Add(string item)
{
list.Add(new StringBuilder(item));
}
public void Clear()
{
list.Clear();
}
public bool Contains(string item)
{
return IndexOf(item) >= 0;
}
public void CopyTo(string[] array, int arrayIndex)
{
foreach (var str in this)
array[arrayIndex++] = str;
}
public int Count
{
get { return list.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(string item)
{
int index = IndexOf(item);
if (index < 0)
return false;
RemoveAt(index);
return true;
}
#endregion
#region IEnumerable<string> Members
public IEnumerator<string> GetEnumerator()
{
foreach (var sb in list)
yield return (sb == null ? string.Empty : sb.ToString());
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
SetString is the method you would use, it copies the string into the 2d array of characters, expanding it as required with empty lines and/or space characters.
And helper methods:
public static class ListHelper
{
public static void Resize<T>(this List<T> list, int count)
{
if (list == null || count < 0)
throw new ArgumentException();
int oldCount = list.Count;
if (count > oldCount)
{
list.Capacity = count;
for (int i = oldCount; i < count; i++)
list.Add(default(T));
}
else if (count < oldCount)
{
for (int i = oldCount-1; i >= count; i--)
list.RemoveAt(i);
}
}
public static void EnsureCount<T>(this List<T> list, int count)
{
if (list == null || count < 0)
throw new ArgumentException();
if (count > list.Count)
list.Resize(count);
}
}
public static class StringBuilderHelper
{
public static void OverwriteAt(this StringBuilder sb, int index, string text, char pad)
{
var textLen = text.Length;
if (textLen + index > sb.Length)
{
for (int i = sb.Length, newLen = textLen + index; i < newLen; i++)
{
sb.Append(pad);
}
}
for (int i = 0; i < textLen; i++)
{
sb[index + i] = text[i];
}
}
}
So a few people have some solutions though they seemed a bit over complicated for what I needed. I managed to resolve the issue myself using a pen and paper then trying it in visual studio. Here is what I did:
First I created a list by looking through the original array and I sorted and removed duplicates:
List<int> rowList = new List<int>();
for (int i = 0; i <= filteredLines.Count - 1; i++)
{
int rowIndex = Convert.ToInt32(filteredLines[i].Substring(1, 2));
rowList.Add(rowIndex);
}
rowList = rowList.Distinct().ToList<int>();
rowList.Sort();
Next I created a container for my final list that would hold the values, then I ran my sorting routine which makes use of a SortedList in order to ensure the columns are sorted before I concatenate them:
foreach (int listRow in rowList)
{
SortedList<int, string> columnList = new SortedList<int, string>();
foreach(string row in filteredLines)
{
int rowIndex = Convert.ToInt32(row.Substring(1, 2));
if(rowIndex==listRow)
{
int columnIndex = Convert.ToInt32(row.Substring(4, 2));
string value = row.Remove(0, 7);
if(columnList.ContainsKey(columnIndex))
{
columnList[columnIndex] = columnList[columnIndex] + value;
}
else
{
columnList.Add(columnIndex, value);
}
}
}
string concatenatedColumns = "";
foreach(string col in columnList.Values)
{
concatenatedColumns = concatenatedColumns + col;
}
parsedAndSortedRows.Add(concatenatedColumns);
}
It does the job fine and puts all the columns in order on the correct row as I wanted. Thanks for the help to everyone though it was through the different answers I helped come up with this solution.