I want to express the following formula with Linq
I have the following function
private double Calc(IEnumerable<Frequency> recording, IEnumerable<Frequency> reading)
{
}
where the Frequency is :
public class Frequency
{
public double Probability { get; set; } //which are p's and q's in the formula
public int Strength { get; set; } //the i's i the formula
}
An example call to the function is
public void Caller(){
IEnumerable<Frequency> recording = new List<Frequency>
{
new Frequency {Strength = 32, Probability = 0.2}, //p32 = 0.2
new Frequency {Strength = 33, Probability = 0.2}, //p33 = 0.2
new Frequency {Strength = 34, Probability = 0.2}, //p34 = 0.2
new Frequency {Strength = 35, Probability = 0.2}, //...
new Frequency {Strength = 41, Probability = 0.2} //...
};
IEnumerable<Frequency> reading = new List<Frequency>
{
new Frequency {Strength = 34, Probability = 0.2}, //q34 = 0.2
new Frequency {Strength = 35, Probability = 0.2}, //q35 = 0.2
new Frequency {Strength = 36, Probability = 0.2},
new Frequency {Strength = 37, Probability = 0.2},
new Frequency {Strength = 80, Probability = 0.2},
};
Calc(reading, recordig);
}
For example, new Frequency {Strength = 32, Probability = 0.2}, means that p32 = 0.2 in the Hellinger formula.
k will be 100 in the formula, if an element does not exists in the collection it will have value 0. For example recording does only have values for i = 32,33, 34,35,41 therefore for other values in 1-100 pi will be zero.
My first implementation is
private double Calc(IEnumerable<Frequency> recording, IEnumerable<Frequency> reading)
{
double result = 0;
foreach (var i in Enumerable.Range(1,100))
{
var recStr = recording.FirstOrDefault(a => a.Strength == i);
var readStr = reading.FirstOrDefault(a => a.Strength == i);
var recVal = recStr == null ? 0 : recStr.Probability;
var readVal = readStr == null ? 0 : readStr.Probability;
result += Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2);
}
result = Math.Sqrt(result/2);
return result;
}
which is neither efficient nor elegant. I feel like the solution could be improved but i could not think a better way.
This question is complicated by the fact that the lists are sparse (we don't have probabilities for all readings). So, first we solve that problem:
public static IEnumerable<Frequency> FillHoles(this IEnumerable<Frequency> src, int start, int end) {
IEnumerable<int> range = Enumerable.Range(start, end-start+1);
var result = from num in range
join _freq in src on num equals _freq.Strength into g
from freq in g.DefaultIfEmpty(new Frequency { Strength = num, Probability = 0 })
select freq;
return result;
}
That leaves us with a dense array of frequency readings. Now we only need to apply the formula:
// Make the arrays dense
recording = recording.FillHoles(1, 100);
reading = reading.FillHoles(1, 100);
// This is the thing we will be summing
IEnumerable<double> series = from rec in recording
join read in reading on rec.Strength equals read.Strength
select Math.Pow(Math.Sqrt(rec.Probability)-Math.Sqrt(read.Probability), 2);
double result = 1 / Math.Sqrt(2) * Math.Sqrt(series.Sum());
result.Dump();
Not sure if this will be more performant than what you have, though.
Resharper turns your function into this :
double result = (from i in Enumerable.Range(1, 100)
let recStr = recording.FirstOrDefault(a => a.Strength == i)
let readStr = reading.FirstOrDefault(a => a.Strength == i)
let recVal = recStr == null ? 0 : recStr.Probability
let readVal = readStr == null ? 0 : readStr.Probability
select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();
return Math.Sqrt(result / 2);
As Patashu said, you can use a Dictionary<int, Frequency> to get O(1) lookup time :
private double Calc(Dictionary<int, Frequency> recording, Dictionary<int, Frequency> reading)
{
double result = (from i in Enumerable.Range(1, 100)
let recVal = recording.ContainsKey(i) ? 0 : recording[i].Probability
let readVal = reading.ContainsKey(i) ? 0 : reading[i].Probability
select Math.Pow(Math.Sqrt(recVal) - Math.Sqrt(readVal), 2)).Sum();
return Math.Sqrt(result / 2);
}
Related
I have a list of object.
class Student
{
int age;
int height;
int weight;
int marksInMath;
int marksInScience;
.
.
.
.
.
.
int marksIn...;
}
List<Student> lst = new List<Student>();
I want to calculate median and average of this List.
I am aware of
lst.Average(x=>x.Age);
lst.Average(x=>x.height);
.
.
Similarly for Median I can sort and then get median
lst.OrderBy(x=>x.Age);
//median logic on lst
But I don't want to repeat this code for every field(age, height, weight, marks, etc) in the object. Is there a way to do this in a loop or any other way so I don't have to get average for each field individually?
Here's the one pass way to compute averages:
var averages =
lst
.Aggregate(
new
{
N = 0,
Age = 0,
Height = 0,
Weight = 0,
MarksInMath = 0,
MarksInScience = 0
},
(a, x) =>
new
{
N = a.N + 1,
Age = a.Age + x.Age,
Height = a.Height + x.Height,
Weight = a.Weight + x.Weight,
MarksInMath = a.MarksInMath + x.MarksInMath,
MarksInScience = a.MarksInScience + x.MarksInScience,
},
a =>
new
{
Age = (double)a.Age / a.N,
Height = (double)a.Height / a.N,
Weight = (double)a.Weight / a.N,
MarksInMath = (double)a.MarksInMath / a.N,
MarksInScience = (double)a.MarksInScience / a.N,
});
If you're after sums, stddev, etc, it's done the same way.
However, you're not going to be able compute the median without doing so on each property, one at a time.
I am at work so haven't been able to run this to see if it works. But if you can retrieve the values of each field using Student.fieldName then should be good. Not 100% on the studentStats.Add, whether that's how to add it or not. Just know I've done it before without needing the Tuple.
public List<(decimal avg, decimal med)> StudentScores(List<Student> students)
{
var fieldNames = typeof(Student).GetFields().Select(field=>field.Name).ToList();
var studentStats = new List<(decimal avg, decimal med)>();
foreach(var field in fieldNames)
{
var average = 0;
var count = 0;
List<decimal> fieldMedian = new List<decimal>();
foreach(var student in students)
{
count++
totalScore = average + student.field;
fieldMedian.Add(student.field);
}
average = totalScore / count;
var sorted = fieldMedian.Sort();
if(count%2 = 0)
{
var middle1 = count/2;
var middle2 = (count/2)+1;
var median = (sorted[middle1] + sorted[middle2]) / 2;
studentStats.Add(average, median);
}
else
{
var middle = (count+1)/2;
var median = sorted[middle];
studentStats.Add(average, median);
}
}
return studentStats;
}
This question already has answers here:
Divide x into y parts by decreasing amount
(3 answers)
Closed 3 years ago.
If I had $1000(variable) and I want to split that amount up and give it to 20(variable) people, but rather than give it evenly to each person, I want to give more to the 1st person, and the 2nd person, etc.
So the 20th person gets the least, and the 5th person gets the 5th most.
People are sorted into a list by score, how could i check to make sure people with the same score are awarded the same amount of the prize while still giving out the prize total to all people?
Formula thus far:
int people = 20;
float prize = 1000;
List<int> list = new List<int>();
for( int i = 0; i < people; ++i )
{
list.add(Random.Range(0,100));
}
list.Sort();
float k = (2 * prize) / ((people) * (people - 1));
float sum = 0;
for (int i = 1; i < list.Count-1; ++i)
{
var personsPrize = i * k;
sum += personsPrize;
Console.WriteLine(personsPrize);
}
Console.WriteLine("sum = " + sum);
First place would get 25% of the total prize pool. Second place gets 20% and third place gets 15% then the rest is divided between the remaining people, with people on the same score getting the same amount.
What should happen for people getting tied first equal? They shouldn't get less than anybody else, but shouldn't double the first prize value.
I'd do it by splitting out the prize fractions first, and determining which prizes should be merged due to ties. Then, sum up the merged fractions and divide that merged amount equally to all the tied participants.
This ensures that the amount received for each tied participant is less than or equal to the greatest prize amount merged in and greater than or equal to the least prize amount merged in.
public class Person
{
public Person(string name, int position)
{
Name = name;
Position = position;
}
public string Name { get; set; }
public int Position { get; set; }
}
static void Main(string[] args)
{
var winners = new Person[]
{
new Person("Test 1", 1),
new Person("Test 2", 1),
new Person("Test 3", 1),
new Person("Test 4", 1),
new Person("Test 5", 5),
new Person("Test 6", 6),
new Person("Test 7", 7),
new Person("Test 8", 8),
new Person("Test 9", 9),
new Person("Test 10", 9),
new Person("Test 11", 11),
new Person("Test 12", 11),
new Person("Test 13", 13),
new Person("Test 14", 14),
new Person("Test 15", 15),
new Person("Test 16", 16),
new Person("Test 17", 17),
new Person("Test 18", 18),
new Person("Test 19", 19),
new Person("Test 20", 19)
};
var prizes = SplitPrizeFund(1000, winners.Length);
AllocatePrizes(winners, prizes);
}
private static void AllocatePrizes(IEnumerable<Person> positions, double[] prizes)
{
var orderedPositions = positions.OrderBy(f => f.Position).ToArray();
for (var pos = 0; pos < orderedPositions.Length;)
{
var currentPerson = orderedPositions[pos];
// Find equally placed people (if any)
var comList = orderedPositions.Skip(pos).Where(f => f.Position == currentPerson.Position).ToList();
// We should now have one or more people in our list
var splitWays = comList.Count;
// Total the prize fund over the places found
double splitFund = prizes.Skip(pos).Take(splitWays).Sum();
// Allocate the total winnings equally between winners of this place
bool first = true;
foreach (var person in comList)
{
if (first)
{
Console.WriteLine($"{person.Name,-20} {(splitFund / splitWays),10:C2}");
first = false;
}
else
{
// Identify equal placed winners
Console.WriteLine($"{person.Name,-19}= {(splitFund / splitWays),10:C2}");
}
}
pos += splitWays;
}
}
private static double[] SplitPrizeFund(double totalFund, int numberOfPrizes)
{
var prizes = new double[numberOfPrizes];
var remainingFund = totalFund;
int remainingPrizes = numberOfPrizes;
// Special handling for top three places
int pos = 0;
prizes[pos] = Math.Round(remainingFund * 0.25, 2, MidpointRounding.AwayFromZero);
pos += 1;
prizes[pos] = Math.Round(remainingFund * 0.20, 2, MidpointRounding.AwayFromZero);
pos += 1;
prizes[pos] = Math.Round(remainingFund * 0.15, 2, MidpointRounding.AwayFromZero);
pos += 1;
remainingPrizes -= 3;
remainingFund -= prizes[0] + prizes[1] + prizes[2];
// Linear reducing split from 4th (replace this with whatever you want)
int totalPortions = 0;
for (int i = 1; i <= remainingPrizes; i++)
totalPortions += i;
for (int i = remainingPrizes; i >= 1; i--)
{
prizes[pos] = Math.Round(remainingFund * i / totalPortions, 2, MidpointRounding.AwayFromZero);
remainingFund -= prizes[pos];
totalPortions -= i;
pos++;
}
return prizes;
}
This can be a solution :
class People
{
public int ID { get; set; }
public int Rank { get; set; }
public float? Prize { get; set; }
}
//Reserves 60% of the prize for the first, second and the third person.
//Imageine that there are 5 people have the highest rank 20 for exemple.
in this case the total of the first place makes 125%. It can't be possible.
For me, you should have another parameter to choose the first places.
Or, you should modify your rank logic: like if the total of percentages is over 100% or 90%, (or a percentage that you will decide), reduce the percentage of the first place, second and third places etc.
Imagene that you have 4 first place, 1 second place and 1 thirt place.
In this case you have (4 * 25%) + 20% + 15% = 135%. It means you have to reduce your 25% to for exemple 15 %, second place to 10% and the third place to 5%.
in this case you will have (4 * 15%) + 10% + 5% = 75 percent for your highest places and you will distribute 25% to other users.
private void CheckPrices()
{
float prize = 1000;
Random rnd = new Random(1);
var peopleList = new List<People>();
for (int i = 0; i < 20; i++)
{
peopleList.Add(new Test.People() { ID = i + 1, Rank = rnd.Next(5, 100) });
}
var firstPrize = prize * 25 / 100;
var secondPrize = prize * 20 / 100;
var thirstPrize = prize * 15 / 100;
int i = 0;
//Sets first places prizes.
foreach (var person in peopleList.OrderByDescending(ro => ro.Rank))
{
i++;
if (i == 1)
person.Prize = firstPrize;
else if (i == 2)
person.Prize = secondPrize;
else if (i == 3)
person.Prize = thirstPrize;
else
break;
}
var totalRank = peopleList.Sum(ro => ro.Rank);
float prizePerRank = (prize - (firstPrize + secondPrize + thirstPrize)) / totalRank;
foreach (var person in peopleList.Where( ro=> ro.Prize == null))
{
person.Prize = person.Rank * prizePerRank;
}
//
var totalPrizeDistributed = peopleList.Sum(ro => ro.Prize); //= 1000
}
}
I have an Object with X amount of hobbies, each hobby has a value between 0-100 (representing %), and together they add up to 100.
Every morning I want to run a function that decides what that Object is going to do, and the higher value a hobby has, the more likely they are to be chosen.
I cannot figure out how to translate that last part into code. Using these variables for example:
int fishing = 25;
int sleeping = 25;
int drinking = 50;
int running = 0;
This will work for you:
class Program
{
private static List<KeyValuePair<string, int>> Actions = new List<KeyValuePair<string, int>>()
{
new KeyValuePair<string, int>("fishing", 25),
new KeyValuePair<string, int>("sleeping", 25),
new KeyValuePair<string, int>("drinking", 5),
new KeyValuePair<string, int>("running", 0),
};
static void Main(string[] args)
{
var result = Actions.OrderByDescending(r => r.Value).First();
Console.WriteLine(result.Key);
}
}
This assumes that you already have the actions and their percentage. Now if any 2 (or more) of the items have the same percentage, it will show the first one added to the list.
EDIT
Sorry didn't get the requirement for randomization. Try this:
var listOfActionsToTake = new List<string>() { "fishing", "sleeping", "drinking", "running" };
var listOFActions = new List<KeyValuePair<string, int>>();
var rand = new Random();
var currentPercentage = 101;
for (int i = 0; i < listOfActionsToTake.Count; i++)
{
var current = rand.Next(currentPercentage);
if (i == listOfActionsToTake.Count - 1)
{
current = currentPercentage - 1;
}
listOFActions.Add(new KeyValuePair<string, int>(listOfActionsToTake[i], current));
currentPercentage -= current;
}
foreach (var action in listOFActions)
{
Console.WriteLine($"{action.Key}: {action.Value}");
}
var result = listOFActions.OrderByDescending(r => r.Value).First();
Console.WriteLine(result.Key);
Like this, no matter how many values you add to the listOfActionsToTake you will always end up with a sum of 100 between them, and the biggest will be selected
I found this example and it seems to make sense. You add the values to get a cumulative value. If the random value created is less then the cumulative probability, then the current item is the selected.
Basically, if that condition is false, then you ignore the current items probability from the condition. So the next item to be compared now has a higher probability to be chosen.
For example:
// 1. RandNum = 0.4
// 2. cumulative = 0.0
// 3. fishing = 0.25
// 4. cumulative = cumulative + fishing = 0.25
// 5. RandNum < cumulative ? false
The random number was 0.4 and fishing has a probably of 0.25 to be selected. Since fishing's probability is lower then the random number, we add it to the cumulative value and essentially ignore it in the next item. The next item will now have a higher probability to be chosen because now it's just sleeping and drinking. They now both have a .50 probability to be chosen.
// 1. RandNum = 0.4
// 2. cumulative = 0.25
// 3. sleeping = 0.25
// 4. cumulative = cumulative + sleeping = .50
// 5. RanNum < cumulative ? true
Although you would have to modify it to account for when the percentages are equal since it'll just take the first one. You could check if the percentages are the same after finding an inital action to do. If the randomly chosen action has the same percentage as another, then randomly pick one of them.
static void Main(string[] args)
{
int fishing = 25;
int sleeping = 25;
int drinking = 50;
int running = 0;
List<KeyValuePair<string, double>> elements = new List<KeyValuePair<string, double>>();
elements.Add(new KeyValuePair<string, double>("fishing", (fishing * (1.0 / 100.0)))); // 25 * (1 /100) = .25
elements.Add(new KeyValuePair<string, double>("sleeping", (sleeping * (1.0 / 100.0))));
elements.Add(new KeyValuePair<string, double>("drinking", (drinking * (1.0 / 100.0))));
elements.Add(new KeyValuePair<string, double>("running", (running * (1.0 / 100.0))));
Random r = new Random();
double rand = r.NextDouble();
double cumulative = 0.0;
string selectedElement = "";
for (int i = 0; i < elements.Count; i++)
{
cumulative += elements[i].Value;
if (rand < cumulative)
{
selectedElement = elements[i].Key;
break;
}
}
Console.WriteLine("Selected Element: " + selectedElement);
Console.ReadKey();
}
// Test 1: rand: 0.522105917
// cumulative: 1
// Selected Element: drinking
// Test 2: rand: 0.49201479
// cumulative: 0.5
// Selected Element: sleeping
This should do it and will work when they don't add to 100 too.
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static Dictionary<string, int> activities = new Dictionary<string, int>
{
{ "running", 0 },
{ "fishing", 25 },
{ "sleeping", 25 },
{ "drinking", 50 }
};
static void Main(string[] args)
{
int sum = activities.Sum(a => a.Value);
int rand = new Random().Next(sum);
int total = -1;
string activity = activities.SkipWhile(a => (total += a.Value) < rand).First().Key;
Console.WriteLine(activity);
}
}
I have a 2D point class as follows:
class Point
{
public int id_;
public float x_, y_;
public Point(int i, float x, float y)
{
id_ = i;
x_ = x;
y_ = y;
}
public float Distance(Point otherPoint)
{
return (float)Math.Sqrt(Math.Pow(x_ - otherPoint.x_, 2) + Math.Pow(y_ - otherPoint.y_, 2));
}
}
In my main code I have a list of these points. I am presented with a new point. I want to find that point in my list that is the shortest distance from the new point if it satisfies a minimum threshold criteria.
I originally wrote it straightforward by having a minValue (initialized to 1e6) and a minID, traverse through the list to find the minimum value. And outside the traversal, I checked to see if this min value was less than the threshold. That worked.
But I wanted to see if there was a better/cleaner way to implement it, and I ended up with this:
var list = new List<Point>();
list.Add(new Point(0, 10.0f, 1.0f));
list.Add(new Point(1, 1.0f, 0.0f));
list.Add(new Point(2, 0.0f, 0.0f));
var p = new Point(3, 0.6f, 0.0f);
var subList = list.Select((item, index) => new { item, index })
.Where(x => (x.item.distance(p) <= 1.0))
.Select(x => x.item).ToList();
Point minPoint = subList[Enumerable.Range(0, subList.Count).Aggregate((a, b) => (subList[a].Distance(p) < subList[b].Distance(p) ? a : b))];
Console.WriteLine(minPoint.id_);
Is there a better way to do this?
I would rather use the following, which does O(N) Distance calculations and O(N) comparisons:
var closest = list.Select(item => new { item, distance = item.Distance(p) })
.Aggregate((a, b) => a.distance <= b.distance ? a : b);
var closestPt = closest.distance <= 1.0 ? closest.item : null;
I would have some ideas about two solutions of the problem, here is the original class, stripped down of unnecessary underscores. Usually id is unique, so readonly and I borrowed Distance method from #CommuSoft's answer, as he is right about that method:
class Point
{
public readonly int id;
public float x;
public float y;
public Point(int id, float x, float y)
{
this.id = id;
this.x = x;
this.y = y;
}
public float Distance(Point p)
{
float dx = this.x - p.x;
float dy = this.y - p.y;
return (float)Math.Sqrt(dx * dx + dy * dy);
}
}
Shared part:
List<Point> list = new List<Point>();
list.Add(new Point(0, 10.0f, 1.0f));
list.Add(new Point(1, 1.0f, 0.0f));
list.Add(new Point(2, 0.0f, 0.0f));
Point p = new Point(3, 0.6f, 0.0f);
Next solution IpavluVersionA1 is the most efficient in usage of the memory/allocation and computationally efficient as well:
//VersionA1, efficient memory and cpu usage
Point closest_to_p = null;
float shortest_d = float.MaxValue;
//list.ForEach because it is iterating list through for cycle, most effective
list.ForEach(point =>
{
//Distance is computed only ONCE per Point!
var d = point.Distance(p);
if (d > 1.0f) return;
if (closest_to_p == null || shortest_d > d)
{
closest_to_p = point;
shortest_d = d;
}
});
//closest_to_p is cloases point in range with distance 1.0
//or below or is null, then does not exist
Next one is IpavluVersionA2, best performance wise:
//VersionA2, most efficient memory and cpu usage
Point closest_to_p = null;
float shortest_d = float.MaxValue;
int max = list.Count;
for (int i = 0; i < max; ++i)
{
var point = list[i];
var d = point.Distance(p);
if (d > 1.0f) continue;
if (closest_to_p == null || shortest_d > d)
{
closest_to_p = point;
shortest_d = d;
}
}
//closest_to_p is closest point in range with distance 1.0
//or below or is null, then does not exist
Another solution, IpavluVersionB which is using LINQ approach, has to create new struct object's in order to keep the Point and the distance, but they are most likely created on stack. Computing Distance is done only ONCE, then value is reused!
//versionB
var null_point = new KeyValuePair<Point,float>(null, float.PositiveInfinity);
var rslt_point =
list
.Select(xp =>
{
var d = xp.Distance(p);
return d <= 1.0f ? new KeyValuePair<Point, float>(xp, d) : null_point;
})
.Aggregate(null_point, (a, b) =>
{
if (a.Key == null) return b;
if (b.Key == null) return a;
return a.Value > b.Value ? b : a;
}, x => x.Key);
rslt_point is null or instance of most closest point to p.
BENCHMARK:
must be built in Release Mode,
must run without debugging, outside of VisualStudio,
test is running 5 times for two scenarios,
scenario X: 3 items, 10 million times, all methods, time in miliseconds,
scenario Y: 3mil itmes, 1 time, all methods, time in milliseconds,
the code is here
BechmarkREsults:
B - number of iterations over list,
I - number of items in list,
all numbers in milliseconds,
CommuSoft is solution from CommuSoft's answer,
Ivan Stoev proposed solution with anonymous types, behave similarly to VersionA2 with struct's,
Clearly IpavluVersionA2 is best performance wise.
B[10000000] I[3]: CommuSoft: 3521 IpavluA1: 371 IpavluA2: 195 IpavluB: 1587
B[10000000] I[3]: CommuSoft: 3466 IpavluA1: 371 IpavluA2: 194 IpavluB: 1583
B[10000000] I[3]: CommuSoft: 3463 IpavluA1: 370 IpavluA2: 194 IpavluB: 1583
B[10000000] I[3]: CommuSoft: 3465 IpavluA1: 370 IpavluA2: 194 IpavluB: 1582
B[10000000] I[3]: CommuSoft: 3471 IpavluA1: 372 IpavluA2: 196 IpavluB: 1583
B1 I[3000000]: CommuSoft: 919 IpavluA1: 21 IpavluA2: 17 IpavluB: 75
B1 I[3000000]: CommuSoft: 947 IpavluA1: 21 IpavluA2: 17 IpavluB: 75
B1 I[3000000]: CommuSoft: 962 IpavluA1: 21 IpavluA2: 17 IpavluB: 75
B1 I[3000000]: CommuSoft: 969 IpavluA1: 21 IpavluA2: 17 IpavluB: 75
B1 I[3000000]: CommuSoft: 961 IpavluA1: 21 IpavluA2: 17 IpavluB: 75
There are several things that can be improved:
Drop the first Select statement, since it has no use? You don't do anything with this point; This allows you to drop the second Select statement as well.
Don't use ToList: you are not interested in constructing this list anyway;
Math.Pow is a method used for arbitrary powers, it is furthermore not that much precise, use x*x instead of Math.Pow(x,2);
You made some small errors with respect to caps, Points instead of Points, Distance instead of Distance;
A way to obtain your Point, that is not the absolute most effecient one is using the following statement:
class Point {
public int id_;
public float x_, y_;
public Point(int i, float x, float y) {
id_ = i;
x_ = x;
y_ = y;
}
public float Distance(Point otherPoint) {
float dx = this.x_-otherPoint.x_;
float dy = this.y_-otherPoint.y_;
return (float)Math.Sqrt(dx*dx+dy*dy);
}
}
with a potential query:
var minPoint = list.Where(x => x.Distance(p) <= 1.0).OrderBy(x => x.Distance(p)).FirstOrDefault();
This will return null if no such item exists (that satisfies the Where clause). This will however, in most cases, not be the absolute most efficient implementation.
Another way is to first implement an extension method:
public static class Utils {
public static T MinBy<T,R> (this IEnumerable<T> source, Func<T,R> f) where R : IComparable<R> {
IEnumerator<T> e = source.GetEnumerator();
if(!e.MoveNext()) {
throw new Exception("You need to provide at least one element.");
}
T min = e.Current;
R minf = f(min);
while(e.MoveNext()) {
T x = e.Current;
R xf = f(x);
if(minf.CompareTo(xf) > 0) {
min = x;
minf = xf;
}
}
return min;
}
}
Then you can use:
var minPoint = list.Where(x => x.Distance(p) <= 1.0).MinBy(x => x.Distance(p));
This method runs in O(n) and is thus probably one of the most efficient ones.
Benchmarks
I've tested both the two methods of #ipavlu and myself, although with the small testset you gave, so the results are not really scientifically valid, and executed these using the csharp interactive shell:
csharp> DateTime dt=DateTime.Now; for(int i = 0; i < 10000000; i++) { var minPoint = list.Where(x => x.Distance(p) <= 1.0).OrderBy(x => x.Distance(p)).FirstOrDefault(); }; DateTime dt2 = DateTime.Now; Console.WriteLine(dt2-dt);
(1,68): warning CS0219: The variable `minPoint' is assigned but its value is never used
00:00:09.3165310
csharp> DateTime dt=DateTime.Now; for(int i = 0; i < 10000000; i++) { var minPoint = list.Where(x => x.Distance(p) <= 1.0).MinBy(x => x.Distance(p)); }; DateTime dt2 = DateTime.Now; Console.WriteLine(dt2-dt);
(1,68): warning CS0219: The variable `minPoint' is assigned but its value is never used
00:00:03.3658400
csharp> DateTime dt=DateTime.Now; for(int i = 0; i < 10000000; i++) { Point closest_to_p = null;float shortest_d = float.MaxValue;list.ForEach(point =>{var d = point.Distance(p);if (d > 1.0f) return;if (closest_to_p == null || shortest_d > d){closest_to_p = point;shortest_d = d;}}); }; DateTime dt2 = DateTime.Now;Console.WriteLine(dt2-dt);
00:00:10.4554550
csharp> DateTime dt=DateTime.Now; for(int i = 0; i < 10000000; i++) { var null_point = new KeyValuePair<Point,float>(null, float.PositiveInfinity);var rslt_point = list.Select(xp =>{var d = xp.Distance(p);return d <= 1.0f ? new KeyValuePair<Point, float>(xp, d) : null_point;}).Aggregate(null_point, (a, b) =>{if (a.Key == null) return b;if (b.Key == null) return a;return a.Value > b.Value ? b : a;}, x => x.Key); }; DateTime dt2 = DateTime.Now; Console.WriteLine(dt2-dt);
(1,146): warning CS0219: The variable `rslt_point' is assigned but its value is never used
00:00:18.5995530
This thus results in some insignificant results:
CommuSoft.A 00:00:09.3165310
CommuSoft.B 00:00:03.3658400
ipavlu.A 00:00:10.4554550
ipavlu.B 00:00:18.5995530
Furthermore note that these work in debug mode, and that compilers can sometimes find useful optimizations.
I have a list of numbers and I want to know which combination of the numbers in the list has the closest sum to a specific number(Target).( if there are many combinations, finding one is enough)
I searched a lot and know that there are lots of solutions here to find the combinations that their sum is equal to specific number, but there were no solution to find the greatest close to that number.
I also wrote a piece of code that loops from zero to "Target" to find the biggest sum, but as this process is so time consuming as it must be calculated for 100,000 lists, i wonder if there is more efficient way using preferably linq.
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
for (int UserNo = 1; UserNo <= MaxViewers; UserNo++)
{
for (int No = 1; No <= target; No++)
{
var matches = from subset in MyExtensions.SubSetsOf(List1)
where subset.Sum() == target
select subset;
}
}
public static class MyExtensions
{
public static IEnumerable<IEnumerable<T>> SubSetsOf<T>(this IEnumerable<T> source)
{
if (!source.Any())
return Enumerable.Repeat(Enumerable.Empty<T>(), 1);
// Grab the first element off of the list
var element = source.Take(1);
// Recurse, to get all subsets of the source, ignoring the first item
var haveNots = SubSetsOf(source.Skip(1));
// Get all those subsets and add the element we removed to them
var haves = haveNots.Select(set => element.Concat(set));
// Finally combine the subsets that didn't include the first item, with those that did.
return haves.Concat(haveNots);
}
}
Hello let's check a tricky way to achieve this,
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
var closestSubSet = MyExtensions.SubSetsOf(List1)
.Select(o=> new{ SubSet = o, Sum = o.Sum(x=> x)})
.Select(o=> new{ SubSet = o.SubSet, Sum = o.Sum, FromTarget = (target - o.Sum >= 0 ? target - o.Sum : (target - o.Sum) * -1 ) })
.OrderBy(o=> o.FromTarget).FirstOrDefault();
I know it's tricky i made the 2nd select for some performance reasons (Not Calling Sum Multiple Times But Use It). This should find the closest sum to the target you specify ^^ Have Fun
Optimization:
var List1 = new int[] { 5, 10, 15, 20, 25, 30, 35, 40, 45 };
var target = 40;
int MaxViewers = Convert.ToInt32(txtNoRecordsToAdd.Text);
int minClosestTargetRequired = 0;
int closestSum = int.maxValue;
int closestSetIndex = -1;
var subsets = MyExtensions.SubSetsOf(List1);
for(var c = 0 ; c < subsets.Count() ; c++)
{
int currentSum = subsets[c].Sum(o=> o);
if(currentSum < closestSum)
{
closestSum = currentSum;
closestSetIndex = c;
}
if(closestSum <= minClosestTargetRequired)
break;
}
Console.WriteLine("Closest Sum Is {0} In Set Index {1},closestSum,closestSetIndex);