I need an efficient algorithm for best matching - c#

I have, for instance, 10 teams and I need to match them.
The criteria for matching are:
Two teams are matched only one time.
Teams with closest score must be matched (Score is property of Team class, type is double).
It could be iterated 9 (n-1) times max and this is what I try to do, go further as I can.
At the end of each iteration, points of teams increase randomly.
I populated my list with random data.
List<Team> _teams = new List<Team>();
for (int i = 1; i <= 10; i++)
{
_teams.Add(new Team("Team "+i,MyExtensions.RandomNumber(31)));
}
private static readonly Random Rand = new Random();
public static int RandomNumber(int max)
{
return Rand.Next(max);
}
My team class:
public class Team
{
private string _name;
private double _score;
private List<Team> _matchedTeams;
private Team _currentMatch;
public Team CurrentMatch
{
get { return _currentMatch; }
set { _currentMatch = value; }
}
public List<Team> MatchedList
{
get { return _matchedTeams; }
set { _matchedTeams = value; }
}
public string Name
{
get { return _name; }
set { _name = value; }
}
public double Score
{
get { return _score; }
set { _score = value; }
}
public Team(string name, double score)
{
_matchedTeams = new List<Team>();
_name = name;
_score = score;
}
public override string ToString()
{
return _name + " - (" + _score + ")";
}
}
And here's my extension method to get closest match;
public static Team FindClosest(this List<Team> list, Team team)
{
var orderedItems =
list.Where(p => p != team && !p.MatchedList.Contains(team)).OrderBy(x => Math.Abs(x.Score - team.Score));
return orderedItems.FirstOrDefault();
}
var nearestMatch = _teams.FindClosest(_teams.ElementAt(0));
Actually, I am trying to make a bridge game fixture calculator. For each round different teams must be matched and matched teams should be equal (or close) strength. But for next rounds, because of team match uniqueness is first criterion and we must arrange a match for people, we must bend the rules of score (point) closeness.
So it will result something like that;
Algorithm runs...
First Round;
Team1-Team2 ; Team3-Team4 ; Team5-Team6 .....
scores are updated by user at the end of the round1
Algorithm runs...
Second Round;
Team1-Team7 ; Team3-Team8 ; Team4-Team9 .....
.....
scores are updated by user at the end of the round8
Algorithm runs...
Ninth Round;
Team1-Team9; Team2-Team7; Team3-Team5 .....
scores are updated by user at the end of the round9
Algorithm runs...
No more match.
I prefer an algorithm like backtracking instead of brute force or random trial-and-error.
Can you suggest me an algorithm, a method to solve my problem?
Any help would be much appreciated.

Sort the teams by number of points, and then take pairs of teams starting from the beginning of the sorted list. Clearly the second-place team is the only possible team that is closest in points to the first-place team. If you don't pair the first-place team with the second-place team, but instead pair the second-place team with the third-place team, then the best match for the first-place team will be the fourth-place team. Pairing teams out of order in the sorted list will only increase the points disparity.
To illustrate, assume that there are 4 teams named A,B,C,D. The difference in points between the teams is d1,d2,d3
A B C D
d1 d2 d3
If the pairings are AB and CD, then the set of differences is { d1, d3 }.
If the pairings are BC and AD, then the set of differences is { d2, (d1+d2+d3) }.
I don't know of any metric where the second set is preferable to the first.

You need to explicitly state an objective function you are trying to optimize. If it's to find a minimum of sum of absolute values of differences between points for pairs, then I agree with another answer, you simply sort the teams by points and then pair them off two teams at a time from left to right and you can prove this gives the optimal pairing. If your objective function is different, then it may be harder to optimize, maybe even NP-hard depending on what your objective function is. But the point is we can't help if you if you just say "teams closest in points must be paired together" because it's possible to have a team with two neighbors on opposite sides that are equally close in points, such that the other neighbors of the two neighbors are far away, and then you can only pair up the team with one of its close neighbors.

You will need this course.
The dynamic programming may be too slow. Maybe the Local search, or other method would be more efficient. You could read the videos about Local Search first.

Related

Optimal algorithm for merging connected parallel 3D line segments

