简体   繁体   English

找到最窄间隔的算法,其中m将覆盖一组数字

[英]Algorithm to find the narrowest intervals, m of which will cover a set of numbers

Let's say you have a list of n numbers. 假设你有一个n个数字列表。 You are allowed to choose m integers (lets call the integer a ). 你可以选择m个整数(让我们调用整数a )。 For each integer a , delete every number that is within the inclusive range [ a - x, a + x ], where x is a number. 对于每个整数a ,删除包含范围[ a - x,a + x ]内的每个数字,其中x是数字。 What is the minimum value of x that can get the list cleared? 什么是可以清除列表的x的最小值?

For example, if your list of numbers was 例如,如果您的数字列表是

1 3 8 10 18 20 25 1 3 8 10 18 20 25

and m = 2, the answer would be x = 5. 并且m = 2,答案是x = 5。

You could pick the two integers 5 and 20. This would clear the list because it deletes every number in between [5-5, 5+5] and [20-5, 20+5]. 你可以选择两个整数5和20.这将清除列表,因为它删除了[5-5,5 + 5]和[20-5,20 + 5]之间的每个数字。

How would I solve this? 我该如何解决这个问题? I think the solution may be related to dynamic programming. 我认为解决方案可能与动态编程有关。 I do not want a brute force method solution. 我不想要蛮力方法解决方案。

Code would be really helpful, preferably in Java or C++ or C. 代码非常有用,最好是Java或C ++或C.

Hints 提示

Suppose you had the list 假设你有这个清单

1 3 8 10 18 20 25

and wanted to find how many groups would be needed to cover the set if x was equal to 2. 并且想要找到如果x等于2则需要多少组来覆盖该集合。

You could solve this in a greedy way by choosing the first integer to be 1+x (1 is the smallest number in the list). 您可以通过选择第一个整数为1 + x(1是列表中的最小数字)以贪婪的方式解决此问题。 This would cover all elements up to 1+x+x=5. 这将覆盖最多1 + x + x = 5的所有元素。 Then simply repeat this process until all numbers are covered. 然后只需重复此过程,直到涵盖所有数字。

So in this case, the next uncovered number is 8, so we would choose 8+x=10 and cover all numbers up to 10+x=12 in the second group. 所以在这种情况下,下一个未覆盖的数字是8,所以我们选择8 + x = 10并覆盖第二组中最多10 + x = 12的所有数字。

Similarly, the third group would cover [18,24] and the fourth group would cover [25,29]. 同样,第三组将涵盖[18,24],第四组将涵盖[25,29]。

This value of x needed 4 groups. x的这个值需要4组。 This is too many, so we need to increase x and try again. 这太多了,所以我们需要增加x并再试一次。

You can use bisection to identify the smallest value of x that does cover all the numbers in m groups. 您可以使用二分法来识别覆盖m组中所有数字的x的最小值。

An effective algorithm can be(assuming list is sorted) -> 一个有效的算法可以(假设列表被排序) - >

  1. We can think of list as groups of 'm' integers. 我们可以将列表视为'm'整数的组。

  2. Now for each group calculate 'last_element - first_element+1', and store maximum of this value in a variable say, 'ans'. 现在为每个组计算'last_element - first_element + 1',并将此值的最大值存储在变量中,例如'ans'。

  3. Now the value of 'x' is 'ans/2'. 现在'x'的值是'ans / 2'。

I hope its pretty clear how this algorithm works. 我希望它非常清楚这个算法是如何工作的。

A recursive solution: 递归解决方案:

First, you need an estimation, you can split in m groups, then estimated(x) must be ~ (greather - lower element) / 2*m. 首先,你需要一个估计,你可以分成m组,然后估计(x)必须是〜(更好 - 下部元素)/ 2 * m。 the estimated(x) could be a solution. 估计的(x)可能是一个解决方案。 If there is a better solution, It has lower x than extimated(x) in all groups! 如果有更好的解决方案,它在所有组中的x均低于预期(x)! and You can check it with the first group and then repeat recursively. 你可以用第一组检查它,然后递归重复。 The problem is decreasing until you have only a group: the last one, You know if your new solution is better or not, If there'is better, you can use it to discard another worse solution. 问题是减少,直到你只有一个组:最后一个,你知道你的新解决方案是否更好,如果有更好的,你可以用它来丢弃另一个更糟糕的解决方案。

private static int estimate(int[] n, int m, int begin, int end) {
    return (((n[end - 1] - n[begin]) / m) + 1 )/2;
}

