Permutation Algorithm OutOfMemory Exception - c#

Please help me to how to fix OutOfMemory exception using below code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Permutation
{
class Program
{
static void Main(string[] args)
{
CreatePartsByFreezingEachElementOnce("abcedfghijk");
PrintPossiblePermutations(false);
Console.WriteLine("---------------");
PrintPossiblePermutations(true);
Console.ReadLine();
}
static void PrintPossiblePermutations(bool unique)
{
var allPermutations = new List<string>();
foreach (var item in _listWithFreezedKey)
if (item.Item2.Count() == 2)
{
allPermutations.Add(Swap(String.Join(",", item.Item1), item.Item2[0], item.Item2[1]));
allPermutations.Add(Swap(String.Join(",", item.Item1), item.Item2[1], item.Item2[0]));
}
if (unique)
{
var uniuePermutations = allPermutations.Distinct();
// PrintPermutations(uniuePermutations.ToList());
Console.WriteLine(uniuePermutations.Count());
}
else
{
// PrintPermutations(allPermutations);
Console.WriteLine(allPermutations.Count());
}
}
static void PrintPermutations(List<string> permutations)
{
int i = 1;
foreach (var item in permutations)
{
Console.WriteLine(string.Format("{0} :{1}", i, item));
i++;
}
}
static List<Tuple<List<char>, List<char>>> _listWithFreezedKey = new List<Tuple<List<char>, List<char>>>();
static void CreatePartsByFreezingEachElementOnce(string str, List<char> indexToFreeze = null)
{
List<Tuple<List<char>, List<char>>> _innerlistWithFreezedKey = new List<Tuple<List<char>, List<char>>>();
var arr = str.ToCharArray().ToList();
var copy = arr;
if (indexToFreeze == null)
{
indexToFreeze = new List<char>();
}
for (int i = 0; i < arr.Count(); i++)
{
copy = str.ToCharArray().ToList();
var nth = arr[i];
copy.RemoveAt(i);
indexToFreeze.Add(nth);
_listWithFreezedKey.Add(new Tuple<List<char>, List<char>>(indexToFreeze.ToList(), copy));
_innerlistWithFreezedKey.Add(new Tuple<List<char>, List<char>>(indexToFreeze.ToList(), copy));
indexToFreeze.RemoveAt(indexToFreeze.Count() - 1);
}
foreach (var item in _innerlistWithFreezedKey)
{
List<char> l = item.Item2;
CreatePartsByFreezingEachElementOnce(String.Join("", l), item.Item1);
}
}
static string Swap(string frezedPart, char swapChar1, char swapChar2)
{
return frezedPart + "," + swapChar1 + "," + swapChar2;
}
}
}
If you run this code using 10 chrs, it throws out of memory exception. But for 9 chars , it returns result.
It was my interview question to write a code such that it should not go out of memory if big data passed.
Thanks,

Your problem is that you want to generate all permutations at once, instead of generating them one-by-one. You are looking for an algorithm which produces the next permutation.
See the first page of Knuth's book on generating permutations.

I would like to answer my question by myself because it may help someone as well.
I will use Iterator Design Pattern to remove unused elements from memory and will convert collection into sequence.
Thanks,

Related

How do i acces individual Values in a hash table

