I have a non-linear optimization problem with constraints. It can be solved in Microsoft Excel with the Solver add-in, but I am having trouble replicating that in C#.
I installed the Microsoft Solver Foundation dll
This is the example where I adapted my code from :
http://msdn.microsoft.com/en-us/library/gg261758(v=vs.93).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-3
Basically, in my code, I have 2 parameters called RHO and NU. I need to get their optimal values which minimizes the sum of squares. I have ONLY 1 value for RHO and 1 value for NU, so I know that putting RHO[s] and NU[s] in my code is wrong, but when I replace them with RHO and NU, I get errors.
Another issue is that, in the example, the code looks for one optimal variable called "fuel" of type "Goal". In my case, I have 2 variables which need to be optimized.
Obviously, the program breaks at the last line, it says: "there is no solution to propagate".
Anyone has an idea how my code could be corrected ?? any help is appreciated !
This is my code:
class Segment
{
public double Strike { get; set; }
public double VolImp { get; set; }
public double _Rho_ { get; set; }
public double _Nu_ { get; set; }
public double _Alpha_ { get; set; }
}
Segment[] segmentData = new Segment[] {
new Segment { Strike = 5, VolImp = 53.2608},
new Segment { Strike = 10, VolImp = 48.3773},
new Segment { Strike = 20, VolImp = 43.8949},
new Segment { Strike = 30, VolImp = 40.9367},
new Segment { Strike = 40, VolImp = 38.891},
new Segment { Strike = 50, VolImp = 37.417},
new Segment { Strike = 60, VolImp = 36.2838},
new Segment { Strike = 70, VolImp = 35.3713},
new Segment { Strike = 80, VolImp = 34.6192},
new Segment { Strike = 90, VolImp = 33.9774},
new Segment { Strike = 100, VolImp = 33.4359},
new Segment { Strike = 110, VolImp = 32.9747},
new Segment { Strike = 120, VolImp = 32.5635},
new Segment { Strike = 130, VolImp = 32.2025},
new Segment { Strike = 140, VolImp = 31.8917},
new Segment { Strike = 150, VolImp = 31.6209},
new Segment { Strike = 160, VolImp = 31.3702},
new Segment { Strike = 170, VolImp = 31.1596},
new Segment { Strike = 180, VolImp = 30.9591},
};
SolverContext context = SolverContext.GetContext();
Model model = context.CreateModel();
// Parameters
Set segments = new Set(Domain.Integer, "segments");
Parameter RHO = new Parameter(Domain.Real, "RHO", segments);
RHO.SetBinding(segmentData, "_Rho_", "Strike");
Parameter NU = new Parameter(Domain.RealNonnegative, "NU", segments);
NU.SetBinding(segmentData, "_Nu_", "Strike");
Parameter ALPHA = new Parameter(Domain.RealNonnegative, "ALPHA", segments);
ALPHA.SetBinding(segmentData, "_Alpha_", "Strike");
Parameter MyStrike = new Parameter(Domain.RealNonnegative, "MyStrike", segments);
MyStrike.SetBinding(segmentData, "Strike", "Strike");
Parameter MyImpVol = new Parameter(Domain.RealNonnegative, "MyImpVol", segments);
MyImpVol.SetBinding(segmentData, "VolImp", "Strike");
model.AddParameters(RHO, NU, ALPHA, MyStrike, MyImpVol);
//Constraints
model.AddConstraint("rho_bound", Model.ForEach(segments, s => (-1 <= RHO[s] <= 1))) ;
model.AddConstraint("nu_bound", Model.ForEach(segments, s => (0 <= NU[s]) )) ;
model.AddConstraint("alpha_bound", Model.ForEach(segments, s => (0 < ALPHA[s]))) ;
double Forward = 100 ;
//Goal
Goal Mygoal = model.AddGoal("Mygoal", GoalKind.Minimize, Model.Sum(Model.ForEach(segments, s => Model.Power((MyImpVol[s] - (ALPHA[s] * (NU[s] / ALPHA[s] * Model.Log(Forward / MyStrike[s])) * (Model.Log((Model.Sqrt(1 - 2 * RHO[s] * (NU[s] / ALPHA[s] * Model.Log(Forward / MyStrike[s])) + Model.Power((NU[s] / ALPHA[s] * Model.Log(Forward / MyStrike[s])), 2)) + (NU[s] / ALPHA[s] * Model.Log(Forward / MyStrike[s])) - RHO[s]) / (1 - RHO[s]))) * (1 + (0.25 * RHO[s] * NU[s] * ALPHA[s] + (2 - 3 * RHO[s] * RHO[s]) / 24 * NU[s] * NU[s]) * 6.46407))), 2)))) ;
//solve
context.Solve();
context.PropagateDecisions();
Related
I've creating 5 shapes and objects in the addin. I need to group these objects, but it won't allow me to. I', creating 3 ovals, 1 rectable and 1 textbox. I'm thinking I can't group them because they aren't the same shape.
How I create the shapes:
PowerPoint.Slide activeSlide = Globals.ThisAddIn.Application.ActiveWindow.View.Slide;
var color = Color.Gray;
var accColor = Color.LightGray;
PowerPoint.Shape ellipseShapeInner = activeSlide.Shapes.AddShape(Office.MsoAutoShapeType.msoShapeOval, 10, 10, 2.5f * 28.3464567f, 2.5f * 28.3464567f);
ellipseShapeInner.Name = "innerCircle";
ellipseShapeInner.Fill.Visible = Office.MsoTriState.msoFalse;
ellipseShapeInner.Line.Weight = 3;
ellipseShapeInner.Line.ForeColor.RGB = ColorTranslator.ToOle(color);
PowerPoint.Shape ellipseShapeMiddle = activeSlide.Shapes.AddShape(Office.MsoAutoShapeType.msoShapeOval, 10, 10, 3.5f * 28.3464567f, 3.5f * 28.3464567f);
ellipseShapeMiddle.Name = "middleCircle";
ellipseShapeMiddle.Fill.Visible = Office.MsoTriState.msoFalse;
ellipseShapeMiddle.Line.Weight = 2;
ellipseShapeMiddle.Line.ForeColor.RGB = ColorTranslator.ToOle(accColor);
PowerPoint.Shape ellipseShapeOuter = activeSlide.Shapes.AddShape(Office.MsoAutoShapeType.msoShapeOval, 100, 100, 4.5f * 28.3464567f, 4.5f * 28.3464567f);
ellipseShapeOuter.Name = "OuterCircle";
ellipseShapeOuter.Fill.Visible = Office.MsoTriState.msoFalse;
ellipseShapeOuter.Line.Weight = 2;
ellipseShapeOuter.Line.ForeColor.RGB = ColorTranslator.ToOle(accColor);
ellipseShapeMiddle.Left = ellipseShapeOuter.Left + (ellipseShapeOuter.Width - ellipseShapeMiddle.Width) / 2;
ellipseShapeMiddle.Top = ellipseShapeOuter.Top + (ellipseShapeOuter.Height - ellipseShapeMiddle.Height) / 2;
ellipseShapeInner.Left = ellipseShapeMiddle.Left + (ellipseShapeMiddle.Width -ellipseShapeInner.Width) / 2;
ellipseShapeInner.Top = ellipseShapeMiddle.Top + (ellipseShapeMiddle.Height - ellipseShapeInner.Height) / 2;
PowerPoint.Shape lineShape = activeSlide.Shapes.AddShape(Office.MsoAutoShapeType.msoShapeRectangle, 10, 10, 7 * 28.3464567f, 0);
lineShape.Name = "LineShape";
lineShape.Line.Weight = 3;
lineShape.Line.ForeColor.RGB = ColorTranslator.ToOle(color);
lineShape.Left = ellipseShapeInner.Left + ellipseShapeInner.Width;
lineShape.Top = ellipseShapeInner.Top + (ellipseShapeInner.Height / 2);
PowerPoint.Shape textbox = activeSlide.Shapes.AddTextbox(Office.MsoTextOrientation.msoTextOrientationHorizontal, 10, 10, 7 * 28.3464567f, 0);
textbox.Name = "OuterTextbox";
textbox.TextFrame.TextRange.InsertAfter("Fokuspungt");
textbox.Top = lineShape.Top - textbox.Height;
textbox.Left = lineShape.Left + (textbox.Width / 4);
I've tried multiple ways of grouping objects I've found from stack overflow, youtube, discord you name it, but nothing seems to work.
I've tried a list:
ArrayList list = new ArrayList();
list.Add(ellipseShapeInner);
list.Add(ellipseShapeMiddle);
list.Add(ellipseShapeOuter);
list.Add(lineShape);
list.Add(textbox);
activeSlide.Shapes.Range(list).Group();
Error: System.ArgumentException: 'Shapes (unknown member): Illegal
value. Bad type: expected 1D array of Variants, Integers, Longs, or
Strings'
I've tried an array:
string[] myRangeArray = new string[2];
myRangeArray[0] = "innerCircle";
myRangeArray[1] = "middleCircle";
myRangeArray[2] = "outerCircle";
myRangeArray[3] = "LineShape";
myRangeArray[4] = "OuterTextbox";
activeSlide.Shapes.Range(myRangeArray).Group();
Error: System.IndexOutOfRangeException: 'Index was outside the matrix
limit'
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've been trying to build a recursive function to replace the following n deep nested loop algorithm, the depth will change based on the length of the combination anywhere for 2 to n (most likely less than 20)
Edit: removed confusing code
I have searched this site and found Converting nested loop and More nested loop conversions but I can't seam to tweak them to output what I need.
or I could just be looking at it wrong and not needing a recursive function at all.
trying to do this in C#, thanks for any help.
Edit: hope this explains things a bit more.
What I’m trying to do is find the best combination of options and amounts to give me maximum return without going over budget.
So I have a cut down list of options that meets my requirements, then I want to feed them into this program/script to get the best combination with highset return, I also need to reduce my risk by buying more than one option normally 3-10
So below is my full code so far fixed at a depth of 3:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Combinations
{
class Program
{
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
List<option> options = new List<option> {
new option("A", 6.50, 0.18, 25000, 3),
new option("B", 23.00, 0.59, 25000, 3),
new option("C", 27.50, 0.60, 25000, 3),
new option("D", 21.00, 0.40, 25000, 3),
new option("E", 16.00, 0.30, 25000, 3),
new option("F", 7.00, 0.13, 25000, 3),
new option("G", 22.50, 0.38, 25000, 3),
new option("H", 27.00, 0.45, 25000, 3),
new option("I", 13.00, 0.20, 25000, 3),
new option("J", 20.50, 0.30, 25000, 3),
new option("K", 17.50, 0.25, 25000, 3),
new option("L", 10.50, 0.15, 25000, 3),
new option("M", 29.00, 0.41, 25000, 3),
new option("N", 26.50, 0.37, 25000, 3),
new option("P", 15.50, 0.21, 25000, 3),
new option("Q", 16.00, 0.20, 25000, 3),
new option("R", 10.00, 0.12, 25000, 3),
new option("S", 25.00, 0.30, 25000, 3),
new option("T", 27.00, 0.32, 25000, 3),
new option("U", 22.00, 0.25, 25000, 3),
new option("V", 26.50, 0.30, 25000, 3),
new option("W", 27.00, 0.30, 25000, 3),
new option("X", 14.50, 0.16, 25000, 3),
new option("Y", 28.50, 0.31, 25000, 3),
new option("Z", 28.50, 0.30, 25000, 3)
};
stopwatch.Start();
IEnumerable<List<option>> combinations = GetCombination(options, 3);
stopwatch.Stop();
Console.WriteLine("Combinations - Time elapsed: {0}", stopwatch.Elapsed);
stopwatch.Start();
bestFit(combinations);
stopwatch.Stop();
Console.WriteLine("Best Fit - Time elapsed: {0}", stopwatch.Elapsed);
Console.ReadKey();
}
static IEnumerable<List<option>> GetCombination(List<option> list, int _size)
{
double count = Math.Pow(2, list.Count);
List<option> combination = new List<option>();
int size = 0;
for (int i = 1; i <= count - 1; i++)
{
for (int j = 0; j < list.Count; j++)
{
if (((i >> j) & 1) == 1)
{
combination.Add(list[j]);
size++;
}
}
if (size == _size)
{
yield return combination;
}
combination.Clear();
size = 0;
}
}
static void bestFit(IEnumerable<List<option>> combinations)
{
double max = 0d;
double results = 0d;
double total = 0d;
string output = "";
string tmpOutput = "";
int y0 = 0;
int y1 = 1;
int yn = 2;
foreach (var combination in combinations)
{
option A1 = combination[y0];
for (int x1 = A1.lower; x1 < A1.upper; x1++)
{
option A2 = combination[y1];
for (int x2 = A2.lower; x2 < A2.upper; x2++)
{
option An = combination[yn];
for (int xn = An.lower; xn < An.upper; xn++)
{
int[] counts = { x1, x2, xn };
int i = 0;
foreach (option objOption in combination)
{
results += objOption.bid * 100 * counts[i] / objOption.cash;
total += (objOption.strike - objOption.bid) * 100 * counts[i];
tmpOutput += objOption.symbol + " STO " + counts[i].ToString() + " # $" + objOption.strike + ", ";
i++;
}
if (results > max && total < A1.cash)
{
output = tmpOutput.Remove(tmpOutput.Length - 2) + " for a gain of " + results*100 + "% using a total of $" + total;
max = results;
}
results = 0d;
total = 0d;
tmpOutput = "";
}
}
}
}
Console.WriteLine(output);
}
}
class option
{
public string symbol { get; set; }
public double strike { get; set; }
public double bid { get; set; }
public double cash { get; set; }
public int lower;
public int upper;
public option(string _symbol, double _strike, double _bid, double _cash, int _trades)
{
this.symbol = _symbol;
this.strike = _strike;
this.bid = _bid;
this.cash = _cash;
double tradeCash = _cash / _trades;
this.lower = (int)((0.25 * tradeCash) / ((_strike - _bid) * 100));
this.upper = (int)((1.25 * tradeCash) / ((_strike - _bid) * 100));
}
}
}
This should give an output of:
Combinations - Time elapsed: 00:00:00.0002575
A STO 15 # $6.5, B STO 3 # $23, D STO 4 # $21 for a gain of 2.428% using a total of $24443
Best Fit - Time elapsed: 00:00:11.9196411
hope this help to clear things up.
it seems as if you are only using the for loops to produce your count entry. I would first begin by refactoring this part out into something like this:
public struct Bound
{
public int Lower { get; set; }
public int Upper { get; set; }
}
public static class Counting
{
public static IEnumerable<int[]> Indizes(this Bound[] bounds)
{
return Indizes(bounds, 0);
}
static IEnumerable<int[]> Indizes(this Bound[] bounds, int index)
{
if (index >= bounds.Length)
yield return new int[] {};
else
{
foreach(var comb in Indizes(bounds, index+1))
{
for (var i = bounds[index].Lower; i < bounds[index].Upper; i++)
{
var newArr = new int[comb.Length + 1];
Array.Copy(comb, 0, newArr, 1, comb.Length);
newArr[0] = i;
yield return newArr;
}
}
}
}
}
remark mostlikely you can get this faster but I think it shows you how you can do such thinks using recursion
With something like this (replace the Bound struct with your data-structure - it's only in there so I could test my code) your insane-loop simplifies to:
foreach(var combination in combinations)
{
foreach (var counts in Counting.Indizes(combination))
{
int i = 0;
foreach (myClass objMyClass in combination)
{
results += objMyClass.a * 100 * counts[i] / objMyClass.b;
total += (objMyClass.c - objMyClass.a) * 100 * counts[i];
tmpOutput += objMyClass.symbol + " count " + counts[i].ToString() + ", ";
i++;
}
// ...
}
}
for the rest I don't understand your code enough to give any advice
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);
}
Hi I’m working on unit test for my project and I want to check a getmin method in an abstract class that is named Measurementool. That method return a minimum value of selectedarea matrix from current matrix. my project has a rectangle area that is really N*N matrix .
and I need minimum value inside of matrix. I write unit test for debug and test the getmin value but I don’t see desired value for actual value. selectedarea matrix filled with zero and not set with expected value in test method. I think, it considers two matrix. matrix with zero value is wrong when new matrix created as follow:
MatrixHolder selectedArea =
new MatrixHolder(new double[areaHeight * areaWidth], areaWidth);
unit test code:
[TestMethod()]
public void GetMinTest()
{
AreaMeasurmentTool target = new Rectangle();
double[,] MatrixResult = new double[,] {
{10, 0, 20, 10, 10},
{12, 2, 30, 15, 16},
{19, 0, 43, 10, 17},
{80, 0, 65, 18, 13},
{70, 4, 10, 10, 10}};
double []SelectedArea1 = new double[9]{2, 30, 15, 1, 43, 10, 2, 65, 18};
MatrixHolder currentMatrix = new MatrixHolder(SelectedArea1, 3);
double expected = 1;
double actual;
actual = target.GetMin(currentMatrix);
Assert.AreEqual(expected, actual);
}
and class MeasurementTool:
public virtual double GetMin(MatrixHolder currentMatrix)
{
if (currentMatrix == null)
{
throw new Exception("the image Matrix cannot be null.");
}
double minValue = 0;
SetSelectArea(currentMatrix);
minValue = CalculateMin();
minValue = Math.Round(minValue, 2);
return minValue;
}
private void SetSelectArea(MatrixHolder currentMatrix)
{
PointHolder start = new PointHolder(0, 0);
PointHolder end = new PointHolder(0, 0);
SetBoundries(start, end);
int areaHeight = (end.Y - start.Y);
int areaWidth = (end.X - start.X);
MatrixHolder selectedArea = new MatrixHolder(new double[areaHeight * areaWidth], areaWidth);
for (int i = 0; i < areaHeight; i++)
{
int sourceIndex = start.X + (currentMatrix.Witdh * (i + start.Y));
int targetIndex = areaWidth * i;
Array.Copy(currentMatrix.Values, sourceIndex
, selectedArea.Values, targetIndex
, areaWidth);
}
SelectedAreas = selectedArea;
}