多维数组中的快速Int范围查找?

时间:2014-09-03 04:09:09

标签: c# arrays performance linq multidimensional-array

我正在尝试设计一种方法(.NET 4.5.2)来快速确定int是否在数值范围内。范围不重叠。速度是此全内存操作的首要优先级。 下面的代码工作正常,但实际系统将有来自DB的500,000行,我担心在阵列中间寻找范围命中会导致性能下降。一旦从数据库中读取数据,它就会保留在内存中,并用作Web应用程序中的参考数据。

所有想法都赞赏。感谢Filter方法的https://stackoverflow.com/a/5612589/139618

// Running console app correctly shows "2288779".

static void Main( string[] args )
{
    int[,] intervals = new int[3, 3];
    intervals[0, 0] = 200;
    intervals[0, 1] = 250;
    intervals[0, 2] = 1121214;
    intervals[1, 0] = 300;
    intervals[1, 1] = 350;
    intervals[1, 2] = 2288779;
    intervals[2, 0] = 400;
    intervals[2, 1] = 450;
    intervals[2, 2] = 3300004;
    var seekIntA = 336;
    var result = Filter(intervals, u => u[0] <= seekIntA && u[1] >= seekIntA).FirstOrDefault();
    if (null != result)
    {
        Console.WriteLine(result[2]);
    }
    else
    {
        Console.WriteLine("null");
    }
}

public static IEnumerable<T[]> Filter<T>( T[,] source , Func<T[] , bool> predicate )
{
    for ( int i = 0 ; i < source.GetLength( 0 ) ; ++i )
    {
        T[] values = new T[source.GetLength( 1 )];
        for ( int j = 0 ; j < values.Length ; ++j )
        {
            values[j] = source[i , j];
        }
        if ( predicate( values ) )
        {
            yield return values;
        }
    }
}

我愿意完全废弃数组的想法并使用任何其他集合(小写故意)类型来存储/搜索范围。

感谢。

1 个答案:

答案 0 :(得分:3)

如果看起来你的范围是一致的,你可以用O(1)时间和记忆来计算范围。 对于更通用的,虽然复杂的解决方案:

class Range
{
    public int min { get; private set; }
    public int max { get; private set; }

    public Range(int min, int max) {
        this.min = min;
        this.max = max;
    }
}

class MinComparer : IComparer<Range>
{
    public int Compare(Range x, Range y) {
        return (x.min - y.min);
    }
}

class MaxComparer : IComparer<Range>
{
    public int Compare(Range x, Range y) {
        return (x.max - y.max);
    }
}

class Ranges
{
    private List<Range> rangesMin;
    private List<Range> rangesMax;

    private IComparer<Range> minComparer;
    private IComparer<Range> maxComparer;

    public Ranges() {
        minComparer = new MinComparer();
        maxComparer = new MaxComparer();

        rangesMin = getRanges();
        rangesMax = new List<Range>(rangesMin);

        rangesMin.Sort(minComparer);
        rangesMax.Sort(maxComparer);
    }

    public IEnumerable<Range> getSetOfPossibleRanges(int numberToSeek) {
        Range rangeToSeek = new Range(numberToSeek, numberToSeek);
        int indexMin = rangesMin.BinarySearch(rangeToSeek, minComparer);
        int indexMax = rangesMax.BinarySearch(rangeToSeek, maxComparer);

        if(indexMin < 0) {
            indexMin = ~indexMin;
        }

        if(indexMax < 0) {
            indexMax = ~indexMax;
        }

        List<Range> subMin = rangesMin.GetRange(0, indexMin);
        List<Range> subMax = rangesMax.GetRange(indexMax, rangesMax.Count - indexMax);

        return subMin.Intersect(subMax);
    }

    private List<Range> getRanges() { //get ranges from DB here }
}

我使用了两个列表,一个按范围的下限排序,另一个按上限排序。 所有具有搜索号码的范围是这些列表的子集的交集,其中该数字大于最小排序列表中的下限,并且小于最大排序列表中的上限。

Ranges只应在应用程序启动时初始化(在初始化时进行昂贵的排序操作)。

我针对类似于您的代码的解决方案对此进行了测试,发现它更快更多(使用1M随机范围进行测试)。