Referencing 2 coordinates and check differential value - c#

This question is a extension of a previous question i asked.
Setting a reference number and comparing that to other data in textfile
I have a set of X & Y data coordinates in a text file.
Recorded Data 1
X: 1081.02409791506 Y:136.538121516361
Data collected at 208786.9115
Recorded Data 2
X: 1082.82841293328 Y:139.344405668078
Data collected at 208810.0446
Recorded Data 4
X: 1525.397051187 Y:1163.1786031393
Data collected at 245756.8823
Recorded Data 5
X: 1524.98201445054 Y:1166.38589429581
Data collected at 245769.489
Recorded Data 6
X: 509.002294087998 Y:913.213486303154
Data collected at 277906.6251
Recorded Data 7
X: 479.826998339658 Y:902.689393940613
Data collected at 277912.9958
I wanna set the first set of data which is X: 1081.02409791506 Y:136.538121516361 as reference point, then it subtracts itself with the next set of data X & Y respectively and check if the resultant value within 100 for both X & Y differential value, if it is, continue the operation. The reference point should keep doing that to the following numbers until it reaches outside the ± 100 range. Once outside the 100 range, now the set of data is X: 1525.397051187 Y:1163.1786031393 because the differential value of 1st data and this data is over 100, then now this set of data is the next reference point and do the same and subtract with the following data below and check if the resultant value within 100. Once outside the 100 range, the next number is X: 509.002294087998 Y:913.213486303154, now, that is the new reference point, and do the same. That is my objective. To put it to simpler terms, The reference points should be moved into a new file.
This code is able to do the above but only for numbers shown below.
278
299
315
360
389
400
568
579
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace ReadTextFile
{
class Program
{
static void Main(string[] args)
{ string inputFile = #"C:\Users\Student\Desktop\ConsoleApp1\ConsoleApp1\Data\TextFile.txt"; // INPUT FILE
string outputFile = #"C:\Users\Student\Desktop\Test.txt"; // OUTPUT FILE
string[] data = File.ReadAllLines(inputFile); // READING FORM FILE
int TotalLine = data.Length; // COUNT TOTAL NUMBER OF ROWS
List<string> FinalList = new List<string>(); // INITIALIZE LIST FOR FINAL RESULT
double CurrentNumber = double.Parse(data[0]), NextNumber, diff; // INITIALIZE OF LOCAL VARIABLES, CURRENT NUMBER = FIRST NUMBER FROM FILE
for (int cntr = 1; cntr < TotalLine; cntr++) // FOR LOOP FOR EACH LINE
{
NextNumber = double.Parse(data[cntr]); //PARSING NEXT NUMBER
diff = CurrentNumber - NextNumber; // GETTING DIFFERENCE
if (diff <= 100 && diff >= -100) // MATCH THE DIFFERENCE
{
continue; // SKIP THE LOGIC IF DIFFERENCE IS LESS THEN 100
}
else
{
FinalList.Add(CurrentNumber.ToString()); // ADDING THE NUMBER TO LIST
CurrentNumber = NextNumber; // POINTING TO NEXT NUMBER
}
}
FinalList.Add(CurrentNumber.ToString()); // ADDING LAST NUMBER
foreach (string d in FinalList) // FOR EACH LOOP TO PRINT THE FINAL LIST
Console.WriteLine(d);
File.WriteAllLines(outputFile, FinalList); // SAVING TO THE FILE
}
How do i do the same for 2 coordinates?
1st condition: At least 1 differential value of X or Y is outside the ± 100 range, that set of data is the new reference data.
2nd condition: If both x and Y differential value is within ± 100 range, we must continue the operation.

Here is a solution, provided the source file content is as stated above:
Recorded Data 1
X: 1081.02409791506 Y:136.538121516361
Data collected at 208786.9115
Recorded Data 2
X: 1082.82841293328 Y:139.344405668078
Data collected at 208810.0446
..
and the target file as follows:
X: 1081.02409791506 Y:136.538121516361
X: 1525.397051187 Y:1163.1786031393
..
Solution
using System;
using System.Collections;
using System.Collections.Generic;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var points = ParseFromFile(
#"C:\Users\Student\Desktop\ConsoleApp1\ConsoleApp1\Data\TextFile.txt");
RenderToFile(
#"C:\Users\Student\Desktop\Test.txt",
Merge(points).ToArray());
}
static void RenderToFile(string fileName, (double x, double y)[] points)
{
var formatProvider = new CultureInfo("en-US", false);
var builder = new StringBuilder();
foreach (var point in points)
{
builder.Append(
$"X: {point.x.ToString(formatProvider)} Y:{point.y.ToString(formatProvider)}");
}
System.IO.File.WriteAllText(fileName, builder.ToString());
}
static (double x, double y)[] ParseFromFile(string fileName)
{
return Parse(System.IO.File.ReadAllText(fileName)).ToArray();
}
static IEnumerable<(double x, double y)> Merge((double x, double y)[] points)
{
points = points ?? throw new ArgumentNullException(nameof(points));
if (points.Length == 0) yield break;
var std = 100;
var current = points[0];
if (points.Length == 1)
{
yield return current;
yield break;
}
for (var i = 1; i < points.Length; i++)
{
var dx = Math.Abs(points[i].x - current.x);
var dy = Math.Abs(points[i].y - current.y);
if (dx <= std && dy <= std)
{
continue;
}
yield return current;
current = points[i];
}
yield return current;
}
static IEnumerable<(double x, double y)> Parse(string raw)
{
var formatProvider = new CultureInfo("en-US", false);
var pattern = new Regex(#"^[Xx][:] (?<x>\d*([.]\d+)?) [Yy][:](?<y>\d*([.]\d+)?)$");
raw = raw ?? throw new ArgumentNullException(nameof(raw));
foreach (var line in raw.Split(
Environment.NewLine, StringSplitOptions.RemoveEmptyEntries).Where(
line => line.ToLowerInvariant().StartsWith("x")))
{
var match = pattern.Match(line);
var xToken = match.Groups["x"].Value.Trim();
var yToken = match.Groups["y"].Value.Trim();
var x = double.Parse(xToken, formatProvider);
var y = double.Parse(yToken, formatProvider);
yield return (x: x, y: y);
}
}
}
}
Explained
First we need to parse the data.
The format provider is required to parse a double correctly from a fixed string with decimal seperator ..
// can parse 1525.397051187, but not 1525,397051187
// en-US is the format you comply with
// 'false' is required to use the default culture settings, not any user overrided
var formatProvider = new CultureInfo("en-US", false);
The pattern ensures we parse the x and y coordinate correctly.
// X: 1525.397051187 Y:1163.1786031393
// we use named groups to capture x (?<x>\d*([.]\d+)?)
// and y (?<y>\d*([.]\d+)?)
var pattern = new Regex(#"^[Xx][:] (?<x>\d*([.]\d+)?) [Yy][:](?<y>\d*([.]\d+)?)$");
Once parsed, we can merge (x,y) coordinates based on your specification. std is the allowed standard deviation for our deltas (dx, dy).
var dx = Math.Abs(points[i].x - current.x);
var dy = Math.Abs(points[i].y - current.y);
if (dx <= std && dy <= std)
{
continue;
}
A note on IEnumerable<T>:
Using this as return value allows us to use the yield syntax. This is called a generator function.
A note on value tuple (double x, double y):
We can use named tuples to avoid creating 'stupid' intermediate classes.

Related

Random number with Probabilities in C#

I have converted this Java program into a C# program.
using System;
using System.Collections.Generic;
namespace RandomNumberWith_Distribution__Test
{
public class DistributedRandomNumberGenerator
{
private Dictionary<Int32, Double> distribution;
private double distSum;
public DistributedRandomNumberGenerator()
{
distribution = new Dictionary<Int32, Double>();
}
public void addNumber(int val, double dist)
{
distribution.Add(val, dist);// are these two
distSum += dist; // lines correctly translated?
}
public int getDistributedRandomNumber()
{
double rand = new Random().NextDouble();//generate a double random number
double ratio = 1.0f / distSum;//why is ratio needed?
double tempDist = 0;
foreach (Int32 i in distribution.Keys)
{
tempDist += distribution[i];
if (rand / ratio <= tempDist)//what does "rand/ratio" signify? What does this comparison achieve?
{
return i;
}
}
return 0;
}
}
public class MainClass
{
public static void Main(String[] args)
{
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(2, 0.3d);
drng.addNumber(3, 0.5d);
//=================
// start simulation
int testCount = 1000000;
Dictionary<Int32, Double> test = new Dictionary<Int32, Double>();
for (int i = 0; i < testCount; i++)
{
int random = drng.getDistributedRandomNumber();
if (test.ContainsKey(random))
{
double prob = test[random]; // are these
prob = prob + 1.0 / testCount;// three lines
test[random] = prob; // correctly translated?
}
else
{
test.Add(random, 1.0 / testCount);// is this line correctly translated?
}
}
foreach (var item in test.Keys)
{
Console.WriteLine($"{item}, {test[item]}");
}
Console.ReadLine();
}
}
}
I have several questions:
Can you check if the marked-by-comment lines are correct or need explanation?
Why doesn't getDistributedRandomNumber() check if the sum of the distribution 1 before proceeding to further calculations?
The method
public void addNumber(int val, double dist)
Is not correctly translated, since you are missing the following lines:
if (this.distribution.get(value) != null) {
distSum -= this.distribution.get(value);
}
Those lines should cover the case when you call the following (based on your example code):
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(1, 0.5d);
So calling the method addNumber twice with the same first argument, the missing code part looks if the first argument is already present in the dictionary and if so it will remove the "old" value from the dictionary to insert the new value.
Your method should look like this:
public void addNumber(int val, double dist)
{
if (distribution.TryGetValue(val, out var oldDist)) //get the old "dist" value, based on the "val"
{
distribution.Remove(val); //remove the old entry
distSum -= oldDist; //substract "distSum" with the old "dist" value
}
distribution.Add(val, dist); //add the "val" with the current "dist" value to the dictionary
distSum += dist; //add the current "dist" value to "distSum"
}
Now to your second method
public int getDistributedRandomNumber()
Instead of calling initializing a new instance of Random every time this method is called you should only initialize it once, so change the line
double rand = new Random().NextDouble();
to this
double rand = _random.NextDouble();
and initialize the field _random outside of a method inside the class declaration like this
public class DistributedRandomNumberGenerator
{
private Dictionary<Int32, Double> distribution;
private double distSum;
private Random _random = new Random();
... rest of your code
}
This will prevent new Random().NextDouble() from producing the same number over and over again if called in a loop.
You can read about this problem here: Random number generator only generating one random number
As I side note, fields in c# are named with a prefix underscore. You should consider renaming distribution to _distribution, same applies for distSum.
Next:
double ratio = 1.0f / distSum;//why is ratio needed?
Ratio is need because the method tries its best to do its job with the information you have provided, imagine you only call this:
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
int random = drng.getDistributedRandomNumber();
With those lines you told the class you want to have the number 1 in 20% of the cases, but what about the other 80%?
And that's where the ratio variable comes in place, it calculates a comparable value based on the sum of probabilities you have given.
eg.
double ratio = 1.0f / distSum;
As with the latest example drng.addNumber(1, 0.2d); distSum will be 0.2, which translates to a probability of 20%.
double ratio = 1.0f / 0.2;
The ratio is 5.0, with a probability of 20% the ratio is 5 because 100% / 5 = 20%.
Now let's have a look at how the code reacts when the ratio is 5
double tempDist = 0;
foreach (Int32 i in distribution.Keys)
{
tempDist += distribution[i];
if (rand / ratio <= tempDist)
{
return i;
}
}
rand will be to any given time a value that is greater than or equal to 0.0, and less than 1.0., that's how NextDouble works, so let's assume the following 0.254557522132321 as rand.
Now let's look what happens step by step
double tempDist = 0; //initialize with 0
foreach (Int32 i in distribution.Keys) //step through the added probabilities
{
tempDist += distribution[i]; //get the probabilities and add it to a temporary probability sum
//as a reminder
//rand = 0.254557522132321
//ratio = 5
//rand / ratio = 0,0509115044264642
//tempDist = 0,2
// if will result in true
if (rand / ratio <= tempDist)
{
return i;
}
}
If we didn't apply the ratio the if would be false, but that would be wrong, since we only have a single value inside our dictionary, so no matter what the rand value might be the if statement should return true and that's the natur of rand / ratio.
To "fix" the randomly generated number based on the sum of probabilities we added. The rand / ratio will only be usefull if you didn't provide probabilites that perfectly sum up to 1 = 100%.
eg. if your example would be this
DistributedRandomNumberGenerator drng = new DistributedRandomNumberGenerator();
drng.addNumber(1, 0.2d);
drng.addNumber(2, 0.3d);
drng.addNumber(3, 0.5d);
You can see that the provided probabilities equal to 1 => 0.2 + 0.3 + 0.5, in this case the line
if (rand / ratio <= tempDist)
Would look like this
if (rand / 1 <= tempDist)
Divding by 1 will never change the value and rand / 1 = rand, so the only use case for this devision are cases where you didn't provided a perfect 100% probability, could be either more or less.
As a side note, I would suggest changing your code to this
//call the dictionary distributions (notice the plural)
//dont use .Keys
//var distribution will be a KeyValuePair
foreach (var distribution in distributions)
{
//access the .Value member of the KeyValuePair
tempDist += distribution.Value;
if (rand / ratio <= tempDist)
{
return i;
}
}
Your test routine seems to be correctly translated.

Reference numbers, and using them to compare numbers in text file

The project is based on Eye Tracker. Let me brief the idea behind the project to understand my problem better.
I have the hardware of Tobii C eye tracker. This eye tracker will be able to give out coordinates of the X, Y of where I am looking at. But this device is very sensitive. When I look at 1 point, the eye tracker will send out many different data of coordinates but within ± 100 range which I found out. Even though you are staring at 1 point, your eyes keep moving, therefore giving out many data. This many data (float numbers) are then saved in a text file. Now I only need 1 data (X coordinate) which signifies the 1 point I am staring instead of the many data which are within the ± 100 range and move it to a new text file.
I have no idea how I should code to do that.
These are the float numbers in the text file.
200
201
198
202
250
278
310
315
360
389
500
568
579
590
When I stare at point 1, the data are 200-300, which are within the ± 100 range. I wanna set the 200 as reference point subtracts itself with the next number and check if the resultant value within 100, if it is, remove them. The reference point should keep doing that to the following numbers until it reaches outside the ± 100 range. Once outside the 100 range, now the number is 310, then now this number is the next reference point and do the same and subtract with the following numbers below and check if the resultant value within 100. Once outside the 100 range, the next number is 500, now, that is the new reference point, and do the same. That is my objective. To put it to simpler terms, The reference points should be moved into a new file.
This is my code so far which get the gaze coordinates and stores them in a text file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Tobii.Interaction;
namespace ConsoleApp1
{
class Program
{
private static void programintro()
{
Console.WriteLine("Press Any Keys To Start");
}
public static void Main(string[] args)
{
programintro();
Console.ReadKey();
double currentX = 0.0;
double currentY = 0.0;
double timeStampCurrent = 0.0;
double diffX = 0.0;
double diffY = 0.0;
int counter = 0;
var host = new Host();
host.EnableConnection();
var gazePointDataStream = host.Streams.CreateGazePointDataStream();
gazePointDataStream.GazePoint((gazePointX, gazePointY, timestamp) =>
{
diffX = gazePointX - currentX;
diffY = gazePointY - currentY;
currentX = gazePointX;
currentY = gazePointY;
timeStampCurrent = timestamp;
if (diffX > 100 || diffX <= -100 || diffY >= 100 || diffY <= -100)
{
counter++;
using (StreamWriter writer = new StreamWriter("C: \\Users\\Student\\Desktop\\FYP 2019\\ConsoleApp1\\ConsoleApp1\\Data\\TextFile1.txt", true))
{
writer.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
writer.WriteLine("=================================================================================================================");
}
Console.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
Console.WriteLine("=================================================================================================================");
}
});
//host.DisableConnection();
while (true)
{
if (counter < 10)
{
continue;
}
else
{
Environment.Exit(0);
}
}
Now my Question is how do I code to read the text file and set a
reference number and subtracts itself with the next number and check
if the resultant value within 100 and have a new reference number if
it outside the ± 100 range. Those reference numbers are then stored in
a new text file.
If there is a code example, I will create a new programme and store there and test it out first.
Assuming that the initial data is present in a list, the logic to get all reference points is as follows:
var initialData = new List<float> { 200,201,198,202,250,278,310,315,360,389,500,568,579,590 };
var lstReferencePoints = new List<float>();
var referencePoint = default(float);
foreach(var num in initialData)
{
if(referencePoint == default(float))
{
referencePoint = num;
}
if(Math.Abs(referencePoint - num) > 100)
{
lstReferencePoints.Add(referencePoint);
referencePoint = num;
}
}
lstReferencePoints.Add(referencePoint);
lstReferencePoints contains the list of referencePoints.
Edit: reading the float numbers from a text file to a List
var pointsArray = File.ReadAllLines(your_file_path);
var initialData = new List<float>(pointsArray.Select(float.Parse));
Storing the lstReferencePoints to a new text file:
using(TextWriter tw = new StreamWriter("newFile_Path"))
{
foreach (var item in lstReferencePoints)
tw.WriteLine(item);
}

Setting a reference number and comparing that to other data in textfile

The project is based on Eye Tracker. Let me brief the idea behind the project to understand my problem better.
I have the hardware of Tobii C eye tracker. This eye tracker will be able to give out coordinates of the X, Y of where I am looking at. But this device is very sensitive. When I look at 1 point, the eye tracker will send out many different data of coordinates but within ± 100 range which I found out. Even though you are staring at 1 point, your eyes keep moving, therefore giving out many data. This many data (float numbers) are then saved in a text file. Now I only need 1 data (X coordinate) which signifies the 1 point I am staring instead of the many data which are within the ± 100 range and move it to a new text file.
I have no idea how I should code to do that.
These are the float numbers in the text file.
200
201
198
202
250
278
310
315
360
389
500
568
579
590
When I stare at point 1, the data are 200-300, which are within the ± 100 range. I wanna set the 200 as reference point subtracts itself with the next number and check if the resultant value within 100, if it is, remove them. The reference point should keep doing that to the following numbers until it reaches outside the ± 100 range. Once outside the 100 range, now the number is 310, then now this number is the next reference point and do the same and subtract with the following numbers below and check if the resultant value within 100. Once outside the 100 range, the next number is 500, now, that is the new reference point, and do the same. That is my objective. To put it to simpler terms, The reference points should be moved into a new file.
This is my code so far which get the gaze coordinates and stores them in a text file.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Tobii.Interaction;
namespace ConsoleApp1
{
class Program
{
private static void programintro()
{
Console.WriteLine("Press Any Keys To Start");
}
public static void Main(string[] args)
{
programintro();
Console.ReadKey();
double currentX = 0.0;
double currentY = 0.0;
double timeStampCurrent = 0.0;
double diffX = 0.0;
double diffY = 0.0;
int counter = 0;
var host = new Host();
host.EnableConnection();
var gazePointDataStream = host.Streams.CreateGazePointDataStream();
gazePointDataStream.GazePoint((gazePointX, gazePointY, timestamp) =>
{
diffX = gazePointX - currentX;
diffY = gazePointY - currentY;
currentX = gazePointX;
currentY = gazePointY;
timeStampCurrent = timestamp;
if (diffX > 100 || diffX <= -100 || diffY >= 100 || diffY <= -100)
{
counter++;
using (StreamWriter writer = new StreamWriter("C: \\Users\\Student\\Desktop\\FYP 2019\\ConsoleApp1\\ConsoleApp1\\Data\\TextFile1.txt", true))
{
writer.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
writer.WriteLine("=================================================================================================================");
}
Console.WriteLine("Recorded Data " + counter + "\n=================================================================================================================\nX: {0} Y:{1}\nData collected at {2}", currentX, currentY, timeStampCurrent);
Console.WriteLine("=================================================================================================================");
}
});
//host.DisableConnection();
while (true)
{
if (counter < 10)
{
continue;
}
else
{
Environment.Exit(0);
}
}
Now my Question is how do I code to read the text file and set a
reference number and subtracts itself with the next number and check
if the resultant value within 100 and have a new reference number if
it outside the ± 100 range. Those reference numbers are then stored in
a new text file.
Based on your sample data here is the code to get only numbers which has 100+ difference.
static void Main(string[] args)
{
string filename = #"C:\PowershellScripts\test.txt"; // INPUT FILE
String outputFile = #"C:\PowershellScripts\result.txt"; // OUTPUT FILE
string[] data = File.ReadAllLines(filename); // READING FORM FILE
int TotalLine = data.Length; // COUNT TOTAL NO OF ROWS
List<string> FinalList = new List<string>(); // INITIALIZE LIST FOR FINAL RESULT
if (TotalLine <= 0) // CHECK IF FILE HAS NO DATA
{
Console.WriteLine("No Data found !");
return;
}
double CurrentNumber = double.Parse(data[0]), NextNumber = 0, diff = 0; // INITIALIZE OF LOCAL VARIABLES, CURRENT NUMBER = FIRST NO FROM FILE
for (int cntr = 1; cntr < TotalLine; cntr++) // FOR LOOP FOR EACH LINE
{
NextNumber = double.Parse(data[cntr]); //PARSING NEXT NO
diff = CurrentNumber - NextNumber; // GETTING DIFFERENCE
if (diff <= 100 && diff >= -100) // MATCH THE DIFFERENCE
{
continue; // SKIP THE LOGIC IF DIFF IS LESS THEN 100
}
else
{
FinalList.Add(CurrentNumber.ToString()); // ADDING THE NO TO LIST
CurrentNumber = NextNumber; // POINTING TO NEXT NO
}
}
FinalList.Add(CurrentNumber.ToString()); // ADDING LAST NO.
foreach (string d in FinalList) // FOR EACH LOOP TO PRINT THE FINAL LIST
Console.WriteLine(d);
File.WriteAllLines(outputFile, FinalList); // SAVING TO THE FILE
}
The above program will generate the output is :
200
310
500

C# using points' offset method in foreach loop to modify array of points

I am having trouble understanding how points' offset method works within a foreach loop to modify an existing array of points. I am able to do it by manually indexing each array entity, but I strongly suspect that is not how it should be done.
*Edit To be clear. What is the best way to store my offset points in MyPoints array?
See code below. I used http://rextester.com/ online C# compiler to test the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Drawing;
namespace Rextester
{
public class Program
{
static int ii = 0;
public static void Main(string[] args)
{
Point[] MyPoints =
{
new Point(0,0),
new Point(10,10),
new Point(20,0)
};
foreach(Point p in MyPoints)
{
p.Offset(5,2); //this works but does not store the new points
} //in MyPoints array when the foreach loop
//exits, which is what I want.
Console.WriteLine("p1x = {0} \t p1y = {1}", MyPoints[0].X, MyPoints[0].Y);
Console.WriteLine("p2x = {0} \t p2y = {1}", MyPoints[1].X, MyPoints[1].Y);
Console.WriteLine("p3x = {0} \t p3y = {1} \n", MyPoints[2].X, MyPoints[2].Y);
foreach(Point p in MyPoints)
{
MyPoints[ii].Offset(5,2);
ii++;
}
Console.WriteLine("p1x = {0} \t p1y = {1}", MyPoints[0].X, MyPoints[0].Y);
Console.WriteLine("p2x = {0} \t p2y = {1}", MyPoints[1].X, MyPoints[1].Y);
Console.WriteLine("p3x = {0} \t p3y = {1}", MyPoints[2].X, MyPoints[2].Y);
}
}
}
//This yields the following
/*
p1x = 0 p1y = 0
p2x = 10 p2y = 10
p3x = 20 p3y = 0
p1x = 5 p1y = 2
p2x = 15 p2y = 12
p3x = 25 p3y = 2*/
System.Drawing.Point is a structure - a value type. The foreach loop copies the Point value out of the collection into the p variable. You then modify the p variable by calling Offset, but that doesn't change the collection at all, because it's only the copy that's modified.
In your second loop, you're modifying the value in the array directly - which is why you're seeing the difference.
A more idiomatic way of doing that would be:
for (int i = 0; i < MyPoints.Length; i++)
{
MyPoints[i].Offset(5, 2);
}
It's worth noting that Point is relatively unusual as it's a mutable value type - the Offset method really changes the value. Most value types (e.g. DateTime) are immutable - methods like AddDays don't modify the value they're called on; instead they return a new value. So to do something similar with an array of dates, you'd need code like this:
for (int i = 0; i < dates.Length; i++)
{
dates[i] = dates[i].AddDays(10);
}
Or you could use LINQ to create a new array:
DateTime[] newDates = dates.Select(date => date.AddDays(10)).ToArray();
You couldn't write it exactly like that for Point because of the way Offset returns void. You'd need something like:
Point[] newPoints = points.Select(point => { point.Offset(5,2); return point; })
.ToArray();

C#: How to speed up my program for searching strong contiguous components in graph

at school i get a task to create a program, which search for strong contiguous components in graph. My program gives correct output, but a program is slow. I cant get into global time requirement. Can somebody help me make a program faster? Thanks you all.
Task:
For a given set of web pages, we want to find largest subsets such that from every page in a subset you can follow links to any other page in the same subset.
Input:
On the first line, there are two numbers, number of the pages N, and total number of links M. Pages are numbered from 0 up to N-1. On lines 2 up to M+1, there are two numbers per line. The first is the source page and the second is the target page of a link.
Output:
On N lines there is a component ID for every single page. The componet ID is the smallest page index on the component.
Time limits:
Amount of data: 10000 -> max 0.2 seconds / Amount of data: 100000 -> max 2 seconds / Amount of data: 500000 -> max 10 seconds
Examples:
Input: Input:
3 3 3 3
0 1 1 2
1 0 2 1
1 2 1 2
Output: Output:
0 0
0 1
2 1
Program:
Program is written in czech language, but i tried description it in english.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace tgh_web
{
class Vrchol //class Vertex
{
public int cislo_vrcholu; //page ID (ID of vertex)
public int cislo_komponenty; //component ID where is page
public bool prepsano = false; //A flag that indicates whether the component ID has been overwritten by the smallest page ID on the component
//for graph
public int typ_vrcholu; //vertex mark -> 0 - unvisited (white), 1 - visiting (gray), 2 - visited/locked (black)
public List<int> sousedici_vrcholy; //neighbors of page (vertex), there is link between page (vertex) and neighbor from List
public int pre_time, post_time; //previsit time and postvisit time of vertex time (for DFS)
//for transpose graph
public int typ_vrcholu_trans; //mark of vertex in transpose graph
public List<int> sousedici_vrcholy_trans; //neighbors of vertex in transpose graph
public int pre_time_trans, post_time_trans; //previsit time and postvisit time of vertex in transpose graph (for DFS in transpose graph)
}
class Program
{
public static int time, time_trans;
public static List<Vrchol> seznam_vrcholu; //list of vertices (pages)
public static int komponenta = 0; //component number
public static Stack<Vrchol> zasobnik; //stack
public static List<int> vrcholy_komponenty; //list of vertices in this component
//DFS in graph
public static void DFS(Vrchol vrchol)
{
vrchol.typ_vrcholu = 1; //mark of vertex is set to 1 - visiting
time = time + 1;
vrchol.pre_time = time; //set previsit time of vertex
zasobnik.Push(vrchol);
List<Vrchol> list_sousedici_vrcholy = new List<Vrchol>();
foreach (int index in vrchol.sousedici_vrcholy)
list_sousedici_vrcholy.Add(seznam_vrcholu.Find(x => x.cislo_vrcholu == index));
foreach (Vrchol v in list_sousedici_vrcholy)
if (v.typ_vrcholu == 0)
DFS(v);
vrchol.typ_vrcholu = 2; //mark of vertex is set to 2 - visited/locked
time = time + 1;
vrchol.post_time = time;
zasobnik.Pop();
vrchol.cislo_komponenty = komponenta;
if (zasobnik.Count == 0)
komponenta++;
}
//DFS in transpose graph
public static void DFS_trans(Vrchol vrchol)
{
vrchol.typ_vrcholu_trans = 1;
time_trans = time_trans + 1;
vrchol.pre_time_trans = time_trans;
List<Vrchol> list_sousedici_vrcholy_trans = new List<Vrchol>();
foreach (int index in vrchol.sousedici_vrcholy_trans)
list_sousedici_vrcholy_trans.Add(seznam_vrcholu.ElementAt(index));
foreach (Vrchol v in list_sousedici_vrcholy_trans)
if (v.typ_vrcholu_trans == 0)
DFS_trans(v);
vrchol.typ_vrcholu_trans = 2;
time_trans = time_trans + 1;
vrchol.post_time_trans = time_trans;
}
public static void Main(string[] args)
{
int time, time_trans = 0;
seznam_vrcholu = new List<Vrchol>();
zasobnik = new Stack<Vrchol>();
vrcholy_komponenty = new List<int>();
int pocet_vrcholu, pocet_hran, aktualni_hrana = 0; //number of vertex, number of links, actual_link is set to 0
string prvni_radek, radek; //first row, next rows
string[] casti_prv_radku, casti_radku; //parts of firts row, parts of next rows
int hrana_out, hrana_in; //links
//loading first row - input (first row - number of pages(vertices) + total number of links)
prvni_radek = Console.ReadLine();
casti_prv_radku = prvni_radek.Split(' ');
int.TryParse(casti_prv_radku[0], out pocet_vrcholu);
int.TryParse(casti_prv_radku[1], out pocet_hran);
//creating vertex
for (int i = 0; i < pocet_vrcholu; i++)
{
Vrchol v = new Vrchol();
v.cislo_vrcholu = i; //id of vertex = i
v.typ_vrcholu = 0; //mark of vertex set to 0 - unvisited
v.typ_vrcholu_trans = 0; //mark of vertex in transpose graph set to 0
v.sousedici_vrcholy = new List<int>();
v.sousedici_vrcholy_trans = new List<int>();
seznam_vrcholu.Insert(i, v);
}
//loading next rows - input (on a row is the source page and the target page of a link
while (aktualni_hrana < pocet_hran) //actual_link < total number of links
{
radek = Console.ReadLine();
casti_radku = radek.Split(' ');
int.TryParse(casti_radku[0], out hrana_out);
int.TryParse(casti_radku[1], out hrana_in);
//targed page, where link goes in (hrana_in), add to the list of neighbors (sousedici_vrcholy) that belongs to source page, where link goes out (hrana_out)
seznam_vrcholu.ElementAt(hrana_out).sousedici_vrcholy.Add(hrana_in);
//It is the same, but pages (vertex) are swaped (to create a transpose graph)
seznam_vrcholu.ElementAt(hrana_in).sousedici_vrcholy_trans.Add(hrana_out);
aktualni_hrana++;
}
//DFS in transpose graph
foreach (Vrchol v in seznam_vrcholu)
if (v.typ_vrcholu_trans == 0)
DFS_trans(v);
//top sorting by postvisit time in transpose graph
seznam_vrcholu.Sort(delegate(Vrchol x, Vrchol y)
{
if (x.post_time_trans == null && y.post_time_trans == null) return 0;
else if (x.post_time_trans == null) return -1;
else if (y.post_time_trans == null) return 1;
else return y.post_time_trans.CompareTo(x.post_time_trans);
});
//DFS in graph, where vertices are sorted
foreach(Vrchol v in seznam_vrcholu)
if (v.typ_vrcholu == 0)
DFS(v);
//overwriting component ID
//the componet ID is the smallest page index on the component
for (int i = 0; i < komponenta; i++ )
{
vrcholy_komponenty.Clear();
foreach(Vrchol v in seznam_vrcholu)
if(v.cislo_komponenty == i && v.prepsano == false)
vrcholy_komponenty.Add(v.cislo_vrcholu);
int min = vrcholy_komponenty.ElementAt(0);
foreach(int cislo_vrcholu in vrcholy_komponenty)
if(cislo_vrcholu < min)
min = cislo_vrcholu;
//overwriting component ID and setting overwritten flag (v.prepsano) as true
foreach (Vrchol v in seznam_vrcholu)
if (v.cislo_komponenty == i && v.prepsano == false)
{
v.cislo_komponenty = min;
v.prepsano = true;
}
}
//output
for (int j = 0; j < pocet_vrcholu; j++)
Console.WriteLine(seznam_vrcholu.Find(x => x.cislo_vrcholu == j).cislo_komponenty);
Console.ReadKey();
}
}
}

Categories

Resources