#c# #.net #math
#c# #.net #математика
Вопрос:
У меня есть список двойных значений, я хочу округлить значение переменной только до этого списка чисел
Пример:
Содержимое списка таково: {12,15,23,94,35,48}
Значение переменной равно 17, поэтому оно будет округлено до 15
Если значение переменной меньше наименьшего числа, оно будет округлено до него, если его значение больше наибольшего числа, оно будет округлено до него.
Содержимое списка всегда меняется в зависимости от внешнего фактора, поэтому я не могу жестко определить значения, которые я хочу округлить в большую или меньшую сторону.
Как это можно сделать на C #?
Комментарии:
1. Определите разницу между неокругленными значениями и возможными результатами. Сохраните эти различия в словаре с указанием возможного результата и выберите результат с наименьшей разницей.
2. Насколько большой может быть эта коллекция? В противном случае вам следует рассмотреть возможность использования отсортированной коллекции и использования двоичного поиска.
3. Как сказал Seven, особенно если ваш список статичен (не меняется)
Ответ №1:
Вот метод, использующий 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;
РЕДАКТИРОВАТЬ: переименовал некоторые переменные, чтобы код был менее запутанным…
Редактировать:
list
Переменная является неявно объявленным массивом int
. Первый оператор LINQ diffList
определяет анонимный тип, который содержит ваш исходный номер из списка ( number
), а также разницу между ним и вашим текущим значением ( input
) .
Второй оператор LINQ result
упорядочивает эту коллекцию анонимных типов по разнице, что является вашим требованием «округления». Он принимает первый элемент в этом списке, поскольку он будет иметь наименьшее различие, а затем выбирает только оригинал .number
из анонимного типа.
Комментарии:
1. 1. Это более эффективно, чем мое решение, поскольку оно выполняется только
values
один раз.2. Работает как шарм, хотя я не могу понять все это 🙂
3. @AndersAbel: На самом деле, скорее в два раза, если не больше. Сначала, чтобы получить различия. Затем по порядку.
4. @Steven Jeuris: Вы правы, особенно реализация сортировки
OrderBy
будет иметь большое влияние на производительность.
Ответ №2:
Предполагая, что массив отсортирован, вы можете выполнить двоичный поиск в массиве, сузив его до двух чисел, между которыми находится данное число.
Затем, когда у вас есть эти два числа, вы просто округляете до ближайшего из двух.
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];
}
}
Комментарии:
1. Не только предполагая, что если это должно работать с большими коллекциями, отсортированная коллекция — это правильный путь.
2. На практике, конечно, это не имеет значения, но я отмечаю, что вы используете «опасный» способ нахождения середины двух целых чисел. Если min max переполняется до отрицательного, то половина этого числа является отрицательным числом. Смотрите Эту статью для интересного анализа проблемы и ее возможных решений: locklessinc.com/articles/binary_search
Ответ №3:
Использование 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;
Пока число возможных значений довольно мало, это должно работать. Если у вас есть тысячи возможных значений, следует использовать другое решение, которое использует values
сортировку (если она действительно отсортирована).
Комментарии:
1. Ошибка 1 ‘int’ не содержит определения для ‘First’, и не удалось найти метод расширения ‘First’, принимающий первый аргумент типа ‘int’ (вам не хватает директивы using или ссылки на сборку?)
Ответ №4:
Вы можете перебрать массив чисел и установить переменную roundedNum равной каждой переменной, если переменная delta меньше текущей наименьшей дельты. Некоторые вещи лучше всего описывать в коде.
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];
}
}
Это должно сделать трюк довольно хорошо.
Ответ №5:
Сделайте что-то вроде этого:
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;
}
}
Ответ №6:
Простое округление в меньшую сторону в linq
public decimal? RoundDownToList(decimal input, decimal[] list)
{
var result = (from number in list
where number <= input
orderby number descending
select number).FirstOrDefault();
return resu<
}