Recursive nested loop c# - c#

I am trying to improve myself on probabilities with a task but couldn't figure it out:
On a backgammon game, I have four (dynamic) rows of placements.
So lets say row 5,7,11,13. I am throwing dynamic number of dice. I am trying to find the probabilities of each gameplay. As an example:
I have thrown 4 dice and result is 1,3,4,6
So
I can play from the 5. row-1,3,4,6 dice
I can play from the 5. row-1,3,4 dice and from the 7. row-6 dice
I can play from the 5. row-3,1 dice and from the 11. row-6 dice and from the 13. row-4 dice
etc etc.
The dice have to be dynamic, the rows have to be dynamic, the dice can be played mixed like 1,3,4,6 or 6,1,4,3 or 3,1,6,4. And it has to calculate all the possibilities of different variations of dice spread on the rows.
Simply I am trying to calculate the possibilities of play on a backgammon with unlimited dice. The method I am trying to use it to have a resizable possible moves class and inside of this class, there are the resizable rows class. Inside the rows class, there is resizable List moves variable. I am adding one move for each variation. This is the codes for non dynamic 4 dice.
public List<int> dice;
public List<int> places;
[System.Serializable]
public class arrayData
{
public List<arrayData2> places = new List<arrayData2>();
}
public List<arrayData> pos = new List<arrayData>();
[System.Serializable]
public class arrayData2
{
public List<int> moves = new List<int> {};
}
void Start()
{
vs();
}
void vs()
{
for (int i = 0; i < places.Count; i++)
{
for (int a = 0; a < dice.Count; a++)
{
for (int b = 0; b < dice.Count; b++)
{
for (int c = 0; c < dice.Count; c++)
{
for (int d = 0; d < dice.Count; d++)
{
if (a == b || a == c || a == d || b == c || b == d || c == d)
{
continue;
}
pos.Add(new arrayData());
for (int s = 0; s < places.Count; s++)
{
pos[pos.Count - 1].places.Add(new arrayData2());
}
pos[pos.Count - 1].places[i].moves.Add(dice[a]);
pos[pos.Count - 1].places[i].moves.Add(dice[b]);
pos[pos.Count - 1].places[i].moves.Add(dice[c]);
pos[pos.Count - 1].places[i].moves.Add(dice[d]);
}
}
}
}
}
}
What I couldn't figure out:
-Firstly, I tried to make it a recursive loop but I couldn't figure out the structure
-Second, the output is giving me missing values for the variation of spread between rows: It gives such values: 5. row-1,3,4,6 dices but not such values: 5. row-3,1 dices and from the 11. row-6 dice and from the 13. row-4 dice
I know it is a big question to ask but any kind of pointing out the mistake or correct direction leading would be so appreciated.
Thanks

