Combine Aggregate and Select Linq - c#

I have a Listof points and I want to calculate the remaining distance to the end using Linq (given an index):
double remainingToEnd = Points.Skip(CurrentIndex).Aggregate((x, y) => x.DistanceTo(y));
This doesn't compile:
Cannot convert lambda expression to intended delegate type because
some of the return types in the block are not implicitly convertible
to the delegate return type
I normally solve this situation projecting by using the Select extension, but that would prevent me from calculating the distance afterwards.
This is easily achieved by using a loop but I want to know if it is possible with some simple Linq. I would like to avoid anonymous types too.
Point is defined like:
public class Point
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
public float DistanceTo(Point p2)
{
float x = this.X - p2.X;
float y = this.Y - p2.Y;
float z = this.Z - p2.Z;
return (float)Math.Sqrt((x * x) + (y * y) + (z * z));
}
}

Assume you want to calculate total distance between points in collection (starting from some index). You need previous point on each step. You can get it by zipping points collection with itself:
double remainingToEnd = Points.Skip(CurrentIndex)
.Zip(Points.Skip(CurrentIndex + 1), (x,y) => x.DistanceTo(y))
.Sum();
Zip will produce pairs of starting and ending points. Result selector function will select distance between points for each pair. And then you just calculate sum or distances.
You can solve this task with aggregation as will, but you need to store last point on each step. So you need accumulator which will keep both current distance and last point:
var remainingToEnd = Points.Skip(CurrentIndex).Aggregate(
new { total = 0.0, x = Points.Skip(CurrentIndex).FirstOrDefault() },
(a, y) => new { total = a.total + a.x.DistanceTo(y), x = y },
a => a.total);
And keep in mind, that Skip means just iterating your sequence item by item without doing anything. If you have a lot of points, skipping twice can hurt your performance. So if you have list of points, and performance matters, then simple for loop will do the job:
double remainingToEnd = 0.0;
for(int i = CurrentIndex; i < Points.Count - 1; i++)
remainingToEnd += Points[i].DistanceTo(Points[i+1]);

Try this:
double remainingToEnd = Points.Skip(CurrentIndex).Sum(point => point.DistanceTo(Points[Points.Findindex(p => p == point) - 1]));

Related

How to generate a number from 3 inputs

I am trying to create a system in unity where a number is generated (with a range from 0 to 9) from 3 float inputs. This is to create a procedural game where being in the same location always gives the same output. Because of this, I don't just want to use an in-built random method, as it would give different results each time. However, I am struggling to create a method that can achieve this.
To summarise:
I want to be able to put in 3 values, e.g.:
[3.2344234, -44.33030, 0.22222]
And have the method come up with a number like:
[2]
But I don't want to use an in-built method.
This is what I have tried so far - It's a bit of a mess:
public float randomNumber(float x, float y, float z)
{
float stage1 = (float)((Mathf.Sign(x + 512) / Mathf.Cos(z + 42) + Mathf.SmoothDampAngle(z,y,ref(z),x) * Mathf.Atan2(y - 15,z)) / 24 + 5 * 6)%7.2f;
string stage2 = stage1.ToString();
int stage3 = (int)stage2[5];
float result = stage3;
return result;
}
Any suggestions to properly generate random numbers are most welcome.
If distribution is not that important, the following will do:
static int GetRandomNumber(float x, float y, float z)
{
// convert the floats to byte arrays
var b1 = BitConverter.GetBytes(x);
var b2 = BitConverter.GetBytes(y);
var b3 = BitConverter.GetBytes(z);
// turn the byte arrays into integers
var i1 = BitConverter.ToInt32(b1);
var i2 = BitConverter.ToInt32(b2);
var i3 = BitConverter.ToInt32(b3);
// XOR the 3 integers
var merged = i1 ^ i2 ^ i3;
// get the positive value of the integer and do a modulo by 10
// that will give you a value between 0 and 9
var result = Math.Abs(merged) % 10;
return result;
}
Same using unsafe:
static unsafe int GetRandomNumberUnsafe(float x, float y, float z)
{
var i1 = *(int*)(&x);
var i2 = *(int*)(&y);
var i3 = *(int*)(&z);
var merged = i1 ^ i2 ^ i3;
var result = Math.Abs(merged) % 10;
return result;
}
----- EDIT -----
Starting from .NET Core 2.2, you can also use HashCode.Combine:
static unsafe int GetRandomNumber(float x, float y, float z)
{
var hash = HashCode.Combine(x, y, z);
var result = Math.Abs(hash) % 10;
return result;
}
Basically you can use
whatever hash algorithm you want that generates distributed values from the given input
use modulo on the hash in order to map it into your required range
since hashes might be negative values get the absolute value of the result
You could e.g. just do
public int randomNumber(float x, float y, float z)
{
var hash = x.GetHashCode();
hash = (hash * 397) ^ y.GetHashCode();
hash = (hash * 397) ^ z.GetHashCode();
return Mathf.Abs(hash % 10);
}
Or in newer version of Unity using .NET Framework 4.7.X you can actually use tuples and simply do e.g.
public int randomNumber(float x, float y, float z)
{
return Mathf.Abs((x, y, z).GetHashCode() % 10);
}
Or as alternative assuming your values come from a Vector3 since you speak of a location you could also use your given Vector3 directly
public int randomNumber(Vector3 vector)
{
return Mathf.Abs(vector.GetHashCode() % 10);
}
Limit you map area for x, y and z axes and after that divide them to 10 as incremental numbers: For example x axis -100:100 freedom of movement. (30-(-30))/3=20 will give incremental number of x axis. Make an array of 10 imaginary boxes where different x, y and z location values will give different numbers according to your array. Your imaginary 3D object with cubes must include 10 members so 3x3x3 will give 9. Add one box(it is up to you) to one dimension and after that every increments step in one axis will change the number.
If you want certain numbers will give always same result make as mod4(input_1)+mod4(input_2)+mod4(input_3)+mod2(input_1+input_2+input_3) may work. But I didn't check last formula, you should check it.

