数组中的舍入整数到最接近的高数

我有一系列这样的整数:[32,128,1024,2048,4096]

给定一个特定值,我需要得到数组中最接近的值,该值等于或高于该值。

我有以下代码

private int GetNextValidSize(int size, int[] validSizes) { int returnValue = size; for (int i = 0; i = size) { returnValue = validSizes[i]; break; } } return returnValue; } 

它有效,但有没有更好/更快的方法呢? 该数组永远不会包含超过5-10个元素。

澄清:如果它大于任何有效尺寸,我实际上想要返回原始值/尺寸。 可以认为validSizes数组始终是排序的,并且它始终包含至少一个值。

鉴于你只有5-10个元素我会认为这没关系。

只有5-10个元素,绝对是最简单的解决方案。 获得二进制文件工作将有助于更大的arrays,但它至少有可能出现一个一个错误。

但是,不是打破,我会直接从循环返回,使其更简单,并使用foreach:

  private int GetNextValidSize(int size, int[] validSizes) { int returnValue = size; foreach (int validSize in validSizes) { if (validSize >= size) { return validSizes; } } // Nothing valid return size; } 

使用LINQ可以使这更简单:

 // Make sure we return "size" if none of the valid sizes are greater return validSizes.Concat(new[] { size }) .First(validSize => validSize >= size); 

如果没有Concat步骤,或者如果有一个Concat方法只占用一个元素,它会更简单。 诚然,这很容易写:

 public static IEnumerable Concat(this IEnumerable source, T tail) { foreach (T element in source) { yield return element; } yield return tail; } 

那就是:

 return validSizes.Concat(size).First(validSize => validSize >= size); 

或者(我意识到我提出了比这里真正需要更多的选项!) FirstOrDefault的重载,它采用默认值返回:

 public static T FirstOrDefault(this IEnumerable source, Func predicate, T defaultValue) { foreach (T element in source) { if (predicate(element)) { return element; } } return defaultValue; } 

这样叫:

 return validSizes.FirstOrDefault(validSize => validSize >= size, size); 

这两种方法都是一次性使用,但如果您已经构建了一个额外的LINQ运算符库,它可能会很有用。

 int[] validSizes = new int[] { 32, 128, 1024, 2048, 4096 }; int sizeICareAbout = 4096; Console.Write(validSizes.Max(i => i < sizeICareAbout ? i : Int32.MinValue)); 

如果输入最小值,这将返回Int32.MinValue。 上帝,我喜欢LINQ。

您可以使用LINQ来简化查询 – 如果列表已排序,它可能与您可以编写的任何内容一样快。

 int someInitialValue; int defaultIfNotFound = ... // set to some value, even initialValue // attempt to find first value less than or equal int bestMatch = myListOfValues.Concat( new []{defaultIfNotFound} ) .FirstOrDefault( x => x >= someInitialValue ); 

如果未订购arrays,或者您需要更好的性能:

 myListOfValues.OrderBy( x => x ).Concat( new []{defaultIfNotFound} ) .FirstOrDefault( x => x >= someInitialValue ); 

你提到你列表相对较小(5-10项) – 所以线性搜索可能足够快。 但是,在较大的列表(数十个或数百个项目)上,您可能需要考虑使用二进制搜索来查找值:

 // index is positive if an exact match is found // if no exact match is found, the index returned will be two's complement and // reference the next number immediately larger than the search target int index = myListOfValues.BinarySearch( someInitialValue ); if( index < 0 && ~index > myListOfValues.Length ) bestMatch = someInitialValue; else bestMatch = index < 0 ? myListOfValues[~index] : myListOfValues[index]; 

如果您的arrays是有序的,您可以使用二进制搜索算法来加快速度。

请参阅: http : //en.wikipedia.org/wiki/Binary_search_algorithm

它不起作用。 以下是3个失败的测试用例。 实际上,函数接口没有任何失败的返回结果。

我写了一个更正版本,GetNextValidSize2。 由于无法返回失败消息,因此我会针对这些情况抛出exception。 以下是运行的结果:

test1:GetNextValidSize失败test1:GetNextValidSize2传递test2:GetNextValidSize对象引用未设置为对象的实例。 test2:GetNextValidSize2 validSizes没什么test3:GetNextValidSize传递test3:GetNextValidSize2 validSizes中没有项目

顺便说一句,LINQ可能更简单或更容易,但它几乎没有效率。 如果查询优化器/ CLR优化器运行良好,它可能同样有效。

这是代码 – 它在VB中,因为我现在正在使用它,不想切换精神齿轮:

模块模块1

 '''  ''' Error - does not work if validSizes is Nothing, or has 0 elements, or if ''' the list contains a validSize that is not the closest one before a closer one, ''' or there are no valid sizes. '''  Public Function GetNextValidSize(ByVal size As Integer, ByVal validSizes As List(Of Integer)) As Integer Dim returnValue As Integer = size For i As Integer = 0 To validSizes.Count - 1 Step 1 If validSizes.Item(i) >= size Then returnValue = validSizes.Item(i) Exit For End If Next Return returnValue End Function '''  ''' Returns the closest item in validSizes that is >= size. Throws an exception if one cannot ''' be found. '''  Public Function GetNextValidSize2(ByVal size As Integer, ByVal validSizes As List(Of Integer)) As Integer Dim closestValue As Integer = Integer.MaxValue Dim found As Boolean = False If validSizes Is Nothing Then Throw New Exception("validSizes is nothing") End If If validSizes.Count = 0 Then Throw New Exception("No items in validSizes") End If For Each x In validSizes If x >= size Then found = True If x < closestValue Then closestValue = x End If End If Next If Not found Then Throw New Exception("No items found") End If Return closestValue End Function '''  ''' Output the result of a test. '''  Public Sub outputResult(ByVal testName As String, ByVal result As Boolean, ByVal funcName As String) Dim passFail As String If result Then passFail = " passed" Else passFail = " failed" End If Console.WriteLine(testName & " : " & funcName & passFail) End Sub '''  ''' Output the result of a test where an exception occurred. '''  Public Sub outputResult(ByVal testName As String, ByVal ex As Exception, ByVal funcName As String) Console.WriteLine(testName & " : " & funcName & " " & ex.Message()) End Sub '''  ''' Test with a list of 3 integers '''  Public Sub test1() Dim aList As New List(Of Integer) aList.Add(5) aList.Add(4) aList.Add(3) Dim result = GetNextValidSize(3, aList) outputResult("test1", 3 = GetNextValidSize(3, aList), "GetNextValidSize") outputResult("test1", 3 = GetNextValidSize2(3, aList), "GetNextValidSize2") End Sub '''  ''' Test with a null reference '''  Public Sub test2() Dim aList = Nothing Try outputResult("test2", GetNextValidSize(3, aList), "GetNextValidSize") Catch ex As Exception outputResult("test2", ex, "GetNextValidSize") End Try Try outputResult("test2", GetNextValidSize2(3, aList), "GetNextValidSize2") Catch ex As Exception outputResult("test2", ex, "GetNextValidSize2") End Try End Sub '''  ''' Test with an empty array. '''  Public Sub test3() Dim aList As New List(Of Integer) Try outputResult("test3", GetNextValidSize(3, aList), "GetNextValidSize") Catch ex As Exception outputResult("test3", ex, "GetNextValidSize") End Try Try outputResult("test3", GetNextValidSize2(3, aList), "GetNextValidSize2") Catch ex As Exception outputResult("test3", ex, "GetNextValidSize2") End Try End Sub '''  ''' Run all tests. '''  Public Sub testAll() test1() test2() test3() End Sub Sub Main() testAll() Console.ReadLine() End Sub 

结束模块

我想你会得到第一个更大的数字,不一定是最接近的更大的数字。

如果您的数组未排序,则需要对其进行双重传递才能找到正确的数字。 首先,您将找到最大值,第二个值仍然大于原始值。