It might be useful to think about your moves as part of a game tree. The basic idea is that all games start in a particular state. We'll call it x. A move then changes the state of the game to x1, the next move to x2, and so on. Most moves involve a choice and, depending on the choice made, each move pushes the game into a different state. From the starting state, all possible moves form a gigantic tree. This is handy because we can use tree traversal algorithms to explore our tree. That's where the recursion comes in.
Backgammon is conceptually a little trickier than a game like chess or checkers because you have to account for the randomness of the dice as well as the behavior of your opponent. That adds even more choices during a move. In pseudo-code, I might tackle the problem like this:
function get_next_move (current_state):
dice = roll_dice()
possible_states = an empty collection
get_possible_states (dice, current_state, possible_states)
return decide_best_state (possible_states)
function get_possible_states (dice, state, possible_states):
// If we've exhausted all dice, we're done.
// Add the state we've reached to our possible outcomes.
if dice are empty:
add state to possible_states
return
for each die in dice:
dice_without_die = remove die from dice
for each next_state in get_legal_states (state, die):
// Here's the recursion. Go a level deeper in our tree.
get_possible_states (dice_without_die , next_state, possible_states)
function get_legal_states (state, die):
// Here's where you determine legal moves based on the
// die rolled and the current state of the game.
Put another way, to determine my next move I have to:
Roll the dice.
With each die rolled, generate all of its legal moves (states).
Repeat the process using a new state and without the die already used.
When there are no dice left, add the final state to the list of possible states.
Finally, determine which move is best. Keep in mind that some generated states may be duplicates. There's more than one way to move your pieces to the same locations.
The nice thing about this approach is there are no hard-coded numbers. It can handle any number of legal moves or dice. (Though I'm not sure when Backgammon would use a variable number of dice.) One detail I left out is that you need to know who's turn it is. The legal moves will depend on who's making them.

Related

How to write in multiple positions in a console application at the same time? C#

I want lines as many as the width of the console to simultaneously write downwards one char to the height of the console. I've done most of it, but it goes from top to bottom to right etc...
If you need help picturing what I mean, think of the matrix code rain.
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < w)
{
int j = 0;
while (j < h)
{
Thread.Sleep(1);
Console.SetCursorPosition(i, j);
Console.Write(".");
j++;
}
i++;
}
What I would do is construct a List<string> lines; that would contain the lines you want to write to the console window, where each line is as wide as the console width. Then just print the list out to the console window in reverse order, so the first line (at lines[0]) will always be the last one printed, and will always be at the bottom of the console window.
Sample Implementation -- someone mentioned this might be homework. I did not think so, but if it is, then please try your own implementation of the above idea first.
We can add new items to the list in the same loop that we use to print out its items. Before we add a line, however, we first check to see if there are already as many lines in the list as there are in the console window (Console.WindowHeight). If there are, then we just remove the line at lines[0] before we add a new one. In this way, the List<string> lines is "scrolling" along with the console window.
The scrolling speed is controlled by a Thread.Sleep, but this code could easily be added to a Timer instead, so that other work could happen in the background (like if this was intended to be a "screensaver", and you wanted to wait for user input to "wake up"). But no matter how we decide to implement the speed, I decided to create an enum with values that represent the number of milliseconds a Thread.Sleep implementation would use:
class Program
{
enum MatrixCodeSpeed
{
Fastest = 0,
Faster = 33,
Fast = 67,
Normal = 100,
Slow = 333,
Slower = 667,
Slowest = 1000
}
I would also create a helper method that creates a "random" line for you. It could take in an integer that specifies the "density", which means how many characters you'd want in the line. density represents a percentage, so if 10 is specified, then we pick a random number between 0 and 99, and if it's less than 10 then we add a random matrix character to the string (otherwise we add a space character).
Also, in order to replicate the matrix a little closer, I've also chosen 4 different characters to print, each one slightly darker than the previous. This adds to the three dimensional effect, where the faded blocks look further away than the solid ones:
private static Random rnd = new Random();
// Add whatever 'matrix' characters you want to this array. If you prefer to have one
// character chosen more often than the others, you can write code to favor a specific
// index, or just add more instances of that character to the array below:
private static char[] matrixChars = new[] { '░', '▒', '▓', '█' };
static string GetMatrixLine(int density)
{
var line = new StringBuilder();
for (int i = 0; i < Console.WindowWidth; i++)
{
// Choose a random number from 0-99 and see if it's greater than density
line.Append(rnd.Next(100) > density
? ' ' // If it is, add a space to reduce line density
: matrixChars[rnd.Next(matrixChars.Length)]); // Pick a random character
}
return line.ToString();
}
Next, we have the main method, which populates a list with random lines (using a density of 10%), then prints them out one at a time, in reverse order, in an endless loop (removing the first line if we need to):
static void Main()
{
var lines = new List<string>();
var density = 10; // (10% of each line will be a matrix character)
var speed = MatrixCodeSpeed.Normal;
// Hide the cursor - set this to 'true' again before accepting user input
Console.CursorVisible = false;
Console.ForegroundColor = ConsoleColor.DarkGreen;
while (true)
{
// Once the lines count is greater than the window height,
// remove the first item, so that the list "scrolls" also
if (lines.Count >= Console.WindowHeight)
{
lines.Remove(lines[0]);
}
// Add a new random line to the list, which will be the new topmost line.
lines.Add(GetMatrixLine(density));
Console.SetCursorPosition(0, 0);
// Print the lines out to the console in reverse order so the
// first line is always last, or on the bottom of the window
for (int i = lines.Count - 1; i >= 0; i--)
{
Console.Write(lines[i]);
}
Thread.Sleep(TimeSpan.FromMilliseconds((int)speed));
}
}
}
Here's a gif of it in action, up to the point where the screen is full (then the gif repeats, but the code version continues to scroll normally):
The task smells like an assignment, so I'm guiding you instead feeding the implementation. It is not ethical to feed you with an answer if it is a homework.
You are looking for a better fit of algorithm. The stated algorithm fill the console from top to bottom, as it iterate to fill through the Y-axis first (the nested loop) and followed by the X-axis (the outer loop).
What is needed is to iterate x-axis and y-axis alternatively so that it looks like it fills from the top left corner to the bottom right corner.
// 1 step to (0,0)
*
// 3 steps to (1,1)
**
**
// 5 steps for reaching (2,2)
***
***
***
// 7 steps for reaching (3,3)
****
****
****
// 9 steps for reaching (4,4) and 11 steps for (5,5)...
// I do think everyone could get this pattern
This draft would also be the final outcome of what it looks like.
Instead of filling them all at the same time, what you need is actually get the thread sleep after it reach the next square point.
(Computers are so fast that it probably do all its work to feed your screen within a second and the black console window is gone without any notice.)
At the time you posted the question, I'm also solving it from the very beginning. I thought of filling X and Y axis alternatively is the solution, but stopping at each time that the square expands is far more important to get the effect.
It is not a threading problem tag either at my point of view.
Let's sum up the above pattern:
Assume i and j are x and y coordinates respectively.
Each iteration takes you from (i, j) and n*2+1 steps to reach
(i+1,j+1)
Note that we are zero-based in this example.
We are about to construct the loop:
The n*2+1 step number is useful. It means you need to fill x-axis for
n times and y-axis for n times, and finally get the diagonal grid
(n+1,n+1) done.
In each inner loop, we first render the X frontier along y-axis and
then render the Y frontier along x-axis.
Let say the cycle start with the checkpoint (n,n), where n=3, and we
slept for a while, so we are in n=4 now.
To achieve this, we'd better first navigate to (n+1,0) then fill up
to (n+1,n)
Afterwards we navigate to (0,n+1) and fill to (n+1,n+1)
Then we are in m=n+1 now (sounds like a mathematical proving :(
The loop would be
//calculate how many checkpoints (n)
int checkpoints = 1080;
//n should indicate the actual turn we are instead of naming the total turns like sucks
//The main, the outermost For-loop
for (int n=0;n<checkpoints;n++)
{
// The nested step
for (int y=0;y<n;y++)
{
// Just fill in (n+1, y) grid
Console.SetCursorPosition(n+1, y);
Console.Write(".");
}
for (int x=0;x<n+1;x++)
{
// Just fill in (x, n+1) grid
Console.SetCursorPosition(x, n+1);
Console.Write(".");
}
// Upon completion of each main cycle we have a sleep, yah
Thread.Sleep(100);
}
Well, I expect the program to crash when the console size is smaller than 1080x1080.
This algorithm could only get you a square to fill, and a typical monitor with resolution 1920x1080 just fails as it is 16:9. This is intentional, if you're doing homework you need to configure it before shipping it to your teacher. (I've got no chance to do an assignment as I self learned programming :(
(The site continuously urging me to format my code, this has been half an hour and I just didn't do things wrong. So I decided to post it bit by bit to debug that. Finally I've got the job done...)
If you just want to write one line at a time you can use this:
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < h)
{
Console.WriteLine(new string('.', w-1));
Thread.Sleep(20);
i++;
}
Just a bit of modification allow the code to simulate the matrix code rain.
int w = Console.WindowWidth;
int h = Console.WindowHeight;
int i = 0;
while (i < h)
{
int j = 0;
string s = "";
Thread.Sleep(10);
while (j < w)
{
Console.SetCursorPosition(j, i);
s += ".";
j++;
}
Console.Write(s);
i++;
}
basically what i did here is just some restructuring of the logic and putting in the proper delays at the right position. Hope it helps.

How to iterate backwards (with pauses for input) through a set of randomly generated numbers?

// EDIT:
This is not a duplicate to: "When should I use a List vs a LinkedList". Check the answer I've provided below.
LinkedList might be useful though if someone wants to insert some positions at a specific place after we have a properly ordered List (in that case - LinkedList) already - how to make one, check my answer below. //
How would you iterate backwards (with pauses for player input) through a set of randomly generated numbers?
I'm trying to build a turn-based game. The order of actions is determined by a result of something like that:
int randomPosition = Random.Range(1,31) + someModifier;
// please note that someModifier can be a negative number!
// There is no foreseeable min or max someModifier.
// Let's assume we can't set limits.
I have a List of KeyValue pairs already containing names of objects and their corresponding randomPosition. // Update: Values of it are already sorted by a custom Sort() function, from highest to lowest.
// A list of KeyValue pairs containing names of objects and their corresponding randomPosition.
public List<KeyValuePair<string, int>> turnOrder = new List<KeyValuePair<string, int>> ();
// GameObject names are taken from a list of player and AI GameObjects.
List <GameObject> listOfCombatants = new List<GameObjects>();
// Each GameObject name is taken from listOfCombatants list.
listOfCombatants[i].name;
I thought, maybe let's add player and AI GameObjects to a list on Index positions equal to each of their randomPosition. Unfortunately, a Generic List can't have "gaps" in it. So we can't create it, let alone iterate it backwards.
Also, I'm not sure how we'd stop a for loop to wait for player input. I have a button, pressing which will perfom an action - switch state and run some functions.
Button combat_action_button;
combat_action_button.onClick.AddListener (AttackButton);
// When player takes his turn, so in TurnState.PLAYER_ACTION:
public void AttackButton() {
switch(actionState) {
case PlayerAction.ATTACK:
Debug.Log (actionState);
// Do something here - run function, etc. Then...
currentState = TurnState.ENEMY_ACTION;
break;
}
To make things worse, I've read that "pausing" a while loop isn't good for performance. That it's better to take player input out of loops.
So maybe I should create more for loops, iterate a new loop from position to position until the last GameObject acted, and use some delegates/events, as some things players/AI can do are in different scripts (classes).
This is not a real-time project, so we can't base anything on time (other than potential max turn time).
Another thing is, we don't know how many GameObjects will take turns.
But maybe there's some collection type that can store GameObjects with gaps between their index positions and iterate a loop from highest to lowest with no problem?...
I want to make it as simple as possible.
For the issue of simultaneously having user input and looping, I recommend looking into background workers or threading/tasks. This should facilitate your problem with simultaneously doing two things at once.
For your list problem I personally prefer the lists as well which is why I would designate each "gap" with a specific character like - 1, and when referencing the data ignore the - 1. To ignore the gaps I recommend LINQ queries but if you don't want to use LINQ that should not be a problem.
EDIT*
I know very little about Unity but from what people have been saying it sounds like running multiple threads is or can be an issue. I looked into this issue and it sounds like you just cannot call the unity api from a background thread. So basically, as long as you do not reference the unity api from the background thread, you should be ok. With that said, you may possibly need/want to make a call to the api inside of the background worker. To do this you need to either invoke the call before or after the background worker thread. I am pretty sure there is also a simultaneous invocation from the main thread by setting the workers step property to true.
I've decided to share my own solutions as I think I've developped some accurate answers to my questions.
Solution #1. First, declare an array, 2 variables and 1 function:
int[] arrayOrderedCombatants;
int min = 100000;
int max;
public void SomePlayerAction() {
Debug.Log ("This is some player or AI action.");
}
public void IterateThroughTurnOrderPositions() {
for (int i=max; i >= min; i--) {
if (arrayOrderedCombatants [i] >= 0 && arrayOrderedCombatants [i] >= min) {
Debug.Log ("This is an existing position in turn order: " + arrayOrderedCombatants [i]);
foreach (var position in turnOrder) {
if (position.Value == arrayOrderedCombatants [i]) {
max = (arrayOrderedCombatants [i] - 1);
goto ExitLoop;
}
}
}
}
ExitLoop:
SomePlayerAction ();
}
Then, for our testing purposes let's use an Update() method triggered by Input.GetKeyDown:
if (Input.GetKeyDown (KeyCode.O)) {
arrayOrderedCombatants = new int[turnOrder[0].Value + 1];
Debug.Log ("This is arrayOrderedCombatants Length: " + arrayOrderedCombatants.Length);
foreach (var number in turnOrder) {
if (number.Value < min)
min = number.Value;
}
Debug.Log ("This is min result in random combat order: " + min);
for (int i=0; i < arrayOrderedCombatants.Length; i++)
arrayOrderedCombatants[i] = min -1;
foreach (var combatant in turnOrder) {
if (combatant.Value >= 0) {
arrayOrderedCombatants [combatant.Value] = combatant.Value;
}
}
max = turnOrder [0].Value;
while (max >= min)
IterateThroughTurnOrderPositions ();
}
The above code answers my question. Unfortunately, problems with this solution may be two. First - you can't have a negative index position. So if someModifier makes randomPosition go below 0, it won't work. Second problem - if you have more than 1 occurance of any value from randomPosition, it will be added to the arrayOrderedCombatants only once. So it will be iterated once too.
But that's obvious - you can't have more than one value occupying an int type Arrays' index position.
So I will provide you a better solution. It's a different approach, but works like it should.
Solution #2. First, declare a list of GameObjects:
List<GameObject> orderedCombatants = new List<GameObject> ();
Then, in Update() method:
if (Input.GetKeyDown (KeyCode.I)) {
orderedCombatants.Clear ();
foreach (var combatant in initiative) {
Debug.Log (combatant);
for (int i=0; i < listOfCombatants.Count; i++) {
if (listOfCombatants[i].name.Contains(combatant.Key)) {
orderedCombatants.Add(listOfCombatants[i]);
}
}
}
foreach (var combatant in orderedCombatants) {
Debug.Log (combatant.name);
}
}
The above creates a new list of GameObjects already set in the right order. Now you can iterate through it, easily access each GameObject and perform any actions you need.

Increment through a list on a button list

I've stored a list of colors in my program. I am after an object in my scene to one of the colors in the list. So far, I have done the followings:
if(Input.GetKeyDown(KeyCode.O))
{
for(int i = 0; i < claddingColor.Count; i++)
{
claddingMaterial.color = claddingColor[i];
}
}
This isn't working due to a reason I know (and you can probably spot) but I lack to the verbal fortitude to write it down.
As opposed to have a multiple lines of the following:
claddingMaterial.color = claddingColor[0];
Each tied to different buttons, I like a way I can emulate the above but tie it to a single button press. Thus, if I hit the 0 button 5 times, it will loop through each color stored in the list. If I hit it for a sixth time, it will go back to the first color in the list.
Could someone please help me implement this? Or point me to something that I may learn how to do it for myself?
Define LastColor property as class member:
int LastColor;
In your function use modulo
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[(LastColor++) % claddingColor.Count];
}
Note: Depending on the type of claddingColor use Count for a List or Length for Array.
You won't need a for loop
int lastStep = 0;
if(Input.GetKeyDown(KeyCode.O))
{
claddingMaterial.color = claddingColor[lastStep++];
if (lastStep == claddingColor.Count)
lastStep = 0;
}

How do i get a character in a given coordinate in the C# console?

I'm trying to make a roguelike in C#, and i got to make a little map, and a loop that checks if w/s/a/d is pressed to move the player; but i have a problem detecting walls; let's say i want to know if there is a wall to the right before moving there, i get the player position, i check what is on the right of that position, and if it's a '#', i don't let the player move because it's supposed to be a wall.
But here's the problem, in the console, how do i check if the coordinate, for instance "(15,2)" contains a '#' char?
Is there an easier way to check if a given coordinate contains that character?
And how? Because i tried, but i can't GET the char in a certain coord of the console.
Something like:
static bool wallcheck_x(int xpos) {
xpos++;
//Now, it should GET the char allocated in xpos++;
if (/*char that's in xpos++ */ == '#')
{
return true; //it's a wall
}
return false; //it's not a wall
}
You shouldn't be doing it that way. You should have something that maps to what is displayed in the console, I assume that's how you even see the map to begin with. When a player moves, you should check it based on your map, not what is displayed in the console.
So for example...
how do i check if the coordinate, for instance "(15,2)" contains a '#' char?
Don't check what's in the console at (15,2), but check your map that displays what's in the console for a # instead. It's much easier that way.
But, let's say i did the map in a multidimensional array... then how do i print the array in the console?
Use Google, man. Here's what I pulled from this question:
int rowLength = arr.GetLength(0);
int colLength = arr.GetLength(1);
for (int i = 0; i < rowLength; i++)
{
for (int j = 0; j < colLength; j++)
{
Console.Write(string.Format("{0} ", arr[i, j]));
}
Console.Write(Environment.NewLine + Environment.NewLine);
}
What this does is traverses the array by row, printing each character in the row. When it reaches the end of a row, it prints a new line and begins printing the new row. Your array would probably be a 2-dimensional character array which represents the map. Likewise, you can keep track of a player's position with something like:
int playerXPos;
int playerYPos;
You would need to update those as your character moves. So, credit to gleng for this snippet, you can check if the player has hit a wall:
if (arr[playerXPos, playerYPos] == '#')
{
// player has collided with a wall
}
else
{
// player has NOT collided with a wall
}
Though it would be more scalable to have an actual Player data structure, this seems like it would suit you just fine.
Without having access to any of your code (you should really post it for help like this), i'm assuming you would do something like:
if (map[player.X, player.Y] == '#')
{
// player has collided with a wall
}
else
{
// player has NOT collided with a wall
}
This code assumes that map is a multidimensional array that contains your map.

calculate average function of several functions

I have several ordered List of X/Y Pairs and I want to calculate a ordered List of X/Y Pairs representing the average of these Lists.
All these Lists (including the "average list") will then be drawn onto a chart (see example picture below).
I have several problems:
The different lists don't have the same amount of values
The X and Y values can increase and decrease and increase (and so on) (see example picture below)
I need to implement this in C#, altought I guess that's not really important for the algorithm itself.
Sorry, that I can't explain my problem in a more formal or mathematical way.
EDIT: I replaced the term "function" with "List of X/Y Pairs" which is less confusing.
I would use the method Justin proposes, with one adjustment. He suggests using a mappingtable with fractional indices, though I would suggest integer indices. This might sound a little mathematical, but it's no shame to have to read the following twice(I'd have to too). Suppose the point at index i in a list of pairs A has searched for the closest points in another list B, and that closest point is at index j. To find the closest point in B to A[i+1] you should only consider points in B with an index equal to or larger than j. It will probably by j + 1, but could be j or j + 2, j + 3 etc, but never below j. Even if the point closest to A[i+1] has an index smaller than j, you still shouldn't use that point to interpolate with, since that would result in an unexpected average and graph. I'll take a moment now to create some sample code for you. I hope you see that this optimalization makes sense.
EDIT: While implementing this, I realised that j is not only bounded from below(by the method described above), but also bounded from above. When you try the distance from A[i+1] to B[j], B[j+1], B[j+2] etc, you can stop comparing when the distance A[i+1] to B[j+...] stops decreasing. There's no point in searching further in B. The same reasoning applies as when j was bounded from below: even if some point elsewhere in B would be closer, that's probably not the point you want to interpolate with. Doing so would result in an unexpected graph, probably less smooth than you'd expect. And an extra bonus of this second bound is the improved performance. I've created the following code:
IEnumerable<Tuple<double, double>> Average(List<Tuple<double, double>> A, List<Tuple<double, double>> B)
{
if (A == null || B == null || A.Any(p => p == null) || B.Any(p => p == null)) throw new ArgumentException();
Func<double, double> square = d => d * d;//squares its argument
Func<int, int, double> euclidianDistance = (a, b) => Math.Sqrt(square(A[a].Item1 - B[b].Item1) + square(A[a].Item2 - B[b].Item2));//computes the distance from A[first argument] to B[second argument]
int previousIndexInB = 0;
for (int i = 0; i < A.Count; i++)
{
double distance = euclidianDistance(i, previousIndexInB);//distance between A[i] and B[j - 1], initially
for (int j = previousIndexInB + 1; j < B.Count; j++)
{
var distance2 = euclidianDistance(i, j);//distance between A[i] and B[j]
if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
{
distance = distance2;
previousIndexInB = j;
}
else
{
break;//don't place the yield return statement here, because that could go wrong at the end of B.
}
}
yield return LinearInterpolation(A[i], B[previousIndexInB]);
}
}
Tuple<double, double> LinearInterpolation(Tuple<double, double> a, Tuple<double, double> b)
{
return new Tuple<double, double>((a.Item1 + b.Item1) / 2, (a.Item2 + b.Item2) / 2);
}
For your information, the function Average returns the same amount of interpolated points the list A contains, which is probably fine, but you should think about this for your specific application. I've added some comments in it to clarify some details, and I've described all aspects of this code in the text above. I hope it's clear, and otherwise feel free to ask questions.
SECOND EDIT: I misread and thought you had only two lists of points. I have created a generalised function of that above accepting multiple lists. It still uses only those principles explained above.
IEnumerable<Tuple<double, double>> Average(List<List<Tuple<double, double>>> data)
{
if (data == null || data.Count < 2 || data.Any(list => list == null || list.Any(p => p == null))) throw new ArgumentException();
Func<double, double> square = d => d * d;
Func<Tuple<double, double>, Tuple<double, double>, double> euclidianDistance = (a, b) => Math.Sqrt(square(a.Item1 - b.Item1) + square(a.Item2 - b.Item2));
var firstList = data[0];
for (int i = 0; i < firstList.Count; i++)
{
int[] previousIndices = new int[data.Count];//the indices of points which are closest to the previous point firstList[i - 1].
//(or zero if i == 0). This is kept track of per list, except the first list.
var closests = new Tuple<double, double>[data.Count];//an array of points used for caching, of which the average will be yielded.
closests[0] = firstList[i];
for (int listIndex = 1; listIndex < data.Count; listIndex++)
{
var list = data[listIndex];
double distance = euclidianDistance(firstList[i], list[previousIndices[listIndex]]);
for (int j = previousIndices[listIndex] + 1; j < list.Count; j++)
{
var distance2 = euclidianDistance(firstList[i], list[j]);
if (distance2 < distance)//if it's closer than the previously checked point, keep searching. Otherwise stop the search and return an interpolated point.
{
distance = distance2;
previousIndices[listIndex] = j;
}
else
{
break;
}
}
closests[listIndex] = list[previousIndices[listIndex]];
}
yield return new Tuple<double, double>(closests.Select(p => p.Item1).Average(), closests.Select(p => p.Item2).Average());
}
}
Actually that I did the specific case for 2 lists separately might have been a good thing: it is easily explained and offers a step before understanding the generalised version. Furthermore, the square root could be taken out, since it doesn't change the order of the distances when sorted, just the lengths.
THIRD EDIT: In the comments it became clear there might be a bug. I think there are none, aside from the mentioned small bug, which shouldn't make any difference except for at the end of the graphs. As a proof that it actually works, this is the result of it(the dotted line is the average):
I'll use a metaphor of your functions being cars racing down a curvy racetrack, where you want to extract the center-line of the track given the cars' positions. Each car's position can be described as a function of time:
p1(t) = (x1(t), y1(t))
p2(t) = (x2(t), y2(t))
p3(t) = (x3(t), y3(t))
The crucial problem is that the cars are racing at different speeds, which means that p1(10) could be twice as far down the race track as p2(10). If you took a naive average of these two points, and there was a sharp curve in the track between the cars, the average may be far from the track.
If you could just transform your functions to no longer be a function of time, but a function of the distance along the track, then you would be able to do what you want.
One way you could do this would be to choose the slowest car (i.e., the one with the greatest number of samples). Then, for each sample of the slowest car's position, look at all of the other cars' paths, find the two closest points, and choose the point on the interpolated line which is closest to the slowest car's position. Then average these points together. Once you do this for all of the slow car's samples, you have an average path.
I'm assuming that all of the cars start and end in roughly the same places; if any of the cars just race a small portion of the track, you will need to add some more logic to detect that.
A possible improvement (for both performance and accuracy), is to keep track of the most recent sample you are using for each car and the speed of each car (the relative sampling rate). For your slowest car, it would be a simple map: 1 => 1, 2 => 2, 3 => 3, ... For the other cars, though, it could be more like: 1 => 0.3, 2 => 0.7, 3 => 1.6 (fractional values are due to interpolation). The speed would be the inverse of the change in sample number (e.g., the slow car would have speed 1, and the other car would have speed 1/(1.6-0.7)=1.11). You could then ensure that you don't accidentally backtrack on any of the cars. You could also improve the calculation speed because you don't have to search through the whole set of all points on each path; instead, you can assume that the next sample will be somewhere close to the current sample plus 1/speed.
As these are not y=f(x) functions, are they perhaps something like (x,y)=f(t)?
If so, you could interpolate along t, and calculate avg(x) and avg(y) for each t.
EDIT This of course assumes that t can be made available to your code - so that you have an ordered list of T/X/Y triples.
There are several ways this can be done. One is to combine all of your data into one single set of points, and do a best-fit curve through the combined set.
you have e.g. 2 "functions" with
fc1 = { {1,0.3} {2, 0.5} {3, 0.1} }
fc1 = { {1,0.1} {2, 0.8} {3, 0.4} }
You want the arithmetic mean (slang: "average") of the two functions. To do this you just calculate the pointwise arithmetic mean:
fc3 = { {1, (0.3+0.1)/2} ... }
Optimization:
If you have large numbers of points you should first convert your "ordered List of X/Y Pairs" into a Matrix OR at least store the points column-wise like so:
{0.3, 0.1}, {0.5, 0.8}, {0.1, 0.4}

Categories