C# - optimize logic [closed] - c#

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have a list of object. Those objects contain lat and lon coordinates among other properties. I want to extract only thoose coordonates that satisfy
(Math.Abs(elem1.lat - elem2.lat) > Delta && Math.Abs(elem1.lon - elem2.lon) > Delta)
Basically I want to extract only objects that dont have coordinates close to each other.
Is there an elegant way to do this with C# LINQ ?
EDIT 1 :
So elem1 and elem2 are any 2 elements from the list. I elements that will result from the query to have more then delta between any 2 of them
EDIT 2 :
This is my code at the moment. It works as I want :
for (int i = 0; i < companyHistory.Count; i++)
{
var isOk = true;
var count = zoomApprovedHistory.Count;
for (var j = 0; j < count; j++)
{
if (Math.Abs(companyHistory[i].X - zoomApprovedHistory[j].X) < delta && Math.Abs(companyHistory[i].Y - zoomApprovedHistory[j].Y) < delta)
{
isOk = false;
break;
}
}
// if no other point is close add it to approved array for this zoom
if (isOk)
{
zoomApprovedHistory.Add(companyHistory[i]);
}
}
How can I optimize it ? it has O(n^2) complexity. Looking for the fastest option

Your question is not well-phrased. If your elem1 or elem2 is a single item you want to compare against, this is pretty easy. Just do a LINQ Where to find the items that match the criteria.
Coordinates it = new Coordinates { lat = 5, lon = 5 };
var picked = list.Where(x => Math.Abs(x.lat - it.lat) < Delta && Math.Abs(x.lon - it.lon) < Delta);
If, on the other hand, your two elements are any two elements in the list, then you run into a problem where you need to clarify things. As I mentioned in my comment, if your have elem1, elem2, and elem3, and if the first two don't match the criteria but if elem2 and elem3 do match the criteria, then are both of those elements (2 and 3) 'hits'? Assuming they are, the solution below will work. I wrote a simple Comparer to help make things easy as well.
public class CoordinateComparer : IEqualityComparer<Coordinates>
{
public bool Equals(Coordinates x, Coordinates y)
{
return (x.lat == y.lat && x.lon == y.lon);
}
public int GetHashCode(Coordinates obj)
{
unchecked
{
int hash = 17;
if (obj != null)
{
hash = hash * 23 + obj.lat.GetHashCode();
hash = hash * 23 + obj.lon.GetHashCode();
}
return hash;
}
}
}
public class Coordinates
{
public int lat { get; set; }
public int lon { get; set; }
}
class MainClass
{
public static void Main(string[] args)
{
List<Coordinates> list = new List<Coordinates>();
list.Add(new Coordinates { lat = 5, lon = 4 });
list.Add(new Coordinates { lat = 4, lon = 5 });
list.Add(new Coordinates { lat = 7, lon = 4 });
list.Add(new Coordinates { lat = 6, lon = 3 });
list.Add(new Coordinates { lat = 8, lon = 2 });
double Delta = 1.1;
List<Coordinates> results = new List<Coordinates>();
foreach(Coordinates item in list)
{
// Find items that match the condition
var matches = list.Where(x => Math.Abs(x.lat - item.lat) < Delta && Math.Abs(x.lon - item.lon) < Delta);
// The 'item' will always match the condition with itself, which is undesirable, so remove it
matches = matches.Except(new List<Coordinates> { item }, new CoordinateComparer());
// Add the items that are not already in it to the results list
results.AddRange(matches.Where(x => !results.Contains(x, new CoordinateComparer())));
}
foreach (Coordinates item in results)
Console.WriteLine("Lat : {0}, Lon : {1}", item.lat, item.lon);
Console.ReadLine();
}
}
Bear in mind that if your latitude and longitude coordinates are either double or float, which they probably are, you might run into problems when comparing. But that's an altogether different question.

Related

List of numbers and goal -> closes sum