Returning list that satisfy conditions

I have two lists filled with (x, y) data points; one of the lists being the main list and the other being a list I want to compare against.
So for example, in the main list I try to find "gaps" in the data; say I'm looking at two x data points x1 = 21 and x2 = 24. The difference between those two points is greater than deltaX = 1. So what I'd like to do, is look through the second list, and find all data points between x1 = 21 and x2 = 24 to "fill in the gap".
I have something like this:
double diffX = Math.Abs(datapoints1[i + 1].X - datapoints1[i].X);
if (diffX > deltaX)
{
datapoints1.AddRange(LookBetween(datapoints1[i + 1].X, datapoints1[i].X);
}
.
.
.
private IEnumerable<PointXY> LookBetween(double upperLimit, double lowerLimit)
{
return datapoints2.Where(x => x.X < upperLimit && x.X > lowerLimit);
}
LookBetween seems to return a boolean because nothing gets added to the main list. How can I get it to return a list of the x, y values that match the criteria based on the gap of x?
It seems like you're trying to do a pairwise iteration through neighbors in datapoints1.
Using the function linked above, you might have something like:
var pairs = datapoints1.Pairwise((d1, d2) => new
{
x1 = d1.X,
x2 = d2.X,
diffX = Math.Abs(d1.X - d2.X),
});
var gaps = from pair in pairs
where pair.diffX > deltaX
from dp2 in datapoints2
where pair.x1 < dp2.X && dp2.X < pair.x2
select dp2;
Or using your LookBetween function...
var gaps = from pair in pairs
where pair.diffX > deltaX
from dp2 in LookBetween(pair.x2, pair.x1)
select dp2;

Passing a pointer to a class property

Edit: This is an example program, I understand that there is a sum operator in linq. However, I was trying to ask specifically on how to pass the pointer to the property to do something complex with it, not necessarily this summing algorithm.
Lets say I have a class like this:
class Point
{
double x {get; set;}
double y {get; set;}
}
and in my main program I had 2 functions.
double function1(List<Point> PointList)
{
double sum = 0.0;
foreach(Point p in PointList)
{
sum += p.x;
}
return sum;
}
double function2(List<Point> PointList)
{
double sum = 0.0;
foreach(Point p in PointList)
{
sum += p.y;
}
return sum;
}
These functions are completely identical except one is summing the X values, the other is summing the Y values. Since these are so similar, I would like to combine them such that I basically pass in that List of Points, and something to identify that I want to do the X or the Y based calculation.
I know I could put in a switch statement and tell it whether it was the X or the Y based on an enum, or a number that is defined to tell it to do the X or the Y. If this was a normal function, I suppose I could use s delegate, but this is a property, so I'm not sure how that translates.
So how do I combine those functions into something like:
double bothFunctions(List<Point> PointList, var propertyToUse)
{
double sum = 0.0;
foreach(Point p in PointList)
{
sum += p.propertyToUse;
}
return sum;
}
Where propertyToUse points to either X or Y.
The current solutions work great, but they assume either that your operation is sum, or that you will have only two possible properties (X and Y).
If you want more freedom, you can do:
double function(List<Point> PointList, Func<Point, double> selector)
{
double sum = 0.0;
foreach (Point p in PointList)
{
sum += selector(p);
}
return sum;
}
Then you call it like this:
double sumX = function(PointList, p => p.X);
double sumY = function(PointList, p => p.Y);
you'll be able to write more complicated selectors, like:
double sumXY = function(PointList, p => p.X + p.Y);
You should look at using the Enumerable.Sum extension method instead of writing your own.
var sumX = pointList.Sum(item => item.X);
var sumY = pointList.Sum(item => item.Y);
You will need to add using System.Linq; if not already present in your code file.
I think you're making it too complicated. You can use a method like this:
double bothFunctions(List<Point> PointList, bool sumX)
{
double sum = 0.0;
foreach(Point p in PointList)
{
if (sumX)
sum += p.x;
else
sum += p.y;
}
return sum;
}
Or just use LINQ:
var ySum = pointList.Sum(e => e.y);
var xSum = pointList.Sum(e => e.x);

Why is my implementation of the parking lot test for random number generators producing bad results?

I'm trying to write an implementation of the parking lot test for random number generators. Here are the sources that I'm getting my information about the test from: Intel math library documentation and Page 4 of this paper along with the phi function for probability density listed here.
I wrote an implementation of the test in C#. It uses a 100x100 grid whose values are initially set to null. I then use the random number generator to generate random integers for x and y. If that index of the grid and it's neighbors are empty, that index gets set to 1. Otherwise, nothing happens because there was a "crash".
I ran it using C# System.Random generator. I don't believe the results are correct because I always get very near 3079 points parked, which is about 500 short of the average I'm supposed to get. It's also yields a p-value of 2.21829146215425E-90.
My code is below. Does anyone have any experience with this or can anyone see something that I might be doing incorrectly in my implementation? Any help would be greatly appreciated.
private void RunParkingLotTest()
{
points = new int?[100,100];
int parked = 0;
for (int i = 0; i < 12000; i++)
{
int x = random.Next(100);
int y = random.Next(100);
if (IsSafeToPark(x, y))
{
points[x, y] = 1;
parked++;
}
}
Console.WriteLine("Parked: " + parked + "\nP value: " + PhiFunction((parked-3523)/21.9));
}
private bool IsSafeToPark(int x, int y)
{
return PointIsEmpty(x, y)
&& LeftOfPointIsEmpty(x, y)
&& RightOfPointIsEmpty(x, y)
&& BelowPointIsEmpty(x, y)
&& AbovePointIsEmpty(x, y);
}
private bool AbovePointIsEmpty(int x, int y)
{
if (y == 99)
{
return true;
}
else
return points[x, y + 1] == null;
}
private bool BelowPointIsEmpty(int x, int y)
{
if (y == 0)
{
return true;
}
else
return points[x, y - 1] == null;
}
private bool RightOfPointIsEmpty(int x, int y)
{
if (x == 99)
{
return true;
}
else
return points[x + 1, y] == null;
}
private bool LeftOfPointIsEmpty(int x, int y)
{
if (x == 0)
{
return true;
}
else
return points[x - 1, y] == null;
}
private bool PointIsEmpty(int x, int y)
{
return points[x, y] == null;
}
private double PhiFunction(double x)
{
//ϕ(x) = (2π)−½e−x2/2
return ((1 / Math.Sqrt(2 * Math.PI)) * Math.Exp(-(Math.Pow(x, 2)) / 2));
}
edit - The problems with my original implementation were
I was plotting squares instead of disks
I only plotted points at integer values. I should have used decimal values instead.
As a result of the above two, I needed to change my distance check
Thanks to Chris Sinclair and mine z for help in figuring this out. The final code is posted below.
I'm going to take a stab at this, and admittedly, I have not attempted any such test, so forgive me if I'm way off. In general though, the .NET Random implementation is pretty good and I've never had issues with it, so I wouldn't suspect that initially especially since you're properly reusing the same instance instead of creating new ones.
Reading from the parking.pdf, and from the Intel documentation, it seems that they're using discs, and compute the distance from their centre points. Your implementation is using squares (array of 1 distance between spots) and thus ignoring diagonals.
From the pdf:
If disks were being used, the distance between the particles r =
p(x(i) − z)2 + (y(i) − z)2 would need to be less than or equal to one.
Does it matter whether one uses disks or squares? An indication of the
importance of which geometric figure is parked can be obtained by
comparing the area occupied by a square of side 1.0 to the area of a
disk of diameter 1.0. The ratio of the areas, disk to square, is π/4.
Therefore, it would be anticipated that more disks could be placed in
a box than squares in the same number of tries.
And the Intel doc:
The test assumes a next random point (x, y) successfully ”parked”, if
it is far enough from every previous successfully ”parked” point. The
sufficient distance between the points (x1, y1) and (x2, y2) is
min(|x1 - x2|,|y1 - y2|) > 1.
I'm guessing that the π/4 disk to square ratio and the differences between how many discs can fit vs squares might be why you're seeing a different number. (although right now I fail to see a directly relationship between 3523 and 3070 and π/4. 3523 * π/4 = 2767, which is close, but I'm sure if there's a relationship it's slightly more complex than just simple multiplication.)
Not a great answer, but my best guess.
EDIT: Interestingly enough, I did a quick implementation using discs with 1 unit diameter and getting results around 4000 parked. So maybe there's a bit more to this than my untrained self can grasp (or maybe .NET's Random doesn't pass the test?) Anyway, here's my disc implementation:
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
//make sure it's "inside" the box
if (pointToPark.X < 0.5 || pointToPark.X > 99.5
|| pointToPark.Y < 0.5 || pointToPark.Y > 99.5)
return false;
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Sqrt((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y));
}
Using my likely too simple application of the π/4 ratio yields about 3142. A bit closer, but it seems very incorrect.
EDIT: As #mike z pointed out, my test using directly distance is incorrect. According to the parameters of the test, which I forgot about, just checks that the X and Y distance are greater than 1. Changing my Distance check to:
Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y))
Yields a much closer result around 3450, which is pretty close. If I take out my "//make sure it's "inside" the box" check, averaged over 10 tries gets 3531!
So my final, "working" code is:
public struct Point
{
public double X,Y;
public Point(double x, double y)
{
this.X = x;
this.Y = y;
}
}
List<Point> parkedCars = new List<Point>();
Random random = new Random();
void Main()
{
int parked = 0;
for (int i = 0; i < 12000; i++)
{
double x = random.NextDouble() * 100;
double y = random.NextDouble() * 100;
Point pointToPark = new Point(x, y);
if (IsSafeToPark(pointToPark))
{
parkedCars.Add(pointToPark);
parked++;
}
}
Console.WriteLine("Parked: " + parked);
}
private bool IsSafeToPark(Point pointToPark)
{
if (parkedCars.Any(p => Distance(pointToPark, p) <= 1))
return false;
return true;
}
private double Distance(Point p1, Point p2)
{
return Math.Max(Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
}
EDIT: I ran the test 100 times twice, and averaged the results to 3521.29 and 3526.74 respectively. Not sure if this means there still something slightly more to this, but perhaps this is just indicative of rounding or floating point precision differences between .NET and Fortran.

How can I convert this divide and conquer code to compare one point to a list of points?

I found this code on the website http://rosettacode.org/wiki/Closest-pair_problem and I adopted the C# version of the divide and conquer method of finding the closest pair of points but what I am trying to do is adapt it for use to only find the closest point to one specific point. I have googled quite a bit and searched this website to find examples but none quite like this. I am not entirely sure what to change so that it only checks the list against one point rather than checking the list to find the two closest. I'd like to make my program operate as fast as possible because it could be searching a list of several thousand Points to find the closest to my current coordinate Point.
public class Segment
{
public Segment(PointF p1, PointF p2)
{
P1 = p1;
P2 = p2;
}
public readonly PointF P1;
public readonly PointF P2;
public float Length()
{
return (float)Math.Sqrt(LengthSquared());
}
public float LengthSquared()
{
return (P1.X - P2.X) * (P1.X - P2.X)
+ (P1.Y - P2.Y) * (P1.Y - P2.Y);
}
}
public static Segment Closest_BruteForce(List<PointF> points)
{
int n = points.Count;
var result = Enumerable.Range(0, n - 1)
.SelectMany(i => Enumerable.Range(i + 1, n - (i + 1))
.Select(j => new Segment(points[i], points[j])))
.OrderBy(seg => seg.LengthSquared())
.First();
return result;
}
public static Segment MyClosestDivide(List<PointF> points)
{
return MyClosestRec(points.OrderBy(p => p.X).ToList());
}
private static Segment MyClosestRec(List<PointF> pointsByX)
{
int count = pointsByX.Count;
if (count <= 4)
return Closest_BruteForce(pointsByX);
// left and right lists sorted by X, as order retained from full list
var leftByX = pointsByX.Take(count / 2).ToList();
var leftResult = MyClosestRec(leftByX);
var rightByX = pointsByX.Skip(count / 2).ToList();
var rightResult = MyClosestRec(rightByX);
var result = rightResult.Length() < leftResult.Length() ? rightResult : leftResult;
// There may be a shorter distance that crosses the divider
// Thus, extract all the points within result.Length either side
var midX = leftByX.Last().X;
var bandWidth = result.Length();
var inBandByX = pointsByX.Where(p => Math.Abs(midX - p.X) <= bandWidth);
// Sort by Y, so we can efficiently check for closer pairs
var inBandByY = inBandByX.OrderBy(p => p.Y).ToArray();
int iLast = inBandByY.Length - 1;
for (int i = 0; i < iLast; i++)
{
var pLower = inBandByY[i];
for (int j = i + 1; j <= iLast; j++)
{
var pUpper = inBandByY[j];
// Comparing each point to successivly increasing Y values
// Thus, can terminate as soon as deltaY is greater than best result
if ((pUpper.Y - pLower.Y) >= result.Length())
break;
Segment segment = new Segment(pLower, pUpper);
if (segment.Length() < result.Length())
result = segment;// new Segment(pLower, pUpper);
}
}
return result;
}
I used this code in my program to see the actual difference in speed and divide and conquer easily wins.
var randomizer = new Random(10);
var points = Enumerable.Range(0, 10000).Select(i => new PointF((float)randomizer.NextDouble(), (float)randomizer.NextDouble())).ToList();
Stopwatch sw = Stopwatch.StartNew();
var r1 = Closest_BruteForce(points);
sw.Stop();
//Debugger.Log(1, "", string.Format("Time used (Brute force) (float): {0} ms", sw.Elapsed.TotalMilliseconds));
richTextBox.AppendText(string.Format("Time used (Brute force) (float): {0} ms", sw.Elapsed.TotalMilliseconds));
Stopwatch sw2 = Stopwatch.StartNew();
var result2 = MyClosestDivide(points);
sw2.Stop();
//Debugger.Log(1, "", string.Format("Time used (Divide & Conquer): {0} ms", sw2.Elapsed.TotalMilliseconds));
richTextBox.AppendText(string.Format("Time used (Divide & Conquer): {0} ms", sw2.Elapsed.TotalMilliseconds));
//Assert.Equal(r1.Length(), result2.Length());
You can store the points in a better data structure that takes advantage of their position. Something like a quadtree.
The divide and conquer algorithm that you are trying to use doesn't really apply to this problem.
Don't use this algorithm at all, just go through the list one at a time comparing the distance to your reference point and at the end return the point that was the closest. This will be O(n).
You can probably add some extra speed ups but this should be good enough.
I can write some example code if you want.
You're mixing up two different problems. The only reason divide and conquer for the closest pair problem is faster than brute force is that it avoids comparing every point to every other point, so that it gets O(n log n) instead of O(n * n). But finding the closest point to just one point is just O(n). How can you find the closest point in a list of n points, while examining less than n points? What you're trying to do doesn't even make sense.
I can't say why your divide and conquer runs in less time than your brute force; maybe the linq implementation runs slower. But I think you'll find two things: 1) Even if, in absolute terms, your implementation of divide and conquer for 1 point runs in less time than your implementation of brute force for 1 point, they still have the same O(n). 2) If you just try a simple foreach loop and record the lowest distance squared, you'll get even better absolute time than your divide and conquer - and, it will still be O(n).
public static float LengthSquared(PointF P1, PointF P2)
{
return (P1.X - P2.X) * (P1.X - P2.X)
+ (P1.Y - P2.Y) * (P1.Y - P2.Y);
}
If, as your question states, you want to compare 1 (known) point to a list of points to find the closest then use this code.
public static Segment Closest_BruteForce(PointF P1, List<PointF> points)
{
PointF closest = null;
float minDist = float.MaxValue;
foreach(PointF P2 in points)
{
if(P1 != P2)
{
float temp = LengthSquared(P1, P2);
if(temp < minDist)
{
minDist = temp;
closest = P2;
}
}
}
return new Segment(P1, closest);
}
However, if as your example shows, you want to find the closest 2 points from a list of points try the below.
public static Segment Closest_BruteForce(List<PointF> points)
{
PointF closest1;
PointF closest2;
float minDist = float.MaxValue;
for(int x=0; x<points.Count; x++)
{
PointF P1 = points[x];
for(int y = x + 1; y<points.Count; y++)
{
PointF P2 = points[y];
float temp = LengthSquared(P1, P2);
if(temp < minDist)
{
minDist = temp;
closest1 = P1;
closest2 = P2;
}
}
}
return new Segment(closest1, closest2);
}
note the code above was written in the browser and may have some syntax errors.
EDIT Odd... is this an acceptable answer or not? Down-votes without explanation, oh well.

Categories