提高嵌套循环的性能

时间:2013-06-10 20:46:23

标签: c# arrays performance sorting loops

这个逻辑是找到数组中的数字n,其中n和n + 5之间的范围将包括数组中的最多数字。我提出了一个解决方案,但它需要一个嵌套循环,因此它有点慢。有没有办法改善其表现?提前谢谢。

保证数组排序。

int[] myArray = new int[]{1,2,4,5,7,9,15,19};
int bestNumber = 0;
int MaxMatchFound = 0;

for (int o = 0; o < myArray.Length; o++)
{

    int TempMatchFound = 0;

    for (int i = 0; i < myArray.Length; i++)
    {
        if (myArray[i] >= myArray[o] && myArray[i] <= (myArray[o] + 5))
        {
            TempMatchFound++;
        }
    }
    if (TempMatchFound > MaxMatchFound)
    {
        bestNumber = myArray[o];
        MaxMatchFound = TempMatchFound;
    }

}

return bestNumber;

4 个答案:

答案 0 :(得分:5)

暂停值,然后遍历值v并对满足w的所有值v <= w <= v + 5的相关计数求和,然后找出最大计数:

var buckets = myArray.GroupBy(n => n)
                     .ToDictionary(g => g.Key, g => g.Count());
var rangeCounts = 
    buckets.Keys
           .Select(v =>
               new {
                   Value = v,
                   Count = Enumerable.Range(0, 6)
                                     .Sum(i => buckets.ContainsKey(v + i) ? 
                                               buckets[v + i] : 
                                               0
                                         )
               }
    );
var bestRange = rangeCounts.MaxBy(x => x.Count);

现在,bestRange.Value是最佳范围的起点,bestRange.Count是属于[bestRange.Value, bestRange.Value + 5]范围内的元素数量。在这里,我使用了MaxBy

认为这会让你获得线性表现。构建字典是线性的,构建范围是线性的,MaxBy是线性的。甚至适用于非分类。

答案 1 :(得分:4)

这里你去:这在O(N)时间和O(1)内存中运行。这形成了其他解决方案中描述的存储桶,然后在您通过阵列时丢弃它们。队列用于跟踪哪些存储桶在可以添加的情况下处于“活动状态”。字典中永远不会有超过6个条目,队列也不会。

int[] myArray = new int[]{1,2,4,5,7,9,15,19};
Dictionary<int, int> counts = new Dictionary<int, int>();
Queue<int> q = new Queue<int>();

int n = 0;
int currentMaxCount = 0;


for(int i = 0; i < myArray.Length; i++)
{
    var currentNum = myArray[i];
    if(counts.ContainsKey(currentNum))
    {
        counts[currentNum]++;
    }
    else
    {
        counts[currentNum] = 1;
        q.Enqueue(currentNum);
    }

    for(int j = 1; j <= 5; j++)
    {
        if(counts.ContainsKey(currentNum - j))
            counts[currentNum - j]++;
    }

    if(q.Peek() + 5 < currentNum)
    {
        if(counts[q.Peek()] > currentMaxCount)
        {
            currentMaxCount = counts[q.Peek()];
            n = q.Peek();
        }
        counts.Remove(q.Dequeue());

    }
}

while(q.Count > 0)
{
    if(counts[q.Peek()] > currentMaxCount)
    {
        currentMaxCount = counts[q.Peek()];
        n = q.Peek();
    }
    counts.Remove(q.Dequeue());
}

Console.WriteLine("There are {0} matches between {1} and {2}", currentMaxCount, n, n + 5);

答案 2 :(得分:3)

这是一个O(n)的解决方案,无论范围如何,都使用O(1)额外空间。

它只通过数组,总是进行2N比较。我没有看到任何方法来改进这种算法,尽管有一些微优化可以从实现中挤出更多的速度。

private int FindRange(int[] myArray)
{
    const int range = 5;
    int start = 0;
    int maxMatchFound = 0;
    int maxIndex = 0;
    for (int i = 0; i < myArray.Length; ++i)
    {
        if (myArray[i] > myArray[start] + range)
        {
            int matchLength = i - start;
            if (matchLength > maxMatchFound)
            {
                maxMatchFound = matchLength;
                maxIndex = start;
            }
            // move forward until within range
            do
            {
                ++start;
            } while (myArray[i] > myArray[start] + range);
        }
    }
    // Final check, from myArray[start] to end of array
    int len = myArray.Length - start;
    if (len > maxMatchFound)
    {
        maxMatchFound = len;
        maxIndex = start;
    }
    return maxIndex;

这里的想法是,如果特定数字a[x]落在a[i]的范围内,那么假设a[i+1],它将落在x > i的范围内。 (因此,在原始数组中,a[3]的值落在a[0]的范围内,因此它也将落在a[1]a[2]的范围内。

因此索引i会递增,直到它引用的值超出a[start]的范围。然后,start会递增,直到a[i]再次进入范围。这两个索引以交替的方式向前移动数组。

答案 3 :(得分:0)

这是一个单行LINQ选项。在性能方面不是最好的(它迭代多次)。还是值得注意的。

var result = myArray
             .OrderByDescending(i => myArray.Count(i2 => i2 >= i && i2 <= i + 5))
             .First();