I have an assignment where i have to take an string input and apply zipfs law to it. Im having issues accessing the value in a hashtable. Whenever i find a word that already exist i'm supposed to update the wordcounter +1. What ends up happening is the counter applies to all the values in the hash table, and i get ridiculously high numbers. I cant wrap my head around how i'm supposed to give an individual counter for each keyvalue
Here is the code:
using System;
using Spire.Doc;
using System.Collections;
using System.Collections.Generic;
namespace Zipf
{
class Program
{
public static Hashtable wordRegister;
static void Main(string[] args)
{
String[] ArrLoop = TextSplitter();
int[] wordCount = new int[ArrLoop.Length];
int count = 1;
wordRegister = new Hashtable();
for(int i = 0; i < ArrLoop.Length; i++)
{
if (wordRegister.ContainsKey(ArrLoop[i]))
{
// here is where im having trouble
wordRegister[ArrLoop[i]] = count += 1;
}
else
{
wordRegister.Add(ArrLoop[i], count);
}
}
foreach (DictionaryEntry pair in wordRegister)
{
Console.WriteLine($"Key : {pair.Key} ; Forekomster : {pair.Value}");
}
Console.ReadLine();
}
public static String WordString()
{
Document doc = new Document();
doc.LoadFromFile(#"C:\Users\xxx\Desktop\2.g\IDO.docx");
String textString = doc.GetText();
return textString;
}
public static string[] TextSplitter()
{
string s = WordString();
String[] wordArr = s.Split();
return wordArr;
}
}
}
You don't need the count variable. You are incrementing a counter common to all entries.
Try this instead to keep the counts distinct from each other:
wordRegister[ArrLoop[i]] += 1;

No errors in Visual Studio, Code Executes but something not right C# (updated! problem in String.Insert)

I'm writing a program for schoolwork, that's supposed to generate a 10 000 "movie" list. A single "movie" consist of a string in a form "moviename year director". I say "movie" because movie name and director are supposed to be randomly generated with letters from a-z.
I wrote the following logic to generate one such "movie". Movie name and director are random letter combination in length between 4-10 charachters. Code gives no errors in visual studio, executes, but shows blank. If I wrote correctly, then this code should generate one such string and print it, yet console is blank.
Do while loop is there to check if, however unlikely, there is a double item in the List (this is for when I do the 10 000 version).
In short, I dont understand what am I doing wrong?
using System;
using System.Collections.Generic;
using System.Linq;
namespace Experiment
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
string movie = "";
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie))
{
check = false;
}
else
{
movieList.Add(movie);
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
Strings are immutable, so calling the Insert() method on the movie string doesn't do anything to the current movie variable. Instead it returns the new string.
You are however better off changing the movie type from string to StringBuilder, which is a dynamically allocated buffer of characters, so your example becomes:
using System;
using System.Text;
using System.Collections.Generic;
namespace sotest
{
class Program
{
static void Main(string[] args)
{
Movies();
Console.ReadKey();
}
public static void Movies()
{
List<string> movieList = new List<string>();
bool check = false;
do
{
StringBuilder movie = new StringBuilder();
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
movie.Insert(0, " ");
movie.Insert(0, Convert.ToString(GetYear()));
movie.Insert(0, " ");
for (int i = 0; i < GetNum(); i++)
{
movie.Insert(0, Convert.ToString(GetLetter()));
}
if (movieList.Contains(movie.ToString()))
{
check = false;
}
else
{
movieList.Add(movie.ToString());
check = true;
}
} while (check == false);
Console.WriteLine(movieList[0]);
}
public static Random _random = new Random();
public static char GetLetter()
{
int num = _random.Next(0, 26);
char let = (char)('a' + num);
return let;
}
public static int GetNum()
{
int num = _random.Next(4, 11);
return num;
}
public static int GetYear()
{
int num = _random.Next(1920, 2020);
return num;
}
}
}
The problem is that you are using movie.Insert incorrectly.
If you read the documentation for String.Insert it says
https://learn.microsoft.com/en-us/dotnet/api/system.string.insert?view=netframework-4.8
Returns a new string in which a specified string is inserted at a
specified index position in this instance
public string Insert (int startIndex, string value);
So it returns a new String, it does not amend the existing one. So you would need to do.
movie = movie.Insert(0, Convert.ToString(GetYear()));
However, I must also say that using String.Insert in this way is not the best approach.
You should instead look at using the StringBuilder class. It is very efficient when you want to amend strings (which are immutable objects).
You might want to read parts of this to help you understand. If you scroll down then it also suggests StringBuilder.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/strings/
Insert() method is used to return a new string from the specified string at a specified index position. In your case, you are not capturing the updated string.
The best approach to solve this is through using StringBuilder object. Please note that StringBuilder object is much efficient rather than playing with immutable string.

c# parallel arrays data from text file

Here's the problem: Index was outside the bounds of the array. Assignment: Write a program that determines the number of students who can still enroll in a given class. Design your solution using parallel arrays. Test your solution by retrieving the following data from a text file. Define a exception class for this problem if the current enrollment exceeds the maximum enrollment by more than three. Halt the program and display a message indicating which course is over-enrolled.
Here's the original code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
private static string[] classes = { "CS150", "CS250", "CS270", "CS300", "CS350" };
private static int[] currentEnrolled = { 18, 11, 9, 4, 20 };
private static int[] maxEnrollment = { 20, 20, 20, 20, 20 };
private static int currentEnrollment()
{
int enrolled = 0;
foreach (int i in currentEnrolled)
{
enrolled += i;
}
return enrolled;
}
private static void listClasses()
{
foreach (string i in classes)
{
Console.WriteLine("Class: {0}", i);
}
}
private static void ClassStatus()
{
for (int i = 0; i < currentEnrolled.Length; i++)
{
Console.WriteLine("Class: {0}, Max: {1}, Current: {2}, remaining: {3}", classes[i], maxEnrollment[i], currentEnrolled[i], maxEnrollment[i] - currentEnrolled[i]);
}
}
static void Main(string[] args)
{
Console.WriteLine("Currently Enrolled: {0}", currentEnrollment());
ClassStatus();
Console.ReadKey(false);
}
}
}
Now, I've been editing the above code to take a text file instead, however I get an error. Here's what I'm working with:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
private static string[] classes = new string[900];
private static int[] currentEnrolled = new int[900];
private static int[] maxEnrollment = new int[900];
private static int currentEnrollment()
{
int enrolled = 0;
foreach (int i in currentEnrolled)
{
enrolled += i;
}
return enrolled;
}
private static void listClasses()
{
foreach (string i in classes)
{
Console.WriteLine("Class: {0}", i);
}
}
private static void ClassStatus()
{
for (int i = 0; i < currentEnrolled.Length; i++)
{
Console.WriteLine("Class: {0}, Max: {1}, Current: {2}, remaining: {3}", classes[i], maxEnrollment[i], currentEnrolled[i], maxEnrollment[i] - currentEnrolled[i]);
}
}
static void Main(string[] args)
{
string[] lines = File.ReadAllLines("classes.txt");
int i = 0;
foreach (string line in File.ReadAllLines("classes.txt"))
{
string[] parts = line.Split(',');
while (i < 900 && i < parts.Length)
{
classes[i] = parts[1];
currentEnrolled[i] = int.Parse(parts[2]);
maxEnrollment[i] = int.Parse(parts[3]);
}
i++;
}
Console.WriteLine("Currently Enrolled: {0}", currentEnrollment());
ClassStatus();
Console.ReadKey(false);
}
}
}
Some of the components used in the above code were taken from this article: Splitting data from a text file into parallel arrays
Text file looks like this:
CS150,18,20
CS250,11,20
CS270,32,25
CS300,4,20
CS350,20,20
Any assistance will be appreciated. And yes, this is an assignment. Programming is most definitely not my strong suit.
There seem to be multiple problems with your while loop.
First, parts.Length will always be 3, since you have 2 commas and split on that. So the condition i < 900 && i < parts.Length does not really make sense, it's like i < 900 and i < 3, so it will always stop at 3. The intent is not really clear here, I think you meant to loop on each 900 values, but fi soforeach already does that for you.
Next, since there's 3 parts and C# arrays are 0-based, it should be parts[0], parts[1] and parts[2]. That's what causing your 'out of range' exception.
Finally, i++; should be in your while loop. If you leave it outside, you will loop forever as the index will never increase.
Basically, it should be something like this :
while (i < 900)
{
classes[i] = parts[0];
currentEnrolled[i] = int.Parse(parts[1]);
maxEnrollment[i] = int.Parse(parts[2]);
i++;
}
Again, the 900 is not really clear since you don't have 900 values per line (remember you're in a foreach). In my opinion you might as well scratch all that and redo it carefully.
What you need to do, is the following :
Read the file and store all the lines
Foreach line do:
Split the line in 3 parts
Store each separate part
Write results
For the "custom exception" part, you can add:
For the length of currentEnrollment do:
If currentEnrollment at current index is superior than maxEnrollment at current index do:
Throw a new exception with the className at current index

