在给定范围列表的情况下找到最大重叠范围的有效算法

时间:2013-03-06 17:11:07

标签: c# algorithm math dynamic-programming

考虑以下界面,该界面描述了integer值的连续范围。

public interface IRange {
    int Minimum { get;}
    int Maximum { get;}

    IRange LargestOverlapRange(IEnumerable<IRange> ranges);
} 

我正在寻找一种有效的算法来查找给定IRange个对象列表的最大重叠范围。下图简要概述了这个想法。顶部数字表示integer值,|-----|表示具有最小值和最大值的IRange个对象。我堆叠了IRange个对象,以便解决方案很容易可视化。

0123456789  ...                            N
|-------|   |------------|        |-----|
   |---------|    |---|
       |---|             |------------|
               |--------|  |---------------|
                              |----------|

此处,LargestOverlapRange方法将返回:

                                  |---|

由于该范围总共有4个'重叠'。如果有两个单独的IRange具有相同数量的重叠,我想返回null

以下是我尝试过的一些简要代码。

public class Range : IRange 
{

    public IRange LargestOverlapRange(IEnumerable<IRange> ranges) {           

        int maxInt = 20000;    

        // Create a histogram of the counts
        int[] histogram = new int[maxInt];
        foreach(IRange range in ranges) {
            for(int i=range.Minimum; i <= range.Maximum; i++) {
                histogram[i]++;
            }
        }

        // Find the mode of the histogram
        int mode = 0;
        int bin = 0;
        for(int i =0; i < maxInt; i++) {
            if(histogram[i] > mode) {
                mode = histogram[i];
                bin = i;
            }
        }

        // Construct a new range of the mode values, if they are continuous
        Range range;
        for(int i = bin; i < maxInt; i++) {
            if(histogram[i] == mode) {  
                if(range != null)
                    return null; // violates two ranges with the same mode   
                range = new Range();             
                range.Minimum = i;                     
                while(i < maxInt && histrogram[i] == mode)
                    i++;
                range.Maximum = i;                    
            }
        }

        return range;
    }

}

这涉及四个循环,如果不是更高则很容易为O(n ^ 2)。是否有更有效的算法(速度方式)从其他范围列表中找到最大的重叠范围?

修改

是的,O(n ^ 2)不正确,我正在考虑错误。它应该是O(N * M),正如评论中所指出的那样。

编辑2

让我说明一些事情,integer值的绝对最小值和最大值将来自(0,20000)。其次,IRange的平均数量将在100的数量级。我不知道这是否会改变算法的设计方式。

编辑3

我在科学仪器(质谱仪)上实施该算法,其中数据处理的速度对于数据质量是最重要的(更快的分析时间=在时间T中收集的更多光谱)。固件语言(专有)仅具有数组[],并且不是面向对象的。我之所以选择C#是因为我在两种语言之间移植概念方面很不错,并认为为了SO社区的利益,一个好的答案会有更广泛的受众。

2 个答案:

答案 0 :(得分:10)

将范围列表转换为起点和终点列表。使用O(n log n)算法对列表进行排序。现在,您可以遍历列表并递增或递减计数器,具体取决于它是开始点还是停止点,这将为您提供当前的重叠深度。

答案 1 :(得分:1)

正如我理解OP的问题,给出3个范围的解决方案

A: 012
B:  123
C:    34

将是范围12(A和B的常见子集), 范围123(因为它不是任何一对的共同子集。)


在编写任何代码之前考虑一下纸上的算法。动态编程解决方案怎么样? (如果您不了解动态编程,那么值得在书中阅读)。动态编程的想法是建立更简单的子问题的解决方案。

f_i(n, k)为从n个共同开始的最长间隔的大小,至少是给定范围的至少k个。

您可以从f_0开始计算f_1,从f_1开始计算f_2,依此类推。更新功能只取决于考虑的额外范围。

假设有M个范围。 f_M的值将告诉我们您的问题的答案。

你所谈到的最深的深度是最大的k,使得某些n的f_M(n,k)不为零。让我们称之为最大深度K.然后我们在n上寻找f_M(n,K)的最大值。它的最大值是你最大范围的大小,从最大化n开始。

最大化n必须是某个范围的下限,所以我们只需要为这些n计算f。有M个范围,所以最多M个下限。因此,该算法具有复杂度O(MMK)。

  

让第i个范围从a到b

     

如果n在a到b之外,则没有变化
    f_i(n,k) = f_i-1(n,k)

     

如果n在a到b之间,我们测试通过将新的区间与我们的旧k-1深度解决方案组合而得到的k深度解。如果它比我们已经拥有的更好,我们只使用它。   f_i(n,k) = max ( f_i-1(n,k) , min( f_i-1(n,k-1) , b-n+1))


实施例!适用于0到5,2到6,4到8和6到9的范围。

n           0123456789

            ......          range 0 to 5
f_1(n,1)    6543210000

              .....         range 2 to 6
f_2(n,1)    6554321000
f_2(n,2)    0043210000

                .....       range 4 to 8
f_3(n,1)    6554543210  
f_3(n,2)    0043321000
f_3(n,3)    0000210000

                  ....      range 6 to 9
f_4(n,1)    6554544321
f_4(n,2)    0043323210
f_4(n,3)    0000211000
f_4(n,4)    0000000000

因此,最深的深度K是3,最长的范围是4到5.我们还可以看到最长的范围深度2的大小为4,从3开始。