【问题标题】:Rounding a value to only a list of certain values in C#在 C# 中将一个值舍入为仅包含某些值的列表
【发布时间】:2011-10-23 11:26:57
【问题描述】:

我有一个双精度值列表,我想将变量的值舍入到仅该数字列表

例子:

列表内容为:{12,15,23,94,35,48}

变量的值为17,所以会四舍五入到15

如果变量的值小于最小数字,则四舍五入,如果它的值大于最大数字,则四舍五入。

列表内容总是根据外部因素而变化,所以我不能硬编码我想要向上或向下舍入的值。

如何在 C# 中做到这一点?

【问题讨论】:

  • 计算出未取整值和可能结果之间的差异。将这些差异存储在以可能结果为关键字的字典中,然后选择差异最小的结果。
  • 这个收藏有多大?否则,您应该考虑使用排序集合并使用二分搜索。
  • 如七所说,特别是如果您的列表是静态的(不会改变)

标签: c# .net math


【解决方案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。它比我的解决方案更有效,因为它只循环 values 一次。
  • 工作就像一个魅力,虽然我不能完全理解它:)
  • @AndersAbel:实际上,如果不是更多的话,应该是两次。首先要了解差异。然后订购。
  • @Steven Jeuris:你说得对,尤其是OrderBy的排序实现会对性能有很大的影响。
【解决方案2】:

假设数组已排序,您可以在数组中执行binary search,将其缩小到给定数字之间的两个数字。

然后,一旦你有了这两个数字,你只需四舍五入到两个数字中最接近的一个。

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];
    }

}

【讨论】:

  • 不仅假设,如果这应该适用于大型集合,排序集合是要走的路。
  • 在实践中当然没关系,但我注意到你正在用“危险”的方式找到两个整数的中点。如果 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 ​​'int' 不包含 'First' 的定义,并且找不到接受类型为 'int' 的第一个参数的扩展方法 'First'(您是否缺少 using 指令或程序集参考?)
【解决方案4】:

做这样的事情:

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;
    }
}

【讨论】:

    【解决方案5】:

    您可以遍历数字数组并设置一个 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];
        }
    }
    

    这应该可以很好地解决问题。

    【讨论】:

      【解决方案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 result;
          }
      

      【讨论】:

        猜你喜欢
        • 2023-01-28
        • 2016-06-29
        • 2015-12-11
        • 1970-01-01
        • 2013-06-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多