Changing one element of string in hashtable in C#

I have to write a program which use hashtable and the keys/values are input by the user. In the program, I have to output all the keys, however if any key starts with small 'a', I have to make it start with a big 'A'. I have a problem in the last step.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Hashtable hashtable = new Hashtable();
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Vnesete kluc");
string kluc = Console.ReadLine();
Console.WriteLine("Vnesete podatok");
string podatok = Console.ReadLine();
hashtable.Add(kluc, podatok);
}
foreach (string klucevi in hashtable.Keys)
{
if (klucevi[0] == 'a')
{
klucevi[0] = 'A';
}
Console.WriteLine(klucevi);
}
}
}
}
I'm getting an error on the line where I'm converting the first element of the string if it's 'a' to 'A'.
You can't dynamically change keys. Most simple approach is check the key before you add to the collection:
for (int i = 0; i < 10; i++)
{
Console.WriteLine("Vnesete kluc");
string kluc = Console.ReadLine();
if (kluc.StartsWith("a"))
kluc = "A" + kluc.Substring(1);
Console.WriteLine("Vnesete podatok");
string podatok = Console.ReadLine();
hashtable.Add(kluc, podatok);
}
Your problem has nothing to do with hash tables. You have a compilation error, because in .NET strings are immutable.
Secondly, and this is unrelated, a foreach loop variable cannot be assigned to.
So, instead of
foreach (string klucevi in *whatever*)
{
if (klucevi[0] == 'a')
{
klucevi[0] = 'A';
}
Console.WriteLine(klucevi);
}
use
foreach (string klucevi in *whatever*)
{
var temp = klucevi;
if (temp[0] == 'a')
{
StringBuilder sb = new StringBuilder(temp);
sb[0] = 'A';
temp = sb.ToString();
}
Console.WriteLine(temp);
}
don't forget to include a using System.Text; declaration.
UPDATE:
The aswer above shows you a generic way to modify a string in .NET, not just to replace one character.
Furthermore, some people have raised concerns about the efficiency of the approach. They are mistaken. More information in this execelent article on Strings Undocumented.
UPDATE 2:
I like being challenged. While it is totally irrelevant for the question at hand, a discussion has emmerged about the efiiciency of using a StringBuilder, compared to using "A" + temp.Substring(1). Because I like facts, and I assume some readers would agree, I ran a little benchmark.
I ran the tests on a 64 bit Windows 7 box with .NET 4.5 as both a 32 and a 64 bit process. It turns out that the StringBuilder approach is always faster than the alternative, by about 20%. Memory usage is approximately the same. YMMV.
For those who care to repeat the test, here's the source code:
using System;
using System.Diagnostics;
using System.Text;
static class Program
{
static void Main(string[] args)
{
for (int length = 50; length <= 51200; length = length * 2)
{
string input = new string(' ', length);
// warm up
PerformTest(input, 1);
// actual test
PerformTest(input, 100000);
}
}
static void PerformTest(string input, int iterations)
{
GC.Collect();
GC.WaitForFullGCComplete();
int gcRuns = GC.CollectionCount(0);
Stopwatch sw = Stopwatch.StartNew();
for (int i = iterations; i > 0; i--)
{
StringBuilder sb = new StringBuilder(input);
sb[0] = 'A';
input = sb.ToString();
}
long ticksWithStringBuilder = sw.ElapsedTicks;
int gcRunsWithStringBuilder = GC.CollectionCount(0) - gcRuns;
GC.Collect();
GC.WaitForFullGCComplete();
gcRuns = GC.CollectionCount(0);
sw = Stopwatch.StartNew();
for (int i = iterations; i > 0; i--)
{
input = "A" + input.Substring(1);
}
long ticksWithConcatSubstring = sw.ElapsedTicks;
int gcRunsWithConcatSubstring = GC.CollectionCount(0) - gcRuns;
if (iterations > 1)
{
Console.WriteLine(
"String length: {0, 5} With StringBuilder {1, 9} ticks {2, 5} GC runs, alternative {3, 9} ticks {4, 5} GC Runs, speed ratio {5:0.00}",
input.Length,
ticksWithStringBuilder, gcRunsWithStringBuilder,
ticksWithConcatSubstring, gcRunsWithConcatSubstring,
((double)ticksWithStringBuilder) / ((double)ticksWithConcatSubstring));
}
}
}

Read double value from a file C#

I have a txt file that the format is:
0.32423 1.3453 3.23423
0.12332 3.1231 9.23432432
9.234324234 -1.23432 12.23432
...
Each line has three double value. There are more than 10000 lines in this file. I can use the ReadStream.ReadLine and use the String.Split, then convert it.
I want to know is there any faster method to do it.
Best Regards,
StreamReader.ReadLine, String.Split and Double.TryParse sounds like a good solution here.
No need for improvement.
There may be some little micro-optimisations you can perform, but the way you've suggested sounds about as simple as you'll get.
10000 lines shouldn't take very long - have you tried it and found you've actually got a performance problem? For example, here are two short programs - one creates a 10,000 line file and the other reads it:
CreateFile.cs:
using System;
using System.IO;
public class Test
{
static void Main()
{
Random rng = new Random();
using (TextWriter writer = File.CreateText("test.txt"))
{
for (int i = 0; i < 10000; i++)
{
writer.WriteLine("{0} {1} {2}", rng.NextDouble(),
rng.NextDouble(), rng.NextDouble());
}
}
}
}
ReadFile.cs:
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
public class Test
{
static void Main()
{
Stopwatch sw = Stopwatch.StartNew();
using (TextReader reader = File.OpenText("test.txt"))
{
string line;
while ((line = reader.ReadLine()) != null)
{
string[] bits = line.Split(' ');
foreach (string bit in bits)
{
double value;
if (!double.TryParse(bit, out value))
{
Console.WriteLine("Bad value");
}
}
}
}
sw.Stop();
Console.WriteLine("Total time: {0}ms",
sw.ElapsedMilliseconds);
}
}
On my netbook (which admittedly has an SSD in) it only takes 82ms to read the file. I would suggest that's probably not a problem :)
I would suggest reading all your lines at once with
string[] lines = System.IO.File.ReadAllLines(fileName);
This wold ensure that the I/O is done with the maximum efficiency. You woul have to measure (profile) but I would expect the conversions to take far less time.
your method is already good!
you can improve it by writing a readline function that returns an array of double and you reuse this function in other programs.
This solution is a little bit slower (see benchmarks at the end), but its nicer to read. It should also be more memory efficient because only the current character is buffered at the time (instead of the whole file or line).
Reading arrays is an additional feature in this reader which assumes that the size of the array always comes first as an int-value.
IParsable is another feature, that makes it easy to implement Parse methods for various types.
class StringSteamReader {
private StreamReader sr;
public StringSteamReader(StreamReader sr) {
this.sr = sr;
this.Separator = ' ';
}
private StringBuilder sb = new StringBuilder();
public string ReadWord() {
eol = false;
sb.Clear();
char c;
while (!sr.EndOfStream) {
c = (char)sr.Read();
if (c == Separator) break;
if (IsNewLine(c)) {
eol = true;
char nextch = (char)sr.Peek();
while (IsNewLine(nextch)) {
sr.Read(); // consume all newlines
nextch = (char)sr.Peek();
}
break;
}
sb.Append(c);
}
return sb.ToString();
}
private bool IsNewLine(char c) {
return c == '\r' || c == '\n';
}
public int ReadInt() {
return int.Parse(ReadWord());
}
public double ReadDouble() {
return double.Parse(ReadWord());
}
public bool EOF {
get { return sr.EndOfStream; }
}
public char Separator { get; set; }
bool eol;
public bool EOL {
get { return eol || sr.EndOfStream; }
}
public T ReadObject<T>() where T : IParsable, new() {
var obj = new T();
obj.Parse(this);
return obj;
}
public int[] ReadIntArray() {
int size = ReadInt();
var a = new int[size];
for (int i = 0; i < size; i++) {
a[i] = ReadInt();
}
return a;
}
public double[] ReadDoubleArray() {
int size = ReadInt();
var a = new double[size];
for (int i = 0; i < size; i++) {
a[i] = ReadDouble();
}
return a;
}
public T[] ReadObjectArray<T>() where T : IParsable, new() {
int size = ReadInt();
var a = new T[size];
for (int i = 0; i < size; i++) {
a[i] = ReadObject<T>();
}
return a;
}
internal void NextLine() {
eol = false;
}
}
interface IParsable {
void Parse(StringSteamReader r);
}
It can be used like this:
public void Parse(StringSteamReader r) {
double x = r.ReadDouble();
int y = r.ReadInt();
string z = r.ReadWord();
double[] arr = r.ReadDoubleArray();
MyParsableObject o = r.ReadObject<MyParsableObject>();
MyParsableObject [] oarr = r.ReadObjectArray<MyParsableObject>();
}
I did some benchmarking, comparing StringStreamReader with some other approaches, already proposed (StreamReader.ReadLine and File.ReadAllLines). Here are the methods I used for benchmarking:
private static void Test_StringStreamReader(string filename) {
var sw = new Stopwatch();
sw.Start();
using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
var r = new StringSteamReader(sr);
r.Separator = ' ';
while (!r.EOF) {
var dbls = new List<double>();
while (!r.EOF) {
dbls.Add(r.ReadDouble());
}
}
}
sw.Stop();
Console.WriteLine("elapsed: {0}", sw.Elapsed);
}
private static void Test_ReadLine(string filename) {
var sw = new Stopwatch();
sw.Start();
using (var sr = new StreamReader(new FileStream(filename, FileMode.Open, FileAccess.Read))) {
var dbls = new List<double>();
while (!sr.EndOfStream) {
string line = sr.ReadLine();
string[] bits = line.Split(' ');
foreach(string bit in bits) {
dbls.Add(double.Parse(bit));
}
}
}
sw.Stop();
Console.WriteLine("elapsed: {0}", sw.Elapsed);
}
private static void Test_ReadAllLines(string filename) {
var sw = new Stopwatch();
sw.Start();
string[] lines = System.IO.File.ReadAllLines(filename);
var dbls = new List<double>();
foreach(var line in lines) {
string[] bits = line.Split(' ');
foreach (string bit in bits) {
dbls.Add(double.Parse(bit));
}
}
sw.Stop();
Console.WriteLine("Test_ReadAllLines: {0}", sw.Elapsed);
}
I used a file with 1.000.000 lines of double values (3 values each line). File is located on a SSD disk and each test was repeated multiple times in release-mode. These are the results (on average):
Test_StringStreamReader: 00:00:01.1980975
Test_ReadLine: 00:00:00.9117553
Test_ReadAllLines: 00:00:01.1362452
So, as mentioned StringStreamReader is a bit slower than the other approaches. For 10.000 lines, the performance is around (120ms / 95ms / 100ms).

Categories

Resources