Regarding a question I got from one of my friends I want to ask the best possible solution
The situation is that I have a list of integers for example
2 5 6 8
And I want to get to the integers 17
I can only use each integers ones.
The closest you can get in this case is 16 because no combination leads up to 17.
public class Item
{
public int Weight { get; set; }
public int Value { get; set; }
}
public class Program
{
public static void Main()
{
var items = new[]
{
new Item {Value = 60, Weight = 10},
new Item {Value = 100, Weight = 20},
new Item {Value = 120, Weight = 30},
};
Console.WriteLine(KnapSackRecursive(items, 50));
}
public static int KnapSackRecursive(Item[] items, int capacity)
{
// keep track of the best value seen.
//TODO: Make it a list of numbers
int best = 0;
for (int i = 0; i < items.Length; i++)
{
// This is an array of the other items.
var otherItems = items.Take(i).Concat(items.Skip(i + 1)).ToArray();
// Calculate the best value without using the current item.
int without = KnapSackRecursive(otherItems, capacity);
int with = 0;
// If the current item fits then calculate the best value for
// a capacity less it's weight and with it removed from contention
// and add the current items value to that.
if (items[i].Weight <= capacity)
{
with = KnapSackRecursive(otherItems, capacity - items[i].Weight)
+ items[i].Value;
}
// The current best is the max of the with or without.
int currentBest = Math.Max(without, with);
// determine if the current best is the overall best.
if (currentBest > best)
best = currentBest;
}
return best;
}
}
Edit: It now finds the best possible weight based on the list. It'll result in finding that 20+30 = 50 so it returns 100+120 = 220 I want it to return ("Found best possible combination: 100 + 120 = 220") not just ("220")

Read a file to List<int> [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I am needing to create a list of numbers from an uploaded file, if the numbers have "-" representing a range, I need to split the numbers, making the first number a start, iterate through til it gets to the second number. I know I will be storing the numbers in a List, I'm just not sure on how to break out the numbers in the file with "-", Here is an example of the file:
099985-10043
102609-102886
102917-102921
106100-106101
110684-110685
114886-114887
117765-117766
120604-120605
121157-121158
121627-121911
122539
and here is where I am with the code:
if(string.IsNullOrEmpty(fileName.Text)) return;
_MissingInt = new List<int>();
var lines = File.ReadAllLines(fileName.Text);
foreach (string line in lines) {
...need help with logic...
}
I would greatly appreciate any direction and help as my programming skills are pretty weak and I am learning...this is not homework
I will assume that the file contains lines which can have two int values maximum, separated by -. Let's suppose we have a class like this:
class Interval {
public int left;
public int right;
public bool hasRight;
public Interval(int left, int right) {
this.left = left;
this.right = right;
hasRight = true;
}
public Interval(int left) {
this.left = left;
hasRight = false;
}
}
Now let's implement a parser method:
protected Interval parse(String line) {
String[] parts = line.Split(new string[] {"-"});
int left, right;
if (!Int32.TryParse(parts[0], left)) {
return null; //Invalid interval
}
return ((parts.length <= 1) || (!Int32.TryParse(parts[1], right))) ? (new Interval(left)) : (new Interval(left, right));
}
And another:
protected Interval[] aggregateParse(String[] lines) {
Interval[] intervals = new Interval[lines.Length];
for (int i = 0; i < lines.Length; i++) {
intervals[i] = parse(lines[i]);
}
return intervals;
}
This could be used to generate intervals. If we need to get the integers between the edges of the interval and store them, then we can use a for cycle, starting from the left edge and ending at the right edge, filling an array of right - left - 1 size, which might be a member of interval. The problem is that an interval which is opened to the right will never end, so make sure you do this wisely.
Linq-style solution that supports all wrong input like strings and empty lines. For example:
1
sfd
2
5-10
11-fdfd
12 13
14
int x;
var res = lines
// filter out empty lines
.Where(line => !string.IsNullOrEmpty(line))
// convert ranges to pairs
.Select(line => line.Split('-'))
// filter out not numbers
.Where(line => line.All(number => int.TryParse(number, out x)))
// convert all strings to int
.Select(item => item.Select(int.Parse).ToList())
.SelectMany(item =>
{
if (item.Count > 1)
{
return Enumerable.Range(item[0], item[1] - item[0] + 1);
}
return item;
})
.ToList();
I didn't agree with Badiparmagi's answer as its adding string/character to int list, its not compilable code.
Here giving you my tested attempt. I hope it may help you.
if (string.IsNullOrEmpty(fileName.Text)) return;
var _MissingInt = new List<int>();
var lines = File.ReadAllLines(fileName.Text);
foreach (var line in lines)
{
if(string.IsNullOrEmpty(line))
continue;
if (line.Contains("-"))
{
var range = line.Split('-');
int startNumber;
int endNumber;
if(int.TryParse(range[0], out startNumber) && int.TryParse(range[1]), out endNumber)
for (var i = startNumber; i < endNumber; i++)
{
_MissingInt.Add(i);
}
}
else
{
int num;
if(int.TryParse(line, out num))
_MissingInt.Add(num);
}
}

C# Simple Constrained Weighted Average Algorithm with/without Solver

I'm at a loss as to why I can't get this seemingly simple problem solved using Microsoft Solver Foundation.
All I need is to modify the weights (numbers) of certain observations to ensure that no 1 observation's weight AS A PERCENTAGE exceeds 25%. This is for the purposes of later calculating a constrained weighted average with the results of this algorithm.
For example, given the 5 weights of { 45, 100, 33, 500, 28 }, I would expect the result of this algorithm to be { 45, 53, 33, 53, 28 }, where 2 of the numbers had to be reduced such that they're within the 25% threshold of the new total (212 = 45+53+33+53+28) while the others remained untouched. Note that even though initially, the 2nd weight of 100 was only 14% of the total (706), as a result of decreasing the 4th weight of 500, it subsequently pushed up the % of the other observations and therein lies the only challenge with this.
I tried to recreate this using Solver only for it to tell me that it is the solution is "Infeasible" and it just returns all 1s. Update: solution need not use Solver, any alternative is welcome so long as it is fast when dealing with a decent number of weights.
var solver = SolverContext.GetContext();
var model = solver.CreateModel();
var decisionList = new List<Decision>();
decisionList.Add(new Decision(Domain.IntegerRange(1, 45), "Dec1"));
decisionList.Add(new Decision(Domain.IntegerRange(1, 100), "Dec2"));
decisionList.Add(new Decision(Domain.IntegerRange(1, 33), "Dec3"));
decisionList.Add(new Decision(Domain.IntegerRange(1, 500), "Dec4"));
decisionList.Add(new Decision(Domain.IntegerRange(1, 28), "Dec5"));
model.AddDecisions(decisionList.ToArray());
int weightLimit = 25;
foreach (var decision in model.Decisions)
{
model.AddConstraint(decision.Name + "weightLimit", 100 * (decision / Model.Sum(model.Decisions.ToArray())) <= weightLimit);
}
model.AddGoal("calcGoal", GoalKind.Maximize, Model.Sum(model.Decisions.ToArray()));
var solution = solver.Solve();
foreach (var decision in model.Decisions)
{
Debug.Print(decision.GetDouble().ToString());
}
Debug.Print("Solution Quality: " + solution.Quality.ToString());
Any help with this would be very much appreciated, thanks in advance.
I ditched Solver b/c it didn't live up to its name imo (or I didn't live up to its standards :)). Below is where I landed. Because this function gets used many times and on large lists of input weights, efficiency and performance are key so this function attempts to do the least # of iterations possible (let me know if anyone has any suggested improvements though). The results get used for a weighted average so I use "AttributeWeightPair" to store the value (attribute) and its weight and the function below is what modifies the weights to be within the constraint when given a list of these AWPs. The function assumes that weightLimit is passed in as a %, e.g. 25% gets passed in as 25, not 0.25 --- ok I'll stop stating what'll be obvious from the code - so here it is:
public static List<AttributeWeightPair<decimal>> WeightLimiter(List<AttributeWeightPair<decimal>> source, decimal weightLimit)
{
weightLimit /= 100; //convert to percentage
var zeroWeights = source.Where(w => w.Weight == 0).ToList();
var nonZeroWeights = source.Where(w => w.Weight > 0).ToList();
if (nonZeroWeights.Count == 0)
return source;
//return equal weights if given infeasible constraint
if ((1m / nonZeroWeights.Count()) > weightLimit)
{
nonZeroWeights.ForEach(w => w.Weight = 1);
return nonZeroWeights.Concat(zeroWeights).ToList();
}
//return original list if weight-limiting is unnecessary
if ((nonZeroWeights.Max(w => w.Weight) / nonZeroWeights.Sum(w => w.Weight)) <= weightLimit)
{
return source;
}
//sort (ascending) and store original weights
nonZeroWeights = nonZeroWeights.OrderBy(w => w.Weight).ToList();
var originalWeights = nonZeroWeights.Select(w => w.Weight).ToList();
//set starting point and determine direction from there
var initialSumWeights = nonZeroWeights.Sum(w => w.Weight);
var initialLimit = weightLimit * initialSumWeights;
var initialSuspects = nonZeroWeights.Where(w => w.Weight > initialLimit).ToList();
var initialTarget = weightLimit * (initialSumWeights - (initialSuspects.Sum(w => w.Weight) - initialLimit * initialSuspects.Count()));
var antepenultimateIndex = Math.Max(nonZeroWeights.FindLastIndex(w => w.Weight <= initialTarget), 1); //needs to be at least 1
for (int i = antepenultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[antepenultimateIndex - 1]; //set cap equal to the preceding weight
}
bool goingUp = (nonZeroWeights[antepenultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight)) > weightLimit ? false : true;
//Procedure 1 - find the weight # at which a cap would result in a weight % just UNDER the weight limit
int penultimateIndex = antepenultimateIndex;
bool justUnderTarget = false;
while (!justUnderTarget)
{
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1]; //set cap equal to the preceding weight
}
var currentMaxPcntWeight = nonZeroWeights[penultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight);
if (currentMaxPcntWeight == weightLimit)
{
return nonZeroWeights.Concat(zeroWeights).ToList();
}
else if (goingUp && currentMaxPcntWeight < weightLimit)
{
nonZeroWeights[penultimateIndex].Weight = originalWeights[penultimateIndex]; //reset
if (penultimateIndex < nonZeroWeights.Count() - 1)
penultimateIndex++; //move up
else break;
}
else if (!goingUp && currentMaxPcntWeight > weightLimit)
{
if (penultimateIndex > 1)
penultimateIndex--; //move down
else break;
}
else
{
justUnderTarget = true;
}
}
if (goingUp) //then need to back up a step
{
penultimateIndex = (penultimateIndex > 1 ? penultimateIndex - 1 : 1);
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1];
}
}
//Procedure 2 - increment the modified weights (subject to a cap equal to their original values) until the weight limit is hit (allowing a very slight overage for the last term in some cases)
int ultimateIndex = penultimateIndex;
var sumWeights = nonZeroWeights.Sum(w => w.Weight); //use this counter instead of summing every time for condition check within loop
bool justOverTarget = false;
while (!justOverTarget)
{
for (int i = ultimateIndex; i < nonZeroWeights.Count(); i++)
{
if (nonZeroWeights[i].Weight + 1 > originalWeights[i])
{
if (ultimateIndex < nonZeroWeights.Count() - 1)
ultimateIndex++;
else justOverTarget = true;
}
else
{
nonZeroWeights[i].Weight++;
sumWeights++;
}
}
if ((nonZeroWeights.Last().Weight / sumWeights) >= weightLimit)
{
justOverTarget = true;
}
}
return nonZeroWeights.Concat(zeroWeights).ToList();
}
public class AttributeWeightPair<T>
{
public T Attribute { get; set; }
public decimal? Weight { get; set; }
public AttributeWeightPair(T attribute, decimal? count)
{
this.Attribute = attribute;
this.Weight = count;
}
}

