I have a double[] array holding many numbers.
I have an algorithm that selects sections from within this array that fall under certain conditions (value greater than x, at least y values, etc.)
Now I want to calculate the average value of all these values in my section.
So, say my section is from index 20 to 40. Now I have 20 values. Is there an easy way to do this in C# or do I have to loop over my array and calculate the average by hand?
var values = new[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var average = values.Skip(2).Take(5).Average();
Use Linq:
var myList = new double[] {1,2,3}
var avg = myList.Where(i => i > 1 && i < 2).Avg();
Note that if you have the numbers with index 20 to 40, you don't have 20 numbers, you have 21 numbers.
You can use the Range method to create an IEnumerable for the indexes, then you can use the Average method to get the average of the numbers:
double average = Enumerable.Range(20, 21).Select(i => numbers[i]).Average();
double avg = array
.Skip(startIndex)
.Take(endIndex - startIndex + 1)
.Average();
Use Enumerable.Average:
double[] values = new[] { 1.0, 2.0, 3.14, 2.71, 9.1 };
double average = values.Where(x => x > 2.0 && x < 4.0).Average();
Therefore, to use this with your selection methods you should consider having those methods return IEnumerable<double>. So, as an example:
public IEnumerable<double> GreaterThan(double[] values, double value) {
for(int i = 0; i < values.Length; i++) {
if(values[i] > value) {
yield return values[i];
}
}
Then:
// values is double[]
double average = GreaterThan(values, 2.0).Average();
You can even make the above an extension method so that it reads nicely:
double average = values.GreaterThan(2.0).Average();
I would encourage you to write your filtering methods to return IEnumerable<double>.
You can use the skip and take methods of linq to choose particular indexes:
var myData = new double[] { ..... };
var average = myList.Skip(20).Take(21).Average();
Related
I am trying to split a number into smaller numbers that fit predefined ranges and I can't seem to get the algorithm right. I am using C#.
Example
Split 20 into three numbers where the numbers have to fit the following ranges: 1-3, 3-10, and 0-15. Final numbers could look like this: 1,5,14 or 2,3,15
Another example could be to split 100 into four numbers that fit the following ranges: 0-10, 0-10, 0-40, 0-40. The result would naturally be 10,10,40,40. Splitting 90 on the same ranges could result 5,8,38,39 etc.
Can you kick me in the right direction?
(no, it's not a homework, it's for a personal project)
You can do it using recursion.
The idea of the algorithm is something like this:
In every execution you're going to iterate through all possible numbers of the interval.
Calls recursive to generate the next number of the next interval.
If at any time the sum passes the desired value then backtracks.
Once all the numbers are generated, if the sum is equal to the desired number then you have a possible combination.
It can be improved but it is a solution.
The following code prints all valid sequences in the console:
SplitNumber(100, new Interval[]
{
new Interval { Min = 0, Max = 11 },
new Interval { Min = 0, Max = 11 },
new Interval { Min = 0, Max = 40 },
new Interval { Min = 0, Max = 40 },
});
public static void SplitNumber(int n, Interval[] intervals)
{
SplitNumber(n, 0, intervals, "");
}
public static void SplitNumber(int n, int k, Interval[] intervals, string s)
{
if (n < 0) return;
if (k >= intervals.Length) { if (n == 0) Console.WriteLine(s); }
else
for (int i = intervals[k].Min; i <= intervals[k].Max; i++)
SplitNumber(n - i, k + 1, intervals, string.Format("{0} {1}", s, i));
}
Interval class is something like this:
public class Interval
{
public int Min { get; set; }
public int Max { get; set; }
}
The following describes a pretty efficient approach, assuming that the buckets have some sort of ordering.
Start by choosing the minimum value for each range and adding them up.
If the sum is equal to your number, then stop.
If the sum is greater then your number, then issue an error.
If the sum is less than your number, then continue.
Next, subtract the minimum value from each range so they are all normalized on 0 . . n and subtract the sum from your number. This isn't strictly necessary, but it helps with the explanation of the rest of the algorithm.
Next, do a cumulative sum of the max range values. Find the bucket where your new sum fits in (too big for the previous bucket but fits in). If none are found, then issue an error.
Then allocate the bins so the preceding buckets are at their maximum and set the one found to the appropriate value.
This gives you one set of values that meet your conditions.
If you want values more in the "middle" of the ranges, then start with the middle value of the ranges. Then add or subtract values in chunks across all buckets, until you hit the maximum. This requires a little more iteration, but it is also quite efficient.
Try this:
List<KeyValuePair<int, int>> ranges = new List<KeyValuePair<int, int>>();
ranges.Add(new KeyValuePair<int, int>(1, 3));
ranges.Add(new KeyValuePair<int, int>(1, 3));
ranges.Add(new KeyValuePair<int, int>(1, 100));
int totalSum = ranges.Sum(i => i.Value - i.Key);
double ws = 0.0;
int rIndex = 0;
var rangeAndWeight = ranges.Select(i => new { index = rIndex++, range = i, maxw = (ws += (double)(i.Value - i.Key) / totalSum) }).ToList();
int[] nums = ranges.Select(i => i.Key).ToArray();
int number = 50;
Random r = new Random();
while (nums.Sum() != number)
{
double rDouble = r.NextDouble();
var index = rangeAndWeight.SkipWhile(i => i.maxw < rDouble).First().index;
if (nums[index] < ranges[index].Value)
nums[index] += 1;
}
nums array contains smaller numbers you need
You could write a programm that just tries to sum up possible numbers in the allowed range and hopes that the result is correct and backtracks if the result is wrong. Its pretty inefficent tho ...
There are a bunch of answers on how to get a subarray with start and end index or start index and length. But I m looking for a way to get an subarray based on index array.
Here is what I have (which works fine but seems clunky).
//sample arrays (could be unordered)
double[] expiry= { 0.99, 0.9, 0.75, 0.60, 0.5, 0.4, ...};
double[] values = { 0.245, 0.24, 0.235, 0.22, 0.21, 0.20, ... };
//find index of all elements meeting criteria in array expiry
int[] expind = expiry
.Select((b, i) => b == 0.75? i : -1)
.Where(i => i != -1).ToArray();
//create new arrays of appropriate size
double[] newvalues = new double[expind.Length];
//populate new arrays based on index
for (int i = 0; i < expind.Length; i++)
newvalues[i] = values[expind[i]];
//check values
foreach (var item in newvalues)
Console.WriteLine(item);
Is there a more efficient and general way of doing this please?
UPDATE
next attempt (Still not super efficient, but at least loopless):
Array.Sort(expiry, values);
double criteria = 0.75;
int start = Array.IndexOf(expiry, criteria);
int last = Array.LastIndexOf(expiry, criteria);
int length = last - start + 1;
double[] newvalues2 = new double[length];
Array.Copy(values, start, newvalues2, 0, length);
Hi you can find the values in this way using lambda expression:
double[] newVals = values.Where((t, i) => expiry[i] == 0.75).ToArray();
This is a bit more concise. no need to actually put the indexes into an expind array; just use the indices directly with the Where() overload that takes an index:
double[] newpoints = points.Where((p, i) => (expiry[i] == 0.75)).ToArray();
double[] newvalues = values.Where((v, i) => (expiry[i] == 0.75)).ToArray();
See deeper discussion.
Now, if for some reason you already have an array of expind indices, but not the original array of expiry it came from, you can do this:
double[] newpoints = expind.Select(ind => values[ind]).ToArray();
Depending on the circumstances, this might work for you.
private static IEnumerable<double> GetByCondition(List<double> expiry, List<double> value)
{
for(int i = 0; i < expiry.Count; i++)
if(expiry[i] == 0.75)
yield return value[i];
}
Furthermore, I'd put it as a extension method, if frequently used in your arrays/lists.
public static IEnumerable<double> GetValuesByExpiry(
this List<double> self, List<double> values)
{
return GetByCondition(self, values);
}
As #Corak mentioned, the problem might be eliminated all together if you merge those two arrays into a single one consisting of touples. If appropriate in your case, of course. You can probably zip them together.
I am using C# and i am trying to find the the average of 5 values but i can only use 2 variables.
How can you input 5 integers into one variable and display the average of said integers
You can use List like this:
var list = new List<int>(){ 1, 2, 3, 4, 5 };
var average = list.Average();
using Average you'll get average of all values in list
Here you have all functions of Enumerable, you can for e.g. sum all values with Sum
Use a collection like a List<int> and the extension method Enumerable.Average:
List<int> numbers = new List<int>{ 10, 20, 30, 40, 50 };
double average = numbers.Average(); // 30.0
Use List.Add to add single integers:
numbers.Add(1);
numbers.Add(2);
numbers.Add(3);
// ...
Take the input values in a Integer list or array then use the following code
List<int> intlist=new List<int>();
intlist.Add(2);
intlist.Add(3);
..
..
var average= intlist.Average();
Using Average will computes the average of a sequence of all the integers in the list.
UPDATE: or if the case is to use integers only then you need to use the following code (Remember to validate the readline() entries)
public decimal Average()
{
int value = 0;
for(int i=0;i<5;i++)
{
value+=ConvertToInt32(Console.ReadLine());
}
return value/5;
}
What about using array? I think array is one variable in your case
int[] input = new int[5];
input[0] = 5;
input[1] = 40;
input[2] = 15;
input[3] = 50;
input[4] = 25;
int sum = 0;
foreach(int i in input)
{
sum = sum + i;
}
sum = sum / input.Length;
Console.WriteLine(sum.ToString());
#up Yeah that's better way!
You dont need arrays, or lists or anything remotely similar. Pseudo-code:
private int sum = 0;
private int count = 0;
while (user inputs valid number)
{
sum += userInput;
count++;
}
return sum / count;
Only two variables.
If you just want solution without List<int> then here it is
int[] arr=new int[5];
arr[0]=10;arr[1]=20;...arr[4]=50;
int sum=0;
foreach(int x in arr)
{
s+=x;
}
s=s/arr.Length;//s is average
If you want list
List<int> list = new List<int>(){ 1, 2, 3, 4, 5 };
var average = list.Average();
I am trying to figure out the best way to find the closest value, ROUNDED DOWN, in a List of integers using any n that is between two other numbers that are stored in a List. The all integers in this situation will ALWAYS be unsigned, in case that helps.
The assumptions are as follows:
The List always starts at 0
The List is always sorted ASC
All integers in the List are unsigned (no need for Math.Abs)
The number for comparison is always unsigned
For example:
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int theAnswer; // should be 8500
for (int i = 0; i < numbers.Count; i++) {
if (i == numbers.Count - 1) {
theAnswer = numbers[i];
break;
} else if (myNumber < numbers[i + 1]) {
theAnswer = numbers[i];
break;
}
}
The previous code example works without any flaws.
Is there a better more succint way to do it?
You can use List<T>.BinarySearch instead of enumerating elements of list in sequence.
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int r=numbers.BinarySearch(myNumber);
int theAnswer=numbers[r>=0?r:~r-1];
Filter list obtaining all values less than the myNumber and return last one:
theAnswer = numbers.Where(x => x <= myNumber ).Last();
A list can be indexed.
Start at the index in the middle of the list. If you found the exact number, you are good. If the number is less than the target number, search in the middle of the range from the start of the list to one less than the middle of the list. If the number is greater than the target number, work with the opposite half of the list. Continue this binary search until you find either an exact match, or the adjacent numbers that are smaller and larger than the target number.
Select the smaller of the two.
Please try this code:
List<int> numbers = new List<int>() { 0, 2000, 4000, 8000, 8500, 9101, 10010 };
int myNumber = 9000;
int theAnswer = numbers[numbers.Count - 1];
if (theAnswer > myNumber)
{
int l = 0, h = numbers.Count - 1, m;
do
{
m = (int)((double)(myNumber - numbers[l]) / (double)(numbers[h] - numbers[l]) * (h - l) + l);
if (numbers[m] > myNumber) h = m; else l = m;
}
while ((h - l) != 1);
theAnswer = numbers[l];
}
I have a list of double values, I want to Round a variable's value to only that list of numbers
Example:
The list contents are: {12,15,23,94,35,48}
The Variable's value is 17, So it will be rounded to 15
If The variable's value is less than the least number, it will be rounded to it, if it's value is larger than the largest number, it will be rounded to it.
The list contents is always changing according to an external factor, So I cannot hardocde the values I Want to round up or down to.
How can do it in C# ?
Here's a method using LINQ:
var list = new[] { 12, 15, 23, 94, 35, 48 };
var input = 17;
var diffList = from number in list
select new {
number,
difference = Math.Abs(number - input)
};
var result = (from diffItem in diffList
orderby diffItem.difference
select diffItem).First().number;
EDIT: Renamed some of the variables so the code is less confusing...
EDIT:
The list variable is an implicitly declare array of int. The first LINQ statement diffList defines an anonymous type that has your original number from the list (number) as well as the difference between it and your current value (input).
The second LINQ statement result orders that anonymous type collection by the difference, which is your "rounding" requirement. It takes the first item in that list as it will have the smallest difference, and then selects only the original .number from the anonymous type.
Assuming the array is sorted, you could perform a binary search in the array, narrowing it down to which two numbers the given number lies between.
Then once you have these two numbers, you simply round to the nearest of the two.
static int RoundToArray(int value, int[] array) {
int min = 0;
if (array[min] >= value) return array[min];
int max = array.Length - 1;
if (array[max] <= value) return array[max];
while (max - min > 1) {
int mid = (max + min) / 2;
if (array[mid] == value) {
return array[mid];
} else if (array[mid] < value) {
min = mid;
} else {
max = mid;
}
}
if (array[max] - value <= value - array[min]) {
return array[max];
} else {
return array[min];
}
}
Using linq:
int value = 17;
var values = new float[] { 12, 15, 23, 94, 35, 48 };
if(value < values.First()) return value.First();
if(value > values.Last()) return value.Last();
float below = values.Where(v => v <= value).Max();
float above = values.Where(v => v >= value).Min();
if(value - below < above - value)
return below;
else
return above;
As long as then number of possible values is quite small this should work. If you have thousands of possible values another solution should be used, which takes advantage of values being sorted (if it really is sorted).
You could loop through the array of numbers and set a roundedNum variable equal to each one if a variable delta is less than the current lowest delta. Some things are best described in code.
int roundedNum = myNum;
int delta = myArray[myArray.Length-1] + 1;
for(int i=0; i<myArray.Length; ++i) {
if(Math.Abs(myNum - myArray[i]) < delta) {
delta = Math.Abs(myNum - myArray[i]);
roundedNum = myArray[i];
}
}
That should do the trick quite nicely.
Do something like this:
double distance = double.PositiveInfinity;
float roundedValue = float.NaN;
foreach (float f in list)
{
double d = Math.Abs(d - f);
if (d < distance)
{
distance = d;
roundedValue = f;
}
}
Simple rounding down in linq
public decimal? RoundDownToList(decimal input, decimal[] list)
{
var result = (from number in list
where number <= input
orderby number descending
select number).FirstOrDefault();
return result;
}