【问题标题】:Algorithm to find the simplest combination of integers that has not yet been used找到尚未使用的最简单整数组合的算法
【发布时间】:2011-03-19 23:32:59
【问题描述】:

我正在寻找一种算法,用于找到尚未使用的 0 到 5 整数的最简单组合(即由最少数量的整数组成的组合)(使用的组合在列表中)。

顺序很重要,组合应该在列表中返回。

例如,使用数字的列表可能如下所示:

{{0},{1},{2},{3},{4},{0,0},{0,1},{0,2},...,{2,1 },{2,2},...,{1,5,4},...}

在这种情况下,算法应该返回一个带有 {5} 的列表,因为 {5} 是由最少整数组成的组合。

如果列表如下所示:

{{0},{1},{2},{3},{4},{5},{0,0},{0,1},{0,2},{0,3 },{0,5},...}

算法应该返回一个包含 0 和 4 ({0,4}) 的列表。

由于要在 Java 中使用,最好使用 Java 答案,但也可以使用伪代码或其他编程语言。

提前谢谢你!

【问题讨论】:

  • {0,1,2, ... 应该是 {{0},{1},{2}, ...
  • 你说得对,谢谢。现在改变了。
  • +1 让我忘记我正在做饭回答:)
  • +1 让我在早餐时思考一些事情;)
  • 您是使用您的返回值填写该列表的人吗?然后只需先返回所有 1 个长度,然后返回 2 个长度组合等。

标签: java algorithm combinatorics combinations


【解决方案1】:

我猜示例 2 是错误的: 对于 {{0},{1},{2},{3},{4},{5},{0,1},{0,2},{0,3},{0,5}, ...} 最小解是 {0,0},而不是 {0,4}

完整的解决方案在这里:

import java.util.*;

public class Algorithm {

    static List<List<Integer>> getChildren(List<Integer> node){
        List<List<Integer>> children = new ArrayList<List<Integer>>();
        for(int i = 0; i < 6; i++){
            List<Integer> child = new ArrayList<Integer>(node);
            child.add(i);
            children.add(child);
        }
        return children;
    }

    static List<Integer> find(Queue<List<Integer>> queue, Set<List<Integer>> set){

        for(;;){
            List<Integer> head = queue.poll();
            if(!set.contains(head)){
                return head;
            } else {
                for(List<Integer> child : getChildren(head)){
                    queue.add(child);
                }
            }
        }
    }

    public static void main(String[] arg) {
        Queue<List<Integer>> queue = new LinkedList<List<Integer>>();
        for(int i = 0; i < 6; i++){
            queue.add(Collections.singletonList(i));
        }
        // Example {{0},{1},{2},{3},{4},{5},{0,1},{0,2},{0,3},{0,5},...}
        Set<List<Integer>> task = new HashSet<List<Integer>>();
        task.add(Arrays.asList(0));
        task.add(Arrays.asList(1));
        task.add(Arrays.asList(2));
        task.add(Arrays.asList(3));
        task.add(Arrays.asList(4));
        task.add(Arrays.asList(5));
        task.add(Arrays.asList(0, 1));
        task.add(Arrays.asList(0, 2));
        task.add(Arrays.asList(0, 3));
        task.add(Arrays.asList(0, 5));

        System.out.println(find(queue, task));
    }

}

【讨论】:

  • 其实不是我——我写的程序找到了它:)
【解决方案2】:

如果你的列表是有序的,我能想到有两种方法比线性搜索更好。

假设你不会完全填满组合空间,你可以使用二分搜索的变体。

首先,我们将每组大小“x”称为一个组。所以,0,1,2,3,4,5 是第 1 组,{0,0} 到 {5,5} 是第 2 组。

从第 1 组开始,检查包含组中最后一个值的列表位置(如果它们都在那里)。例如,List[5] == 5。如果是这样,请转到第 2 组并重复。如果没有,则继续在该组中进行二分搜索,总是偏向于较低的一侧,最终您会找到第一个缺失值。


否则,如果您希望最终使用整个组合空间,只需对整个组合空间进行二分查找,如果前面的值都存在,则检查该位置的值是否与预期值匹配。

【讨论】:

  • -“我在此指出您的描述存在差异。您排除了 {0,0} 但包含了 {2,2}”{0,0} 也是一种可能的解决方案。在我的示例中,未使用 {0,0},因此它不在列表中。
  • +1 假设输入是排序的,第二种方法似乎是一个很好的方法(可能是最优的)
【解决方案3】:

一个完整的(天真的)解决方案:

import java.util.*;

public class Test {

    public static String increment(String str) {
        if (str.isEmpty()) return "0";
        int i = str.length() - 1;
        if (str.charAt(i) < '5')
            return str.substring(0, i) + (char) (str.charAt(i) + 1);
        return increment(str.substring(0, i)) + "0";
    }


    public static String nextUnused(Set<String> used) {
        String s = "0";
        while (used.contains(s))
            s = increment(s);
        return s;
    }

    public static void main(String args[]) {
        Set<String> used = new HashSet<String>(Arrays.asList("0", "1", "2", "3",
                "4", "00", "01", "02", "21", "22", "154"));
        for (int i = 0; i < 10; i++) {
            String toUse = nextUnused(used);
            System.out.println("Adding " + toUse);
            used.add(toUse);
        }
    }
}

输出:

Adding 5
Adding 03
Adding 04
Adding 05
Adding 10
Adding 11
Adding 12
Adding 13
Adding 14
Adding 15

您可以通过将memoization 应用于增量方法来加快速度。

【讨论】:

    【解决方案4】:

    只需按顺序尝试每种组合,从最短的开始,当你有一个不使用时停止?你试过了吗,看起来确实很明显?

    【讨论】:

      【解决方案5】:

      对于这个问题,我会创建一个特定的对象来存储一个元素(单个数字或数字元组):

      class Tuple {
          String key;
          Set<Integer> tuple;
      }
      

      关键是数字的连接,有序。 在您的示例中,键为“0”“1”“2”“3”“4”“5”“01”“02”“03”“05”。

      您可以使用 Map 来存储元组,并带有关联键 - 值。

      由于键遵循逻辑顺序,因此很容易找到下一个空闲元组。您只需从“0”开始并递增键(使用定义的顺序),检查 Map 以验证元组是否已被使用。

      在本例中,第一个空闲元组的键为“04”。通过这个键,创建关联的元组很容易。

      【讨论】:

        猜你喜欢
        • 2019-08-07
        • 1970-01-01
        • 2012-09-04
        • 2019-06-11
        • 1970-01-01
        • 1970-01-01
        • 2016-09-04
        • 2011-12-11
        • 1970-01-01
        相关资源
        最近更新 更多