private static int calculate(int[] n, int m, int begin, int end, int estimatedX){
    if (m == 1){
        return estimate(n, 1, begin, end);
    } else {
        int bestX = estimatedX;
        for (int i = begin + 1; i <= end + 1 - m; i++) {
            // It split the problem:
            int firstGroupX = estimate(n, 1, begin, i);
            if (firstGroupX < bestX){
                bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m-1, i, end, bestX)));
            } else {
                i = end;
            }
        }
        return bestX;
    }
}

public static void main(String[] args) {
    int[] n = {1, 3, 8, 10, 18, 20, 25};
    int m = 2;
    Arrays.sort(n);
    System.out.println(calculate(n, m, 0, n.length, estimate(n, m, 0, n.length)));
}

EDIT: 编辑:

Long numbers version: Main idea, It search for "islands" of distances and split the problem into different islands. 长号版本:主要思想,它搜索距离的“岛屿”并将问题分成不同的岛屿。 like divide and conquer, It distribute 'm' into islands. 像分而治之,它将'm'分配到岛屿。

private static long estimate(long[] n, long m, int begin, int end) {
    return (((n[end - 1] - n[begin]) / m) + 1) / 2;
}

private static long calculate(long[] n, long m, int begin, int end, long estimatedX) {
    if (m == 1) {
        return estimate(n, 1, begin, end);
    } else {
        long bestX = estimatedX;
        for (int i = begin + 1; i <= end + 1 - m; i++) {
            long firstGroupX = estimate(n, 1, begin, i);
            if (firstGroupX < bestX) {
                bestX = Math.min(bestX, Math.max(firstGroupX, calculate(n, m - 1, i, end, bestX)));
            } else {
                i = end;
            }
        }
        return bestX;
    }
}

private static long solver(long[] n, long m,  int begin, int end) {
    long estimate = estimate(n, m, begin, end);
    PriorityQueue<long[]> islands = new PriorityQueue<>((p0, p1) -> Long.compare(p1[0], p0[0]));
    int islandBegin = begin;
    for (int i = islandBegin; i < end -1; i++) {
        if (n[i + 1] - n[i] > estimate) {
            long estimatedIsland = estimate(n, 1, islandBegin, i+1);
            islands.add(new long[]{estimatedIsland, islandBegin, i, 1});
            islandBegin = i+1;
        }
    }
    long estimatedIsland = estimate(n, 1, islandBegin, end);
    islands.add(new long[]{estimatedIsland, islandBegin, end, 1});
    long result;
    if (islands.isEmpty() || m < islands.size()) {
        result = calculate(n, m, begin, end, estimate);
    } else {    
        long mFree = m - islands.size();
        while (mFree > 0) {
            long[] island = islands.poll();
            island[3]++;
            island[0] = solver(n, island[3], (int) island[1], (int) island[2]);
            islands.add(island);
            mFree--;
        }
        result = islands.poll()[0];
    }
    return result;
}

public static void main(String[] args) {
    long[] n = new long[63];
    for (int i = 1; i < n.length; i++) {
        n[i] = 2*n[i-1]+1;
    }
    long m = 32;
    Arrays.sort(n);
    System.out.println(solver(n, m, 0, n.length));
}

I think it's similarly problem of clusterization. 我认为这同样是集群化的问题。 For example You may use k-means clustering algorithm: do partitions of initial list on m classes and for x get maximum size divided by two of obtained classes. 例如,您可以使用k-means聚类算法:对m个类进行初始列表的分区,对于x,得到最大大小除以两个获得的类。

1) You should look into BEST CASE, AVERAGE CASE and WORST CASE complexities with regards to TIME and SPACE complexities of algorithms. 1)关于算法的时间和空间复杂性,您应该研究最佳案例,平均案例和最坏情况的复杂性。

2) I think David Pérez Cabrera has the right idea. 2)我认为DavidPérezCabrera有正确的想法。 Let's assume average case (as in the following pseudo code) 我们假设平均情况(如下面的伪代码)

3) Let the list of integers be denoted by l 3)让整数列表用l表示

    keepGoing = true
    min_x = ceiling(l[size-1]-l[0])/(2m)

    while(keepGoing)
    {
        l2 = l.copy
        min_x = min_x-1
        mcounter = 1

        while(mcounter <= m)
        {
            firstElement = l2[0]
//  This while condition will likely result in an ArrayOutOfBoundsException
//  It's easy to fix this.

            while(l2[0] <= firstElement+2*min_x)
            {   remove(l2[0])   }
            mcounter = mcounter+1
        }
        if(l2.size>0)
            keepGoing = false
    }
    return min_x+1

4) Consider 4)考虑一下

l = {1, 2, 3, 4, 5, 6, 7}, m=2 (gives x=2)
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=2
l = {1, 10, 100, 1000, 10000, 100000, 1000000}, m=3

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM