Two Dimensional Array farthest point search - c#

I am trying to retrieve the farthest point in a collection, by comparing the distances from each point in the pointsToSearchFrom List to each other point in the pointCloudToSearchList. You can see a sample scenario from the attached image. I am still not an expert in navigating these data structures, and this algorithm exceeds my current knowledge of traversing two dimensional arrays.
Here is the code that I have until know. Any help would be great.
public static void Test(List<Point3d> pointsToSearchFrom, List<Point3d> pointCloudToSearch)
{
int rows = pointsToSearchFrom.Count;
int columns = pointCloudToSearch.Count;
double[,] arrayDistance = new double [rows, columns];
for (int i = 0; i < pointsToSearchFrom.Count; i++)
{
for (int j = 0; j < pointCloudToSearch.Count; j++)
{
arrayDistance[i, j] = (pointsToSearchFrom[i] - pointCloudToSearch[j]).magnitude;
}
}
}

You can use a two-d array for this, but you do not have to.
What you need is the MaxBy method which can be found here:
https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/MaxBy.cs
We begin by constructing a sequence of pairs:
var pairs = from first in pointsToSearchFrom
from second in pointCloudToSearch
select new { first, second };
Note that in C# 7 you would use a tuple, not an anonymous type here.
Now we have a sequence of all possible pairs. Use MaxBy to find the pair that maximizes the given quantity:
var maxPair = pairs.MaxBy(pair => (pair.first - pair.second).magnitude);
And now you're done:
Console.WriteLine($"{maxPair.first} --> {maxPair.second});
If you have a large collection of points -- like, thousands or millions -- then there are special techniques you have to use, because if there are thousands of points then there will be millions of pairs, and if there are millions of points there will be trillions of pairs. But if you just have a small handful, this technique works fine.

I didn't catch on at first that this was 3D. I used a class and list to store the 2 points and the distances between them. That way I could not only get the longest distance but the 2 points producing the distance.
public class Distances
{
public double Distance { get; set; }
public Point3D FromPoint { get; set; }
public Point3D ToPoint { get; set; }
public Distances(Point3D from, Point3D to)
{
FromPoint = from;
ToPoint = to;
Distance = (to - from).Length;
}
}
private void OP2()
{
List<Point3D> searchFrom = new List<Point3D>()
{
new Point3D(20,30,50),
new Point3D(10,50,10),
new Point3D(30,40,20),
new Point3D(60,30,10)
};
List<Point3D> searchCloud = new List<Point3D>()
{
new Point3D(60,70,80),
new Point3D(110,30,30),
new Point3D(80,110,55),
new Point3D(90,20,90)
};
List<Distances> resultDistances = new List<Distances>();
foreach (Point3D p1 in searchFrom)
{
foreach (Point3D p2 in searchCloud)
{
Distances d = new Distances(p1, p2);
resultDistances.Add(d);
}
}
//The following is just for testing purposes, skip to var longestDistance
List<Distances> distancesInOrder = resultDistances.OrderByDescending(i => i.Distance).ToList<Distances>();
foreach (Distances d in distancesInOrder)
{
Debug.Print($"From Point ({d.FromPoint.X}, {d.FromPoint.Y}, {d.FromPoint.Z}) To Point ({d.ToPoint.X}, {d.ToPoint.Y}, {d.ToPoint.Z}) Distance = {d.Distance}");
}
var longestDistance = resultDistances.OrderByDescending(i => i.Distance).FirstOrDefault();
//this gives you the longest distance and the two points
MessageBox.Show($"First Point ({longestDistance.FromPoint.X}, {longestDistance.FromPoint.Y}, {longestDistance.FromPoint.Z}) Cloud Point ({longestDistance.ToPoint.X}, {longestDistance.ToPoint.Y}, {longestDistance.FromPoint.Z}) Distance = {longestDistance.Distance}");
}

Related

Two lists of coords returning most distinct minimal for one list based on distance function

I have a list of coords using Tuple, since I don't have access to the Drawing library to use 'Point'.
List<Tuple<int,int>> coords = new List<Tuple<int,int>>();
string[] movement = new string[temp.Count];
for(int i=1000; i<=8000; i=i+2300)
for(int j=1000; j<=15000; j=j+2000)
coords.Add(Tuple.Create(j,i));
coords.RemoveAll(x=> 3500>= getDist(0,0,x.Item1,x.Item2) );
coords.RemoveAll(x=> 3500>= getDist(16000,9000,x.Item1,x.Item2) );
I have player pieces in a list, two examples below.
List<int[]> player = new List<int[]>() {new int[]{0,726,1084,0,0,5},new int[]{2,1481,2208,0,0,-1} };
//piece numb, loc_X, loc_Y, teamID, state, value
Turn-based movements must be figured, and when the state of a piece indicates scouting, I want to find the most minimal coord-point-set to each scouting piece, without two pieces going to the same set of coords. If I try to use a foreach loop like:
foreach(var myBust in temp) {
int minDist = coords.Select(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) ).OrderBy(x => x).Distinct().First();
coords.RemoveAll(x => getDist(16000,9000,x.Item1,x.Item2) == minDist); }
then I fall into an issue that the first piece configured may not be the closest to a point-set as compared to another scouting piece, which is why the foreach loop doesn't work or me. Therefore I want some type of linq/lambda statement that can return the said "Distinct" minimal coord-point in the coord list in comparison (by getDist and Min) to all the player pieces.
//Not sure why this doesn't give me what I am looking for
int minDist= coords.Zip(player, (x,y) => getDist(x.Item1,x.Item2,y[1],y[2])).Min();
My dist method.
static int getDist(int x1, int y1, int x2, int y2)
{ return Convert.ToInt32( Math.Sqrt(Math.Pow(x1-x2,2) + Math.Pow(y1-y2,2) );}
So for an answer I am looking for a way to run a function on two lists, which runs a function (but doesn't aggregate anything together), and can return whatever I want from one or both of those lists.
Based on the answer from #Jacob I came up with the below so far:
string[] movement = new string[player.Count];
List<int[]> temp = player;
while(temp.Any()){
HashSet<int> dists = new HashSet<int>();
foreach(var myBust in temp)
{ dists.UnionWith(coords.Select(x => getDist(x.Item1,x.Item2,myBust[1],myBust[2]) )); }
foreach(var myBust in player)
{ if(coords.Exists(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ) )
{
Tuple<int,int> result = coords.FindAll(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ).First();
movement[player.IndexOf(myBust)] = "Move " + result.Item1 + " " + result.Item2;
Console.WriteLine("Player Number "+myBust[0]+" going a dist of "+dists.Min()+" to coords "+result.Item1+","+result.Item2);
coords.Remove(result);
temp.Remove(myBust);
}
}
}
This gives the correct output of "Player Number 2 going a dist of 1871 to coords 3000,3300".
That is at least the first iteration through but then throws the error "Collection was modified; enumeration operation may not execute."
Any suggestions or modifications would be appreciated.
As I understand it, you want each player piece to get its closest coordinates from a set of coordinates, and you also want each player piece to not share a coordinate. There is probably not a simple one-liner for that without avoiding terrible algorithmic complexity. You have two major concerns; finding the closest coordinates and eliminating previously-used coordinates or players.
So let's use this algorithm:
Create a list of player/coordinate pairs, ordered by distance.
Create an empty set of players & coordinates that have already been used.
Loop through the player/coordinate pairs. If the pair is for a player and coordinate that hasn't been assigned, use that point, since it should be the shortest, then mark the player & coordinate as assigned.
As stated by others, this code would be clearer if you used more descriptive objects instead of arrays, but let's stick with what we have.
Here's an attempt at this:
var players = new List<int[]>() { new int[] { 0, 726, 1084, 0, 0, 5 }, new int[] { 2, 1481, 2208, 0, 0, -1 } };
List<int[]> coords = new List<int[]>();
for (int i = 1000; i <= 8000; i = i + 2300)
for (int j = 1000; j <= 15000; j = j + 2000)
coords.Add(new int[2] { j, i });
coords.RemoveAll(x => 3500 >= getDist(0, 0, x[0], x[1]));
coords.RemoveAll(x => 3500 >= getDist(16000, 9000, x[0], x[1]));
var closestPoints =
from playerIdx in Enumerable.Range(0, players.Count())
let player = players[playerIdx]
from coordIdx in Enumerable.Range(0, coords.Count())
let theseCoords = coords[coordIdx]
orderby getDist(theseCoords[0], theseCoords[1], player[1], player[2])
select new { playerIdx, coordIdx };
var playerCoords = new Dictionary<int, int>();
var usedPlayers = new HashSet<int>();
var usedCoords = new HashSet<int>();
foreach (var pairing in closestPoints)
{
if (!usedPlayers.Contains(pairing.playerIdx)
&& !usedCoords.Contains(pairing.coordIdx))
{
playerCoords[pairing.playerIdx] = pairing.coordIdx;
usedPlayers.Add(pairing.playerIdx);
usedCoords.Add(pairing.coordIdx);
}
}
// Result is in playerCoords as { 0: 0, 1: 6 }
If you have a large number of players or coordinates, this still isn't terribly efficient, since you're computing the distance for every single player/coordinate pairing. You may want to find a shortcut to avoid computing some of these. See Closest Pair: A Divide-and-Conquer approach for ideas there.

C# How to work-around identical keys in a Dictionary

This program add's values with their other values into a dictionary, all is fine until there are identical keys (var eat(.8) and var extra(.8) with different values. How do i ensure that i can use the right key every time even though they are similar? For example, var example = gigantDictionary[.8] (but i want var edmg value instead of '500' in the code?
var wqat = 1.1; //| index 0
var rat = .2; //| index 1
var eat = .8; //| index 2
var baat = 1.2; //| index 3
var extra = .8; //| index 4
var wqdmg = 120; //| index 0
var rdmg = 60; //| index 1
var edmg = 50; //| index 2
var badmg = 40; //| index 3
var extradmg = 500; //| index 4
List<double> theOneList = new List<double>();
List<double> damageList = new List<double>();
theOneList.Add(wqat);
theOneList.Add(rat);
theOneList.Add(eat);
theOneList.Add(baat);
damageList.Add(wqdmg);
damageList.Add(edmg);
damageList.Add(rdmg);
damageList.Add(badmg);
Dictionary<double, double> gigantDictionary = new Dictionary<double, double>();
for (int i = 0; i < theOneList.Count; i++)
{
gigantDictionary.Add(theOneList[i], damageList[i]);
gigantDictionary.Add(extra, 500); //this is the malignant similar key
}
theOneList.Sort((c, p) => -c.CompareTo(p)); //orders the list
List<double> finalList = new List<double>();
for (int i = 0; i < theOneList.Count; i++)
{
finalList.Add(gigantDictionary[theOneList[i]]); //grabs damage values and add's it to 'finalList'
Console.WriteLine(finalList[i]);
}
So ultimately, i want to order 'theOneList' by descent, in doing so i can get the damages from 'gigantDictionary' and put those into 'finalList', now i have an ordered damage list that i need, but since 2 keys are similar... this is holding me back.
*Edit: Could identical indexes be the key to this? be the bridge? for example, in index 0 i get 1.1 and 120, maybe the answer lies with the identical indexes, i want to get '120' damage from '1.1', notice both have index 0, this might work
They keys aren't "similar" they're "identical". If the keys were just "similar" then, as far as the dictionary is concerned, it's no different than being "completely different". From the point of view of a dictionary items are either equal, or not equal.
For example,
var example = gigantDictionary[.8]
(but i want var edmg value instead of '500' in the code?)
But how should the dictionary possibly know that? How would it know if you actually wanted to get 500?
Do you want to prevent the duplicate keys from being added, and instead always use the first value paired with every key? If so, just check if a key exists before adding a new one.
Do you want to just get all values associated with a key, if there are duplicates? Then have a dictionary where the value is a collection, and add all values associated with that one key to the collection.
Is there actually some way to distinguish the keys so that they're not actually identical? Then do that. With just a double (which is a very bad type to use as a key for a dictionary in the first place, as it's easy for floating point rounding errors to result in similar but different doubles that you consider equivalent) there's no good way to do this, but if your actual key could be different in such a way that distinguishes the two keys, then each could point to a unique value.
Right now you have two separate list for values that must go together. A better approach is to create a structure with the two values and keep a single list.
public class Thing
{
public string Name { get; set; }
public double TheOne { get; set; }
public double Dmg { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Thing> list=new List<Thing>() {
new Thing() { Name = "wq", TheOne = 1.1, Dmg=120 },
new Thing() { Name = "r", TheOne = 0.2, Dmg=60 },
new Thing() { Name = "e", TheOne = 0.8, Dmg=50 },
new Thing() { Name = "ba", TheOne = 1.2, Dmg=40 },
new Thing() { Name = "extra", TheOne = 0.8, Dmg=500 },
};
list.Sort((t1, t2) => t1.TheOne.CompareTo(t2.TheOne));
double[] dmg_list=list.Select((t) => t.Dmg).ToArray();
}
}
Edit 1
A constructor to Thing can be used to assign values with one operation.
public class Thing
{
// Constructor sets all the values
public Thing(string name, double theone, double dmg)
{
this.Name=name;
this.TheOne=theone;
this.Dmg=dmg;
}
public string Name { get; private set; }
public double TheOne { get; private set; }
public double Dmg { get; private set; }
}
class Program
{
static void Main(string[] args)
{
List<Thing> list=new List<Thing>();
list.Add(new Thing("wq", 1.1, 120));
list.Add(new Thing("r", 0.2, 60));
list.Add(new Thing("e", 0.8, 50));
list.Add(new Thing("ba", 1.2, 40));
list.Add(new Thing("extra", 0.8, 500));
list.Sort((t1, t2) => t1.TheOne.CompareTo(t2.TheOne));
double[] dmg_list=list.Select((t) => t.Dmg).ToArray();
}
}

Algorithm for detecting cycles finds far to few cycles

I have the following algorithm:
class CycleData : List<IntPoint>{
public IntPoint startPoint;
public Boolean ended=false;
public CycleData(IntPoint startpoint) { startPoint = startpoint; base.Add(startpoint); }
}
class GeoDataGraphPoint
{
private IntPoint point;
private List<GeoDataGraphPoint> connected = new List<GeoDataGraphPoint>();
private int generation=-9999;
public void AddConnection(GeoDataGraphPoint c)
{
connected.Add(c);
c.connected.Add(this);
}
public GeoDataGraphPoint(IntPoint point)
{
this.point = point;
}
public List<CycleData> GetCycles(int gen)
{
if (generation != -9999)
{
var r = new CycleData(point);
return new List<CycleData> { r };
}
generation = gen;
List<CycleData> res = new List<CycleData>();
foreach (GeoDataGraphPoint p in connected)
{
if (p.generation != gen-1)
{
res.AddRange(p.GetCycles(gen + 1));
}
}
foreach (CycleData list in res)
{
if (list.ended == false)
{
list.Add(point);
if (list.startPoint == this.point)
{
list.ended = false;
}
}
}
gen = -9999;
return res;
}
}
Now in principle this should return every cycle in the graph (for polygon detection). However it seems to fail to return anything in some occasions, I suspect that there is some kind of memory problem as removing parts of the graph sometimes causes new cycles to be found.
Here is a part of the input where it fails:
connection:(2282,3) to (2282,-192)
connection:(2282,3) to (2085,3)
connection:(2282,-192) to (2282,3)
connection:(2282,-192) to (2466,-192)
connection:(2466,-192) to (2282,-192)
connection:(2466,-192) to (2466,581)
connection:(2466,581) to (2466,-192)
connection:(2466,581) to (1494,581)
connection:(1494,581) to (2466,581)
connection:(1494,581) to (1494,397)
connection:(1494,397) to (1494,581)
connection:(1494,397) to (2282,397)
connection:(2282,397) to (1494,397)
connection:(2282,397) to (2282,187)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,3) to (2085,187)
connection:(2085,3) to (2282,3)
connection:(2085,187) to (2282,187)
connection:(2085,187) to (2085,3)
connection:(2282,187) to (2282,397)
connection:(2282,187) to (2085,187)
connection:(2282,397) to (1494,397)
The above code is for two triangles arranged to form a square (in coordinates) where both sides touch one another so like this:
Where I can the function as following:
class GeoDataGraph : Dictionary<IntPoint, GeoDataGraphPoint>
{
public void resetGens()
{
foreach(var v in base.Values)
{
v.generation = -9999;
}
}
public static Island GetHolesInIsland(Island input)
{
GeoDataGraph graph = new GeoDataGraph();
for (int i = 0; i < input.area.Count-1; i = i + 2)
{
var p1 = new IntPoint(input.area[i].X, input.area[i].Y);
var p2 = new IntPoint(input.area[i + 1].X, input.area[i + 1].Y);
if (!graph.ContainsKey(p1)) graph.Add(p1, new GeoDataGraphPoint(p1));
if (!graph.ContainsKey(p2)) graph.Add(p2, new GeoDataGraphPoint(p2));
graph[p1].AddConnection(graph[p2]);
}
IntPoint min = new IntPoint(int.MaxValue, int.MaxValue);
List<IntPoint> minCycle = null;
List<List<IntPoint>> cycles = new List<List<IntPoint>>();
while (graph.Count != 0)
{
var first = graph.First();
var NewCycles = first.Value.GetCycles(1);
graph.resetGens();
if (NewCycles.Count == 0)
{
graph.Remove(first.Key);
Console.WriteLine("point" + first.Key + "is uncycled");
}
cycles.AddRange(NewCycles);
foreach (var cycle in NewCycles)
{
foreach (var cycleNode in cycle)
{
graph.Remove(cycleNode);
if (min.X > cycleNode.X || min.Y > cycleNode.Y)
{
minCycle = cycle;
min = cycleNode;
}
}
}
}
cycles.Remove(minCycle);
if (minCycle == null) { minCycle = new List<IntPoint>();
foreach(IntPoint a in input.area) {
Console.Write(a);
} }
input.holes = cycles;
input.area = minCycle;
return input;
}
}
}
where Island contains.area contains a list of points ordered by connected pairs.
The basic algorithm is simple: take a node recursively visit every node connected to it until you detect a cycle, then return that node and append any node on the way out until you find the start of the cycle again. Once you have found every node connected to the start node remove the cycles (they since we checked every node connected to the cycle we shouldn't be deleting the once we already did) and start again on the next node, if a node contains no cycles remove it. I suspect something might be going wrong at this step but am unsure.
Any idea what I'm doing wrong that causes a weird interdependence that causes seemingly unrelated polygons to go wrong?
I think that one problem is the way you ignore connected nodes using p.generation != gen-1. As you use depth first search you will tag all the nodes until the most depth and when backtracking I think it can miss some nodes or explore nodes twice.
As a general advise I can say: Don't reinvent the wheel yourself but use a known algorithm.
First ask yourself what you want to do. The number of cycles can be exponential. So the question is if you really need all the cycles.
If the answer is yes and you want to find all the cycles in a undirected graph you can get more information here.
If you don't really need all the cyles maybe what your are looking for is to find strongly connected components.

Get the inner points which are on a straight line

The question I'm about to ask might seem a Geometry question at the first glance, but it actually can be solved by using LINQ, at least I hope so!
I have 5 points on a straight line two of them are at the ends of the line. How can I select the ones that are in the interior of the line (not at the end points) using LINQ?
public class Point
{
public double X;
public double Y;
}
var listOfPointsOnALine = new List<Point>
{
new Point { X = 2500, Y = 50 },
new Point { X = 2540, Y = 112.5 },
new Point { X = 2580, Y = 175 },
new Point { X = 2620, Y = 237.5 },
new Point { X = 2660, Y = 300 },
}
So using some LINQ on the list above should give me the list below:
innerPointsOnALine: {(2540, 112.5), (2580, 175), (2620, 237.5)}
The points are ordered in the original list
The points should be in the same order they appear in the original list (listOfPointsOnALine)
If I understand it correctly then I think you are looking for:
var newList = listOfPointsOnALine
.Skip(1)
.Take(listOfPointsOnALine.Count - 2)
.ToList();
You may have to check for length of list before doing this.

How do I convert an array of structs into an array of Point3D objects in C#?

I intend to form 3D mesh object.
The mesh object has an 3D point array of approx. 50.000 items.
Due to the number of 3D points, the array must be initialized on the heap.
The required code is, shortly, as follows:
class MyMesh
{
public MeshGeometry3D Mesh3D // Properties tanimlaniyor
{
get { return GetMesh3D(); }
}
public struct mystruct
{
public int m_i;
public int m_j;
public int m_k;
public mystruct(int i, int j, int k)
{
m_i = i;
m_j = j;
m_i = k;
}
}
private mystruct[] mypts =
{
new mystruct(20 , 7 , 7),
.
.
new mystruct(23 , 5 , 7)
};
}
Could you explain me how 3D Coordinates in mystruct above can be converted
into 3D coordinates of a System.Windows.Media.Media3D.Point3D structure.
Thanks in advance.
Öner YILMAZ
If you have an actual list of 50,000 mystruct objects, would it be better to just create them as Point3D structs in the first place?
Simply do a "Find & Replace" of:
new mystruct(
and replace it with
new Point3D(
Then, change:
private mystruct[] mypts =
to:
private Point3D[] mypts =
Are you looking for something like this...
List<Point3D> points = mypts.Select<mystruct, Point3D> (x =>
new Point3D(x.m_i, x.m_j, x.m_k))
.ToList();
Alternatively, you could expose an iterator that returned an IEnumerable like this...
public IEnumerable<Point3D> Points()
{
foreach(var point in mypts)
{
yield return new Point3D(point.m_i, point.m_j, point.m_k, );
}
}
[add validation/error handling code as appropriate]
If you need to retain your mystruct objects as well as enable Point3D functionality, you could use something like:
class MyMesh {
...
public Point3D[] ToPoint3D()
{
Point3D[] p3D = null; // or set it to an Empty Point3D array, if necessary
if (mpts.Length > 0)
{
p3D = new Point3D[mpts.Length]
for (int x = 0; x < mypts.Length; x++)
{
p3D[x].X = new Point3D(mypts[x].m_i;
p3D[x].Y = new Point3D(mypts[x].m_j;
p3D[x].Z = new Point3D(mypts[x].m_k;
}
}
return p3D;
}
...
}

Categories