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

时间:2009-07-17 13:57:08

标签: c# arrays

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

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

我有以下代码

  private int GetNextValidSize(int size, int[] validSizes)
  {

      int returnValue = size;

      for (int i = 0; i < validSizes.Length; i++)
      {
          if (validSizes[i] >= size)
          {
              returnValue = validSizes[i];
              break;
          }
      }

      return returnValue;
  }

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

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

7 个答案:

答案 0 :(得分:8)

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

然而,不是打破,我会直接从循环返回,使其更简单,并使用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<T> Concat(this IEnumerable<T> 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<T> source,
                               Func<T, bool> predicate,
                               T defaultValue)
{
    foreach (T element in source)
    {
        if (predicate(element))
        {
            return element;
        }
    }
    return defaultValue;
}

这样称呼:

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

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

答案 1 :(得分:7)

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

答案 2 :(得分:2)

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。

答案 3 :(得分:2)

您可以使用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 );

如果没有订购数组,或者您需要更好的性能:

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

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

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

答案 4 :(得分:1)

如果你的阵列是有序的,你可以使用二进制搜索算法加快速度。

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

答案 5 :(得分:1)

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

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

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

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

这是代码 - 它在VB中,因为我现在正在使用它,不想切换心理齿轮:

模块模块1

''' <summary>
''' 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.
''' </summary>
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

''' <summary>
''' Returns the closest item in validSizes that is >= size. Throws an exception if one cannot 
''' be found.
''' </summary>
 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

''' <summary>
''' Output the result of a test.
''' </summary>
 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

''' <summary>
''' Output the result of a test where an exception occurred.
''' </summary>
 Public Sub outputResult(ByVal testName As String, ByVal ex As Exception, ByVal funcName As String)

    Console.WriteLine(testName & " : " & funcName & " " & ex.Message())
End Sub

''' <summary>
''' Test with a list of 3 integers
''' </summary>
 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

''' <summary>
''' Test with a null reference
''' </summary>
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

''' <summary>
''' Test with an empty array.
''' </summary>
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

''' <summary>
''' Run all tests.
''' </summary>
Public Sub testAll()
    test1()
    test2()
    test3()
End Sub

Sub Main()
    testAll()
    Console.ReadLine()
End Sub

结束模块

答案 6 :(得分:-1)

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

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