Find next highest and next lowest number in list c# - c#

I have a list of numbers and if the number that I am looking for is not in the list I want to be able to find the next value that is in the list above and below the number that I have asked for.
double index = 5;
List<double> list = new List<double>() { 1, 2, 3, 7, 8, 9, };
//do code to find next highest/lowest
double higher = 7;
double lower = 3;
for example, because 5 is not exactly in the list itself I want it to return the next item that is closest to 5 both above and below. so for this case, 3 and 7.

Original version
You can use List's Sort() method, then use LINQ FirstOrDefault() and LastOrDefault()
List<double> numbers = new List<double>()
{
1, 2, 3, 7, 8, 9
};
double input = 5;
numbers.Sort();
double nextHighest = numbers.FirstOrDefault(x => x > input);
double nextLowest = numbers.LastOrDefault(x => x < input);
Better version edit This wont actually work for both cases
Originally I suggested this as a more efficient solution, but this will not work for the number before a given input. It only works for finding a number after the given input.
You can be more efficient since you are using List<T>, there is a Find() method that accepts a predicate:
List<double> numbers = new List<double>()
{
1, 2, 3, 7, 8, 9
};
double input = 5;
numbers.Sort();
double nextHighest = numbers.Find(x => x > input);
This will exit the loop immediately upon finding a match (whereas LastOrDefault() has to iterate the entire collection)
Fiddle for both here