More efficient looping? [closed]

Closed. This question is off-topic. It is not currently accepting answers.
Want to improve this question? Update the question so it's on-topic for Stack Overflow.
Closed 9 years ago.
Improve this question
So I'm writing a simple struct to act like an Array of strings but with some handy operators and other functions that I've always wanted to see in strings. Specifically the method I'm working on right now is the / operator. The problem is, it won't add on any remainders at the end like I want it to.
What it's supposed to do, is take an array of strings, like {"Hello", "Test1", "Test2", "Goodbye", "More?", "Qwerty"} and, say I want to divide by 4, it should return { {"Hello", "Test1", "Test2", "Goodbye"}, {"More?", "Qwerty"} } but it doesn't.
The whole class (the method I want to improve is the / operator, but if you see anything else I can work on please point it out) (I know barely any of it is commented. Sorry about that, didn't expect anyone else to see this code aside from me.):
public struct StringCollection
{
private String[] value;
public StringCollection(params String[] s)
{
this.value = s;
}
public StringCollection(StringCollection current, String ad)
{
if (current.value == null) {
current.value = new String[0] { };
}
this.value = new String[current.value.Length+1];
for (int i=0; i<this.value.Length; i++)
{
try {
this.value[i] = current[i];
} catch {
break;
}
}
this.value[this.value.Length-1] = ad;
}
public StringCollection(StringCollection x, params StringCollection[] y)
{
this.value = x.value;
for (int j=0;j<y.Length;j++)
{
for (int i=0;i<y[j].value.Length;i++)
{
this += y[j][i];
}
}
}
public static StringCollection[] operator /(StringCollection x, int y)
{
StringCollection[] result = null;
if (((int)x.value.Length/y) == ((double)x.value.Length)/y)
result = new StringCollection[y];
else
result = new StringCollection[y+1];
for (int j=0;j<y;j++)
{
for (int i=0;i<((int)x.value.Length/y);i++)
{
result[j] += x.value[i+(int)((x.value.Length/y)*j)];
}
}
if (((int)x.value.Length/y) != ((double)x.value.Length)/y)
{
// This is the part that isn't working.
for (int i=0;i<(((int)x.value.Length/y)*result[0].value.Length)-x.value.Length;i++)
{
result[result.Length-1] += x.value[i+((result[0].value.Length)*result.Length-2)];
}
}
return result;
}
public String this[int index]
{
get {
return this.value[index];
}
set {
this.value[index] = value;
}
}
}
What it does is basically takes your array (single array) and splits it into a bunch of arrays that are the same size, then it adds on the remainder in a new array at the end.
Firstly your question isn't really related to loops at all, or at least loops are only addressed in your code. You should have titled this differently.
Secondly your array adding/removing could be improved; i.e. adding 1 to array size every time and removing 1 then re-copying the entire array every time is a time-sink.
Now onto your question, your code should basically look like this:
//Make your return array
int retLen = x.Length / y;
//Add space for the remainder
if(x.Length % y != 0)
retLen++;
var ret = new StringCollection[retLen];
//Reusing variables is a good way to save memory, but watch naming conventions as this can be confusing
retLen = 0;
var tempCollection = new StringCollection();
for (int i = 0; i < x.Length; i++)
{
tempCollection = new StringCollection(tempCollection, x[i]);
if(i % y == 0 || i == x.Length - 1)
{
ret[retLen++] = tempCollection;
tempCollection = new StringCollection();
retLen = 0;
}
}
return ret;
I really don't like that you don't have a Add function in this struct, just so we're clear. the tempCollection = new StringCollection(tempCollection, x[i]); is f$*kin' TERRIBLE when it comes to time CPU time to create all those new objects.
Pretty sure you'll need to tweak that to make sure all items are entered properly, but that was a first attempt, so ... meh o.O Figured since no one was actually going to answer you I'd take the time.
EDIT: Found a bug, forgot to set retLen back to 0 when adding to ret

How to iterate through two dimensional array

I have two list that I convert them to a single two dimesnional array like this:
double[,] data = new double[voltage.Count(), 2];
for (int i = 0; i < voltage.Count(); i++)
{
data[i, 0] = voltage[i];
data[i, 1] = current[i];
}
Now I am trying to itterate through this array but what I get is same value for both voltage and current in each line:
foreach (double data in ztr.GetCurveDataForTestType()) //this will return my array
{
richTextBox1.AppendText("Voltage" + data + " --------- ");
richTextBox1.AppendText("Current" + data + "\r\n");
}
Voltage-0.175 --------- Current-0.175
Voltage-9.930625E-06 --------- Current-9.930625E-06
Voltage-0.171875 --------- Current-0.171875
Voltage-9.53375E-06 --------- Current-9.53375E-06
Voltage-0.16875 --------- Current-0.16875
As you see in the first line both voltage and current are same value that is voltage, and on the second raw they are the same again but this time it is the current value. How can I fix this?
I'd recommend not using multi-dimensional arrays for this.
Instead you can make a class something like this:
class Measurement
{
public double Voltage { get; set; }
public double Current { get; set; }
}
And change your method to return IEnumerable<Measurement>. In .NET 4.0 you can use Zip:
public IEnumerable<Measurement> GetCurveDataForTestType()
{
return voltage.Zip(current,
(v, c) => new Measurement { Voltage = v, Current = c});
}
And for older versions of .NET:
public IEnumerable<Measurement> GetCurveDataForTestType()
{
for (int i = 0; i < voltage.Count(); i++)
{
yield return new Measurement
{
Voltage = voltage[i],
Current = current[i]
};
}
}
Then your code becomes:
foreach (Measurement data in ztr.GetCurveDataForTestType())
{
richTextBox1.AppendText(
"Voltage: {0} --------- Current: {1}", data.Voltage, data.Current);
}
You use the same kind of loop as when you created the array:
for (int i = 0; i < data.GetLength(0); i++) {
richTextBox1.AppendText("Voltage" + data[i, 0] + " --------- ");
richTextBox1.AppendText("Current" + data[i, 1] + "\r\n");
}
Update:
This question appears to be a duplicate of How to foreach through a 2 dimensional array?. The linked answer has an alternate solution that enables the use of foreach by switching from a two-dimensional array to an array of array.
foreach with a two dimensional array, will sequentially return every double value in the array (by design).
For example, given the following two-dimensional array of doubles:
var array = new double[,] { { 0, 1, }, { 2, 3 }, { 4, 5 } };
foreach (var d in array)
{
Console.WriteLine(d);
}
You will get the following Console output:
0
1
2
3
4
5
Therefore, foreach is not appropriate for you needs. You should use a for loop.

Categories

Resources