I have a sketch with 3D line segments. Each segment has start and end 3D point. My task is to merge segments if they are parallel and connected. I've implemented it on C#. This algorithm is recursive. Can this code be optimized? Can it be not recursive?
/// <summary>
/// Merges segments if they compose a straight line.
/// </summary>
/// <param name="segments">List of segments.</param>
/// <returns>Merged list of segments.</returns>
internal static List<Segment3d> MergeSegments(List<Segment3d> segments)
{
var result = new List<Segment3d>(segments);
for (var i = 0; i < result.Count - 1; i++)
{
var firstLine = result[i];
for (int j = i + 1; j < result.Count; j++)
{
var secondLine = result[j];
var startToStartConnected = firstLine.P1.Equals(secondLine.P1);
var startToEndConnected = firstLine.P1.Equals(secondLine.P2);
var endToStartConnected = firstLine.P2.Equals(secondLine.P1);
var endToEndConnected = firstLine.P2.Equals(secondLine.P2);
if (firstLine.IsParallel(secondLine) && (startToStartConnected || startToEndConnected || endToStartConnected || endToEndConnected))
{
Segment3d mergedLine = null;
if (startToStartConnected)
{
mergedLine = new Segment3d(firstLine.P2, secondLine.P2);
}
if (startToEndConnected)
{
mergedLine = new Segment3d(firstLine.P2, secondLine.P1);
}
if (endToStartConnected)
{
mergedLine = new Segment3d(firstLine.P1, secondLine.P2);
}
if (endToEndConnected)
{
mergedLine = new Segment3d(firstLine.P1, secondLine.P1);
}
// Remove duplicate.
if (firstLine == secondLine)
{
mergedLine = new Segment3d(firstLine.P1, firstLine.P2);
}
result.Remove(firstLine);
result.Remove(secondLine);
result.Add(mergedLine);
result = MergeSegments(result);
return result;
}
}
}
return result;
}
The classes Segment3D and Point3D are pretty simple:
Class Segment3D:
internal class Segment3d{
public Segment3d(Point3D p1, Point3D p2)
{
this.P1 = p1;
this.P2 = p2;
}
public bool IsParallel(Segment3d segment)
{
// check if segments are parallel
return true;
}
public Point3D P1 { get; }
public Point3D P2 { get; }
}
internal class Point3D
{
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public override bool Equals(object obj)
{
// Implement equality logic,
return true;
}
}
The optimizations
You are asking about the way to remove the recursion. However that isn't the only nor the largest problem of your current solution. So I will try to give you an outline of possible directions for the optimization. Unfortunately, since your code still isn't self-contained minimal reproducible example it is rather tricky to debug. If I have time in the future, I might re-visit.
First step: Limiting the number of comparisons.
Currently, you are performing unnecessary number of comparisons since you compare every two possible line segments and every possible alignment that they can have. This is unnecessary.
First step to lower the number of the comparisons is to separate the line segments by their direction. Currently, when you are trying to compare two vectors, you go and check if these align. If that is the case, you proceed with the rest of the comparisons.
If we sort the segments by the direction, we will naturally group them into buckets of sort. Sorting the segments might sound weird, there are three axes to sort by. That is fine though since the only thing we actually care about is the fact, that if two normalized vectors (x,y,z) and (a,b,c) differ in at least one coordinate, they are not parallel.
This sorting can be done by implementing the IComparable interface and then calling sort method as you would normally. This has already been described for example here. The sorting has the complexity O(N * log(N)) which is quite a lot better than your current O(N^2) approach. (Actually, your algorithm is even worse than that since during each recursion step you do another N^2 steps. So you might be dangerously close to the O(N^4) territory. There be the dragons.)
Note: If you are feeling adventurous and O(Nlog(N) is not enough, it could be possible to get to the O(N) territory by implementing the System.HashCode function for your direction vector and find the groups of parallel lines by using Sets or Dictionaries. However it might be rather tricky since floating point numbers and comparison for equality aren't exactly a friendly combination. Caution is required. Sorting should be enough for most of the use cases.
So now we have the line segments separated by the direction vectors. Even if you proceed now with your current approach the results should be much better since we lowered the size of the groups to be compared. However we can go further.
Second step: Smart comparison of the segment ends.
Since you want to merge the segments if either of their endpoints align(as you covered start-start, start-end, end-start, end-end combinations in your question) we can most likely start merging any two identical points we find. The easiest way to do that is once more either sorting or hashing. Since we won't be normalizing which can result in the marginall differences, hashing should be viable. However sorting is still the more-straightforward and "safer" way to go.
One of the many ways this could be done:
Put all of the endpoints into single linked list as a tuple (endpoint, parent_vector). Arrays are not suitable since the deletion would be costly.
Sort the list by the endpoints. (Once again, you will need to implement the IComparable interface for your points)
Go through the list. Whenever the two neighboring points are equal, remove them and merge their siblings into a new line segment (Or delete one of them and the closer sibling if the neighbors are both the starting or ending point of the segment).
When you pass whole list, all the segments are either merged or have no more neighbors.
Pick up your survivors and you are done.
The complexity of this step is once again O(N * log(N)) as in the previous case. No recursion necessary and the speedup should be nice.

Compare Two Ordered Lists in C#

The problem is that I have two lists of strings. One list is an approximation of the other list, and I need some way of measuring the accuracy of the approximation.
As a makeshift way of scoring the approximation, I have bucketed each list(the approximation and the answer) into 3 partitions (high, medium low) after sorting based on a numeric value that corresponds to the string. I then compare all of the elements in the approximation to see if the string exists in the same partition of the correct list.
I sum the number of correctly classified strings and divide it by the total number of strings. I understand that this is a very crude way of measuring the accuracy of the estimate, and was hoping that better alternatives were available. This is a very small component of a larger piece of work, and was hoping to not have to reinvent the wheel.
EDIT:
I think I wasn't clear enough. I don't need the two lists to be exactly equal, I need some sort of measure that shows the lists are similar. For example, The High-Medium-Low (H-M-L) approach we have taken shows that the estimated list is sufficiently similar. The downside of this approach is that if the estimated list has an item at the bottom of the "High" bracket, and in the actual list, the item is at the top of the medium set, the score algorithm fails to deliver.
It could potentially be that in addition to the H-M-L approach, the bottom 20% of each partition is compared to the top 20% of the next partition or something along those lines.
Thanks all for your help!!
So, we're taking a sequence of items and grouping it into partitions with three categories, high, medium, and low. Let's first make an object to represent those three partitions:
public class Partitions<T>
{
public IEnumerable<T> High { get; set; }
public IEnumerable<T> Medium { get; set; }
public IEnumerable<T> Low { get; set; }
}
Next to make an estimate we want to take two of these objects, one for the actual and one for the estimate. For each priority level we want to see how many of the items are in both collections; this is an "Intersection"; we want to sum up the counts of the intersection of each set.
Then just divide that count by the total:
public static double EstimateAccuracy<T>(Partitions<T> actual
, Partitions<T> estimate)
{
int correctlyCategorized =
actual.High.Intersect(estimate.High).Count() +
actual.Medium.Intersect(estimate.Medium).Count() +
actual.Low.Intersect(estimate.Low).Count();
double total = actual.High.Count()+
actual.Medium.Count()+
actual.Low.Count();
return correctlyCategorized / total;
}
Of course, if we generalize this to not 3 priorities, but rather a sequence of sequences, in which each sequence corresponds to some bucket (i.e. there are N buckets, not just 3) the code actually gets easier:
public static double EstimateAccuracy<T>(
IEnumerable<IEnumerable<T>> actual
, IEnumerable<IEnumerable<T>> estimate)
{
var query = actual.Zip(estimate, (a, b) => new
{
valid = a.Intersect(b).Count(),
total = a.Count()
}).ToList();
return query.Sum(pair => pair.valid) /
(double)query.Sum(pair => pair.total);
}
Nice question. Well, I think you could use the following method to compare your lists:
public double DetermineAccuracyPercentage(int numberOfEqualElements, int yourListsLength)
{
return ((double)numberOfEqualElements / (double)yourListsLength) * 100.0;
}
The number returned should determine how much equality exists between your two lists.
If numberOfEqualElements = yourLists.Length (Count) so they are absolutely equal.
The accuracy of the approximation = (numberOfEqualElements / yourLists.Length)
1 = completely equal , 0 = absolutely different, and the values between 0 and 1 determine the level of equality. In my sample a percentage.
If you compare these 2 lists, you will retrieve a 75% of equality, the same that 3 of 4 equal elements (3/4).
IList<string> list1 = new List<string>();
IList<string> list2 = new List<string>();
list1.Add("Dog");
list1.Add("Cat");
list1.Add("Fish");
list1.Add("Bird");
list2.Add("Dog");
list2.Add("Cat");
list2.Add("Fish");
list2.Add("Frog");
int resultOfComparing = list1.Intersect(list2).Count();
double accuracyPercentage = DetermineAccuracyPercentage(resultOfComparing, list1.Count);
I hope it helps.
I would take both List<String>s and combine each element into a IEnumerable<Boolean>:
public IEnumerable<Boolean> Combine<Ta, Tb>(List<Ta> seqA, List<Tb> seqB)
{
if (seqA.Count != seqB.Count)
throw new ArgumentException("Lists must be the same size...");
for (int i = 0; i < seqA.Count; i++)
yield return seqA[i].Equals(seqB[i]));
}
And then use Aggregate() to verify which strings match and keep a running total:
var result = Combine(a, b).Aggregate(0, (acc, t)=> t ? acc + 1 : acc) / a.Count;