One solution would be sort the list and then iterate over it to find the spot where list[i] < index < list[i+1]. This would take O(n * log(n)) time.
A better solution is simply iterate over the list and use 2 variables that you will update accordingly (let's say max and min).
max will store the maximum value that is below the index.
min will store the minimum value that is above the index.
That last solution takes O(n) time.

LinQ-Solution:
double index = 5;
List<double> list = new List<double>() { 1, 2, 3, 7, 8, 9, };
double higher=list.OrderBy(q=>q).First(q=>q>index); // 7
double lower=list.OrderByDescending(q=>q).First(q=>q<index); // 3
Explanation: First you sort the list Ascending or Descending. After that all you have to do is to find the greater or smaller number;
You might want to replace First() by FirstOrDefault() if you do not want an exception if no higher/lower value exists.

In case you want all the occurency
double index = 5;
List<double> list = new List<double>() { 1, 2, 3, 7, 8, 9, };
for(int i =1 ; i <= 9-5 ; i++)
{
List<double> NextHigh = list.Where(x=> x-i == index).ToList();
if(! (NextHigh.Count == 0))
{
NextHigh.Dump();
break;
}
}
for(int i =1 ; i <= 5-1 ; i++)
{
List<double> NextLow = list.Where(x=> x+i == index).ToList();
if( !(NextLow.Count== 0))
{
NextLow.Dump();
break;
}
}

The other answer given is not very good if you are going to be using a large list of values, because it needs to index the list twice.
If you make your own loop like this:
double higher = Double::MaxValue;
double lower = Double::MinValue;
double index = 5; // the value you want to get.
List<double> list = {1,2,3,7,8,9}; // the list you're searching.
for(int i = 0; i < list.Count; i++)
{
if(list[i] > index && list[i] < higher)
higher = list[i]; // sets the higher value.
else if(list[i] < index && list[i] > lower)
lower = list[i]; // sets the lower value.
}
After execution, this would give you 3 and 7, the correct values.
I should note that this takes O(n) time, because it only loops once. This will be faster than anything posted at the time of this edit (plus it's pretty easy to see what its doing).

You can archieve that by this way. These codes below was tested from my side.
double index = 5;
double higher = 0 ;
double lower = 0;
List<double> list = new List<double>() { 1, 2, 3, 7, 8, 9, };
list.Sort();//Sort list from low to high numbers
if(!list.Contains(index))
{
foreach (var item in list)
{
if(item < index)
{
lower = item;
}
if (item > index)
{
higher = item;
break;
}
}
}
Console.WriteLine(index);//5
Console.WriteLine(lower);//3
Console.WriteLine(higher);//7

Related

find the highest value that is less than a given number in a sorted list

I want to find the find the highest value that is less than a given number to a specified number in a sorted list of integers.
I have the following code
List<int> list = new List<int> { 2, 5, 7, 10 };
int number = 9;
In the above example the expected outcome is 7.
I do
int closestSmaller = list.Aggregate((x,y) => Math.Abs(x-number) < Math.Abs(y-number) ? x : y);
But it returns 10.
My list has hundreds of thousands of numbers. The above was just a sample.
As the list is sorted, you can use Array.BinarySearch to get at the relevant location quickly. A bit tricky to evaluate the return value in case you don't hit an element exactly.
List<int> list = new List<int> { 2, 5, 7, 10 };
int number = 1;
var index = Array.BinarySearch(list.ToArray(), number);
if (index < 0)
{
index = ~index - 1;
if (index >= 0)
Console.WriteLine(list[index]);
else
Console.WriteLine("less than all elements in the list");
}
else
{
Console.WriteLine(list[index]);
}
var result = list.Where(x => x < number).LastOrDefault();

Finding Highest and Lowest value in Array and removing them to compute an average C#

I am trying to write code that finds the lowest and highest values stored in an array and then removes them from the array to compute an average.
Currently I have written code to produce the average of all numbers in the array but I need to change that once I figure out how to remove Highest and lowest value.
Code I have:
private void HighAndLow()
{
try
{
int[] HighAndLowGrade;
int[] highest = HighAndLowGrade.Max();
int lowest = HighAndLowGrade.Min();
}
catch
{
MessageBox.Show("HighAndLow Method failed");
}
}
//find average without highest and lowest values
private void ComputeMean()
{
double total = 0;
for (int index = 2; index < 9; index ++)
{
total += double.Parse(lineContent[index]);
}
averageTestScore = total / 7;
}
This should work from what I have tested so far.
int[] numberArray = new int[] {1,2,5,9,5,2};
double answer = 0;
var ignoreList = new List<decimal>() {
numberArray.Max(),
numberArray.Min()
};
var cleanList = numberArray.Where(x => !ignoreList.Contains(x));
answer = cleanList.Any() ? cleanList.Average() : 0;
This only requires one iteration through the collection:
public double ComputeAdjustedMean(IEnumerable<int> items)
{
int total = 0;
int count = 0;
int min = int.MaxValue;
int max = int.MinValue;
foreach(int item in items)
{
count++;
total += item;
if (item < min) min = item;
if (item > max) max = item;
}
if (count <= 2) // not enough items
{
// do something here
}
return (total - (min + max)) / (double)(count - 2);
}
Try this using bubble sorting algorithm-
static void Main(string[] args)
{
int[] array = { 12, 6, 34, 23, 89 };
int temp;
for (int i = 0; i <= array.Length - 2; i++)
{
if (array[i] > array[i + 1])
{
temp = array[i];
array[i] = array[i + 1];
array[i + 1] = temp;
}
}
array = array.Skip(1).SkipLast(1).ToArray();
Console.WriteLine((array.Sum()) / (array.Length));
Console.Read();
}
If you have an array of values then you can do this neat LINQ query:
var average_skip_min_and_max =
values
.OrderBy(x => x)
.Skip(1)
.Take(values.Length - 2)
.Average();
I really don't get people when they encounter this kind of questions, they became insanely eager to provide a direct answer. This question is obviously a homework assignment. I'm not saying we don't help OPs but we need to lead them to solutions.
Dear OP,
Do not use the LINQ, yet. I think your instructor is meaning you to learn the sorting algorithms and memory operations. Do some research about them, say, Buble Sort, to sort the array you have. Then it'll be in front of you how to implement and use. After then, you should use the framework provided methods like LINQ's Min() / Max() extension methods.
The approach to your problem is could be like this:
Sort the array ascending.
Get the first element which is now the minimum valued element.
Reallocate a new array but 1 element shorter
Copy your entire array with the current ordered state to newly allocated array but skip the first element when copying, start with next element.
Get the minimum again, but this time search in the newly allocated array and check with the previous minimum
If they are equal go the 3rd operation, if you need to eliminate the repeating minimums ( [1, 1, 2, 3 ...] ), which I think you need to.
If they are not equal, then it means you've found the minimum element of your array and removed all occurences
Now if you repeat the approach to finding the maximum valued element you are done with the elimination process.

Sum of Array's Subsets

Im looking for some help with finding subsets of array.
int[] array = { 1,2,3,5,8,10,15,23};
I have to find all subsets of an array. If sum of the subsets elements equal to any number in array then my counter increment. For example: 1+2=3, 2+3=5, 5+8+10=23, 1+2+5=8, 2+3+8+10=23
public static void Main(string[] args)
{
int[] array = { 1, 2, 3, 5, 8, 10, 15, 23 };
int arrayLength = array.Count();
int sum = 0;
int subsetCount = 0;
for (int i = 0; i < arrayLength; i++)
{
for (int j = i + 1; j < arrayLength; j++)
{
sum = array[i] + array[j];
for (int m = j + 1; m < arrayLength; m++)
{
for (int k = 0; k < arrayLength; k++)
{
if (array[k] == sum)
{
subsetCount++;
}
}
sum = array[i] + array[j] + array[m];
}
}
}
Console.WriteLine(subsetCount);
Console.ReadLine();
}
I'm ok with 2-elements and 3-elements of subsets. But 4 and above I can't figured out how to solve it?
Any help would be greatly appreciated
You only need two loops to find the sum of all subsets. The outer loop is the starting point of subsets, and the inner loop is calculating the sums of all subsets from that starting point.
With the first index as starting points the subsets are 1+2, 1+2+3, 1+2+3+5 and so on. As you are only interested in the sum of the subsets you can just add one item after the other to get the sum of the subsets.
Then for each sum loop through the items to check for a match:
int[] array = { 1, 2, 3, 5, 8, 10, 15, 23 };
int subsetCount = 0;
for (int i = 0; i < array.Length; i++) {
int sum = array[i];
for (int j = i + 1; j < array.Length; j++) {
sum += array[j];
for (int k = 0; k < array.Length; k++) {
if (array[k] == sum) {
subsetCount++;
}
}
}
}
Console.WriteLine(subsetCount);
Edit:
I assumed that you meant continuous subsets, but from your examples it seems that you also want non-continuous subsets.
Lets's start with the correct solution:
23 = 15+8, 15+5+3, 15+5+2+1, 10+8+5, 10+8+3+2
15 = 10+5, 10+3+2, 8+5+2
10 = 8+2, 5+3+2
8 = 5+3, 5+2+1
5 = 3+2
3 = 2+1
That gives us 14 different subsets that sums up to an item in the set.
You can count the subsets recursively, only keeping track of the sum and number of items in subsets. You don't need the actual subsets, only to know the sum and that there are at least two items in the subset.
The subsets in a set is the first item combined with all subsets in the rest of the set, plus the subsets in the rest of the set. For example the subsets s() of [1,2,3] is 1,s([2,3]) and s([2,3]).
This gives you:
public static int CountSubsets(int[] arr, int start, int len, int sum) {
int cnt = 0;
if (start < arr.Length) {
if (len >= 1 && arr.Contains(sum + arr[start])) cnt++;
cnt += CountSubsets(arr, start + 1, len + 1, sum + arr[start]);
cnt += CountSubsets(arr, start + 1, len, sum);
}
return cnt;
}
And calling it:
int[] set = { 1, 2, 3, 5, 8, 10, 15, 23 };
Console.WriteLine(CountSubsets(set, 0, 0, 0));
Output:
14
This seems a lot like homework to me. So I will answer in that spirit (i.e. rather than write the code, point you in the right direction).
First, it's not really clear what you mean by "subset". Are you talking about contiguous runs of elements from the array? Or do you literally mean treating the array as an unordered set, from which you examine every possible subset?
The latter is significantly harder than the former. So I'm going to assume the former for the moment.
Then, it seems you really have two different problems:
Find all subsets of the array and sum each one
For a given sum, determine whether it's in the array
The latter is fairly straightforward. If you know these arrays will always be relatively short (and hopefully they will be, otherwise "find all subsets" may take awhile :) ), you can just do a linear search every time you have a new sum to look for.
Alternatively, a more semantically straightforward approach would be to create a HashSet<int> instance once using the members of the array, and then when you want to know if the sum is in the array, just check your set.
For example:
HashSet<int> setOfValues = new HashSet<int>(array);
Then you can just write setOfValues.Contains(sum) to check whether the value held by the variable sum is contained in the array.
As for the first problem, it seems to me that you really should only need two loops:
A loop to iterate on the index of the first element of a subset. I.e. you need to enumerate all subsets, first starting with all subsets that start with element 0, then with all subsets that start with element 1, and so on.
A loop to iterate on the length of the subset. I.e. having determined the starting index for your current group of subsets, now you want to find all the actual subsets: the first has length 1, the second has length 2, and so on up to the maximum possible length (i.e. the length of the array minus the current 0-based starting index value).
Considering for a moment the alternative possibility — that you are treating the array as an unordered set — then it seems to me an obvious, if brute-force approach, would be to generate the subsets recursively.
In that approach, you would again have a loop to enumerate the subset lengths, starting with 1, up to the total length of the original set. Then, given a length, you need to pick all possible subsets.
You can do this recursively:
Given a set, iterate over the elements of the current set (passed in to your recursive method). The current element is the new element of your current subset.
If your current subset is now the correct length, sum it and compare to the original set.
Otherwise, remove the current element from the current set and recurse.
The easiest implementation of the above will create new copies of the current set for each level of recursion, to pass to the method when it calls itself. This way you don't have to worry about one level of recursion interfering with previous levels.
Do note that this is going to be practical only for relatively small initial sets. It won't take very large initial arrays before your computer doesn't have enough time or memory to complete the search of all possible subsets.
First up, you need a method that will return all subsets.
Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
getAllSubsets = xs =>
(xs == null || !xs.Any())
? Enumerable.Empty<IEnumerable<int>>()
: xs.Skip(1).Any()
? getAllSubsets(xs.Skip(1))
.SelectMany(ys => new [] { ys, xs.Take(1).Concat(ys) })
: new [] { Enumerable.Empty<int>(), xs.Take(1) };
So given getAllSubsets(new[] { 1, 2, 3 }) I get:
{ }
{ 1 }
{ 2 }
{ 1, 2 }
{ 3 }
{ 1, 3 }
{ 2, 3 }
{ 1, 2, 3 }
Now's it's easy to compute the result you want.
int[] array = { 1,2,3,5,8,10,15,23};
var result =
getAllSubsets(array)
.Where(ss => array.Contains(ss.Sum()))
.Count();
I get 22.
Using a bit of Linq:
int[] array = {1, 2, 3, 5, 8, 10, 15, 23};
var subsets = new List<IEnumerable<int>>();
int counter = 0;
for (int i = 0; i < array.Length; i++)
{
for (int j = 2; j < array.Length - i; j++)
{
if (array.Contains(array.Skip(i).Take(j).ToList().Sum()))
{
counter++;
}
}
}
Console.WriteLine("Number of subsets:" + counter);
Gives you:
Number of subsets:5

Find the greatest right number from the current number in the array algorithm

My algorithm should find the greatest right number from the current number in an input array, for example, given the following int[] input:
5, 9, 6, 1, 3, 2
My algorithm will output:
9, 6, 3, 3, 2, 2
Here is my current code:
public static int[] FindGreatestRightNumber(int[] input)
{
var output = new int[input.Length];
for (var i = 0; i < input.Length; i++)
{
int maxRightNumber = (i == input.Length - 1 ? input[i] : 0);
for (var j = i+1; j < input.Length; j++)
{
var currentNumber = input[j];
if (maxRightNumber < currentNumber)
maxRightNumber = currentNumber;
}
output[i] = maxRightNumber;
}
return output;
}
I was told it could be much faster, how? any idea?
UPDATE: Please don't use LINQ in your answers, I would like to get familiar with faster ways to solve the problem using simple code, no LINQ, IEnumerable Extension Methods etc.
You can do this in a single pass from the right hand side. The trick is realizing maxRightVal(n) = max(maxRightVal(n+1), values(n+1)):
var output = new int[input.Length];
output[input.Length-1] = input[input.Length-1];
for(int i = input.Length - 2; i >= 0; i--)
output[i] = output[i+1] > input[i+1] ? output[i+1] : input[i+1];
Why not just using Enumerable.Max() method?
Returns the maximum value in a sequence of Int32 values.
int[] input = new int[] { 5, 9, 6, 1, 3, 2 };
int biggest = input.Max();
Console.WriteLine(biggest); // 9
Here is a DEMO.
Since, I see the question better now, VLad's answer looks the right one.
very simple if you want to skip some items and search the max
int[]arr = {5, 9, 6, 1, 3, 2};
int currentIndex = 2;
int currentValue = 6;
int max = arr.Skip(currentIndex).Where(f => f > currentValue).Max();
EDIT if you want to simply sort an array, then:
int[] sorted = arr.OrderByDescending();
Start from the (n-2)th elements, maintain a current max array which is initialised with nth element. Keep updating it if the current element is greater than the element in max array. Continue this until the first element is reached.
This takes the largest value to the right of each element;
int[] input = {5, 9, 6, 1, 3, 2};
int[] output = input
.Take(input.Length-1)
.Select( (x,i) => input.Skip(i+1).Max()).ToArray();

Efficient algorithm to find a combination, which summation is equal to a known number, in a set of number

Let's say there is a set of number
1, 2, 3, 4, 5, 6, 7, 8, 9, 10
I want to find out several combinations in the set of number such that the summation of it equal to a known number, for example, 18. We can find out that 5, 6, 7 is matched (5+6+7=18).
Numbers in a combination cannot be repeated and the number in a set may not be consecutive.
I've wrote a C# program to do that. The program is random to pick up number to form a combination and check whether the summation of combination is equal to a known number. However, the combination the program found may be repeated and it makes the progress not effective.
I am wondering whether there is any efficient algorithm to find out such combination.
Here's part of my code.
int Sum = 0;
int c;
List<int> Pick = new List<int>();
List<int> Target = new List<int>() {some numbers}
Target.Sort();
while (!Target.Contains(Sum))
{
if (Sum > Target[Target.Count - 1])
{
Pick.Clear();
Sum = 0;
}
while (true)
{
if (Pick.IndexOf(c = Math0.rand(0, Set.Count - 1)) == -1)
{
Pick.Add(c);
}
//Summation Pick
Sum = 0;
for (int i = 0; i < Pick.Count; i++)
Sum += Set[Pick[i]];
if (Sum >= Target[Target.Count - 1])
break;
}
}
Result.Add(Pick);
You can use recursion. For any given number in the set, find the combinations of smaller numbers that adds up to the number:
public static IEnumerable<string> GetCombinations(int[] set, int sum, string values) {
for (int i = 0; i < set.Length; i++) {
int left = sum - set[i];
string vals = set[i] + "," + values;
if (left == 0) {
yield return vals;
} else {
int[] possible = set.Take(i).Where(n => n <= sum).ToArray();
if (possible.Length > 0) {
foreach (string s in GetCombinations(possible, left, vals)) {
yield return s;
}
}
}
}
}
Usage:
int[] set = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
foreach (string s in GetCombinations(set, 18, "")) {
Console.WriteLine(s);
}
Output:
1,2,4,5,6,
3,4,5,6,
1,2,3,5,7,
2,4,5,7,
2,3,6,7,
1,4,6,7,
5,6,7,
1,2,3,4,8,
2,3,5,8,
1,4,5,8,
1,3,6,8,
4,6,8,
1,2,7,8,
3,7,8,
2,3,4,9,
1,3,5,9,
4,5,9,
1,2,6,9,
3,6,9,
2,7,9,
1,8,9,
1,3,4,10,
1,2,5,10,
3,5,10,
2,6,10,
1,7,10,
8,10,
A possible alternative method. With a small set like this, you could use brute force. Your set {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} has 10 elements, and each element can be present or not present. That can be mapped to a binary number between 0 (= 0b0000000000) and 1023 (= 0b1111111111). Loop through the numbers from 0 to 1023, inclusive, and check the sum for the subset corresponding to the set bits of the binary representation of the number.
Maybe not the most useful for this particular question, but a good way to generate all possible subsets of a given set.

Categories

Resources