C# Count occurrences in list

I have a type Card with an int property called Value where Ace = 14, Five = 5, etc.
If I have a list of Cards (5), ie. a hand. What I would like to do is count the number cards where the Value is equal to another card, I.e. Finding 4 of a kind, 3 of a kind, a pair, two pair etc. I'm fairly new to C#/programming, but I believe this is a case for LINQ/Lambda expression? Can someone help me out?
class Card : IComparable<Card>
{
private string name;
private int value;
private string suit;
public int Value
{
get
{
return value;
}
}
<.....>
//Constructor accepts string ex. "AH" and builds value 14, suit "Hearts"
public int CompareTo(Card that)
{
if (this.value > that.value) return -1;
if (this.value == that.value) return 0;
return 1;
}
}
List<Card> HandBuilder = new List<Card>();
HandBuilder.Add(...); //5 Cards
HandBuilder.Count ?? //Help
It's pretty trivial using GroupBy.
var cards = HandBuilder.GroupBy(card => card.Value)
.OrderByDescending(group => group.Count());
To check for four of a kind just see if the first group has four items; to check for three of a kind see if the first group has three items. To check for two pair just see if the first two groups each have two items.

Decaying value over time expontentially

I want to create a sorting algorithm that takes an items score (based on upvotes/downvotes) and sorts them based on an invisble underlying score that take time decay into account.
Being from the analytic philosophy side, my mathematical algorithms aren't always the best. What is a simple elegant way to solve for InverseTimeRelationship(currentItem.CreationDate) in the following example:
class Item()
{
int upvotes;
int downvotes;
int SortScore;
DateTime CreationDate;
}
var currentItem = GetSomeSpecificItemMethod();
int Score = currentItem.upvotes - currentItem.downvotes;
currentItem.SortScore = Score * InverseTimeRelationship(currentItem.CreationDate);
SortItemsBySortScore(Item[]);
InverseTimeRelationship(DateTime CreationDate)
{
//Code To Write
}
The hope being that after a day, the SortScore would be a little lower, but after say 2-3 days no matter how many votes it has, it will disappear from the top of the list/front page.
You can take a look at the algorithm reddit uses. It seems to be what you want.
Perhaps:
e^-x (= 1/e^x)
See this image (from Wikipedia).
And here's code:
double InverseTimeRelationship(DateTime CreationDate)
{
double HowFast = 0.1;
return Math.Exp(-DateTime.Now.Subtract(CreationDate).Days * HowFast);
}
And you can try it with:
Text = InverseTimeRelationship(DateTime.Today.AddDays(-3)).ToString();

Cards Game - Subset Sum - Can the following algorithm be optimized?

Ok, the cards game I'm developing is pretty similar to Scopa if someone knows it.
The deck contains 40 cards divided into 4 different suits of 10 cards each (ace => value 1, two => value 2, three = ..., four, five, six, seven, knave, queen, king => value 10).
There are 2 players (actually an AI and a human player) and they have 4 cards in their hand.
There are 4 free cards to take on the table and players can take them only respecting the following rules:
1) Court cards (knave, queen and king) can only take identical court cards (for example, if I have a queen I can only take a queen from the table).
2) Numeric cards (from ace to seven) can take identical numeric cards or smaller numeric cards by sum (for example, if I have a seven I can take a seven or { an ace, a six } or {a three, a four } or { an ace, three two }).
Now the time has come to find which cards the AI can eventually take during it's turn:
private List<List<Card>> CalculateAITake()
{
List<Int32> handValues = new List<Int32>();
List<List<Card>> takes = new List<List<Card>>();
/* here i take every hand card value, in a unique way
* in order to avoid processing two or more times the
* same value
*/
foreach (Card card in m_AIHand)
{
Int32 cardValue = (Int32)card.Rank;
if (!handValues.Contains(cardValue))
handValues.Add(cardValue);
}
/* for each hand card value now, I calculate the
* combinations of cards I can take from table
*/
foreach (Int32 handValue in handValues)
{
// it's a court card, let's use a direct and faster approach
if (handValue >= 8)
{
foreach (Card card in m_CardsOnTable)
{
if ((Int32)card.Rank == handValue)
{
List<Card> take = new List<Card>();
take.Add(card);
takes.Add(take);
}
}
}
else
// it's a numeric card, let's use recursion
CalculateAITakeRecursion(takes, (new List<Card>(m_CardsOnTable)), 0, (new List<Card>()), handValue, 0);
}
return takes;
}
private void CalculateAITakeRecursion(List<List<Card>> takes, List<Card> cardsExcluded, Int32 cardsExcludedIndex, List<Card> cardsIncluded, Int32 sumWanted, Int32 sumPartial)
{
for (Int32 i = cardsExcludedIndex; i < cardsExcluded.Count; ++i)
{
Card cardExcluded = cardsExcluded[i];
Int32 sumCurrent = sumPartial + (Int32)cardExcluded.Rank;
/* the current sum is lesser than the hand card value
* so I keep on recursing
*/
if (sumCurrent < sumWanted)
{
List<Card> cardsExcludedCopy = new List<Card>(cardsExcluded);
cardsExcludedCopy.Remove(cardExcluded);
List<Card> cardsIncludedCopy = new List<Card>(cardsIncluded);
cardsIncludedCopy.Add(cardExcluded);
CalculateAITakeRecursion(takes, cardsExcludedCopy, ++cardsExcludedIndex, cardsIncludedCopy, sumWanted, sumCurrent);
}
/* the current sum is equal to the hand card value
* we have a new valid combination!
*/
else if (sumCurrent == sumWanted)
{
cardsIncluded.Add(cardExcluded);
Boolean newTakeIsUnique = true;
Int32 newTakeCount = cardsIncluded.Count;
/* problem: sometimes in my results i can find both
* { ace of hearts, two of spades }
* { two of spades, ace of hearts }
* not good, I don't want it to happens because there
* is still a lot of work to do on those results!
* Contains() is not enought to guarantee unique results
* so I have to do this!
*/
foreach (List<Card> take in takes)
{
if (take.Count == newTakeCount)
{
Int32 matchesCount = 0;
foreach (Card card in take)
{
if (cardsIncluded.Contains(card))
matchesCount++;
}
if (newTakeCount == matchesCount)
{
newTakeIsUnique = false;
break;
}
}
}
if (newTakeIsUnique)
takes.Add(cardsIncluded);
}
}
}
Do you think that this algorithm could be improved somehow?
I'm trying to shorten this code as much as I can so that it can be easy to debug and easy to maintain... also, if someone has a more elegant solution to avoid duplicate combinations I would really, really appreciate it (I don't want to get both { ace of hearts, two of spades } and { two of spades, ace of hearts }... only one of them).
Many, many thanks in advance!
Rather than considering each numeric card in your hand and looking for free cards that total it, I would consider each possible total of free cards and looking for a numeric card in your hand that matches it. You could use some sort of bitset to speed up the check for a matching card in your hand, and if you sort the free cards in ascending order, you could avoid adding a card matching one that you skipped, and you could stop adding cards if you exceeded the highest numeric card in your hand.
EDIT: pseudocode follows (sorry, I'm no good at naming variables):
call find_subset_sum(1, List<int>, 0)
// Passing the total because it's easy to calculate as we go
sub find_subset_sum(int value, List<int> play, total)
if total > max_hand_card
return // trying to pick up too many cards
if total in hand_set
call store_play(play)
if value > max_free_card
return // no more cards available to pick up
// try picking up higher value cards only
find_subset_sum(value + 1, play, total)
// now try picking up cards of this value
for each free card
if card value = value // only consider cards of this value
total += value
play.append(card)
find_subset_sum(value + 1, play, total)
// you could remove all the added cards here
// this would avoid having to copy the list each time
// you could then also move the first recursive call here too
It looks a bit odd but that's to ensure that if you only need one card of a particular value you don't unnecessarily consider picking up each available card of that value.
You can optimise this still further by sorting the array in ascending order.

Categories