当前位置: 首页 > 知识库问答 >
问题:

从包含重复元素的数组中随机找到一个组合,其和等于n

龙成仁
2023-03-14

如何从具有重复元素且其和等于n数组中随机找到一个组合。

示例

  • 数组[1,2,2,3]n3
  • 答案是12123
  • 如果randomSubsetSum(array,n)是解决方案,那么randomSubsetSum([1,2,2,3],3)将返回12123中的一个。注意:12出现的频率是3
  • 一个真实的场景:随机选择题库中的试题进行考试

我发现了一些类似的问题和解决方案:

>

A:方案A和方案B

Q:具有k个部分的秩和非秩整数分区

A:解决方案C

缺点

解决方案A解决方案B无法随机找到组合<代码>解决方案C不允许重复元素。

我的Java解决方案

public List<Integer> randomSubsetSum(List<Integer> list, Integer n) {
    list.removeIf(e -> e > n);
    int maxSum = list.stream().reduce(0, Integer::sum);
    if (maxSum < n) {
        throw new RuntimeException("maxSum of list lower than n!");
    }
    if (maxSum == n) {
        return list;
    }
    final SecureRandom random = new SecureRandom();
    // maybe helpful, not important
    final Map<Integer, List<Integer>> map = list.stream().collect(Collectors.groupingBy(Function.identity()));
    final List<Integer> keys = new ArrayList<>(map.keySet());
    final List<Integer> answers = new ArrayList<>();
    int sum = 0;
    while (true) {
        int keyIndex = random.nextInt(keys.size());
        Integer key = keys.get(keyIndex);
        sum += key;

        // sum equal n
        if (sum == n) {
            List<Integer> elements = map.get(key);
            answers.add(elements.get(random.nextInt(elements.size())));
            break;
        }

        // sum below n
        if (sum < n) {
            List<Integer> elements = map.get(key);
            answers.add(elements.remove(random.nextInt(elements.size())));
            if (elements.isEmpty()) {
                map.remove(key);
                keys.remove(keyIndex);
            }
            continue;
        }

        // sum over n: exists (below  = n - sum + key) in keys
        int below = n - sum + key;
        if (CollectionUtils.isNotEmpty(map.get(below))) {
            List<Integer> elements = map.get(below);
            answers.add(elements.get(random.nextInt(elements.size())));
            break;
        }

        // sum over n: exists (over  = sum - n) in answers
        int over = sum - n;
        int answerIndex =
                IntStream.range(0, answers.size())
                        .filter(index -> answers.get(index) == over)
                        .findFirst().orElse(-1);
        if (answerIndex != -1) {
            List<Integer> elements = map.get(key);
            answers.set(answerIndex, elements.get(random.nextInt(elements.size())));
            break;
        }

        // Point A. BUG: may occur infinite loop

        // sum over n: rollback sum
        sum -= key;
        // sum over n: remove min element in answer
        Integer minIndex =
                IntStream.range(0, answers.size())
                        .boxed()
                        .min(Comparator.comparing(answers::get))
                        // never occurred
                        .orElseThrow(RuntimeException::new);
        Integer element = answers.remove((int) minIndex);
        sum -= element;
        if (keys.contains(element)) {
            map.get(element).add(element);
        } else {
            keys.add(element);
            map.put(element, new ArrayList<>(Collections.singleton(element)));
        }
    }
    return answers;
}

点A,可能会出现无限循环(例如随机子循环([3,4,8],13))或使用大量时间。如何修复这个错误,或者有其他解决方案吗?

共有1个答案

贾成天
2023-03-14

如果我做对了,核心任务是过滤具有特定总和的子集,并返回一个随机的。由于任务不是想出如何构建给定列表的所有子集的算法,我建议使用一个库来为你完成这项任务。其中一个库是组合库。如果你碰巧已经使用了ApacheCommons或Guava,他们也实现了一些方法来获取列表的所有子集。

使用cominatoricSlib3获取子集非常简单:

Generator.subset(List.of(1, 2, 2, 3))
        .simple()
        .stream()
        .forEach(System.out::println);

得到:

[]
[1]
[2]
[1, 2]
[2]
[1, 2]
[2, 2]
[1, 2, 2]
[3]
[1, 3]
[2, 3]
[1, 2, 3]
[2, 3]
[1, 2, 3]
[2, 2, 3]
[1, 2, 2, 3]

对于上面描述的任务,以下内容可以作为使用上述lib外包子集查找的起点:

import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;

import org.paukov.combinatorics3.Generator;

public class TestJavaClass {
    public static void main(String[] args) {
        System.out.println(randomSubsetSum(List.of(1, 2, 2, 3), 3));
    }

    public static List<Integer> randomSubsetSum(List<Integer> list, Integer n) {
        List<List<Integer>> subLists = Generator.subset(list)
                                                .simple()
                                                .stream()
                                                .filter(x -> !x.isEmpty())
                                                .filter(x -> sum(x).equals(n))
                                                .collect(Collectors.toList());
        if (!subLists.isEmpty()){
           return subLists.get(new Random().nextInt(subLists.size()));
        }
        else {
            throw new IllegalArgumentException("No subset found in: " + list + " having a sum = " + n);
        }
    }

    public static Integer sum(List<Integer> list) {
        return list.stream().reduce(0, Integer::sum);
    }
}

为了简单起见,我删除了边缘案例的处理。如果需要,请添加:

list.removeIf(e -> e > n);
int maxSum = list.stream().reduce(0, Integer::sum);
if (maxSum < n) {
    throw new RuntimeException("maxSum of list lower than n!");
}
if (maxSum == n) {
    return list;
}
 类似资料:
  • 问题内容: 假设我有一个数组,我想随机选择一个元素。 最简单的方法是什么? 明显的方法是。但是也许有红宝石之类的东西?或者如果不能通过扩展创建这种方法? 问题答案: Swift 4.2及更高版本 推荐的新方法是Collection协议的内置方法:。它返回一个可选参数以避免我以前假设的空情况。 如果不创建数组并且不能保证count> 0,则应执行以下操作: Swift 4.1及以下 只是为了回答您的

  • 假设我有一个数组,我想随机选择一个元素。 最简单的方法是什么? 最明显的方法是数组[随机索引]。但可能有类似ruby的数组。示例 ?或者,如果不是,那么可以使用扩展创建这样的方法吗?

  • 本文向大家介绍写一个方法从数组中随机抽取N个不重复的元素相关面试题,主要包含被问及写一个方法从数组中随机抽取N个不重复的元素时的应答技巧和注意事项,需要的朋友参考一下

  • 本文向大家介绍从Python中的元组列表中找到包含给定元素的元组,包括了从Python中的元组列表中找到包含给定元素的元组的使用技巧和注意事项,需要的朋友参考一下 列表可以将元组作为其元素。在本文中,我们将学习如何识别包含特定搜索元素(字符串)的元组。 有条件 我们可以根据情况设计跟踪。之后,我们可以提及条件或条件组合。 示例 输出结果 运行上面的代码给我们以下结果- 带过滤器 我们将过滤器功能与

  • 问题内容: 我试图遍历2个数组,外部数组则比另一个数组更长。它将循环遍历第一个,如果第二个数组不包含该int,它将返回false。但是我不知道该怎么做。这是我到目前为止所拥有的: 运行时出现此错误: 我想知道是否可以不使用嵌套循环(如上)来完成。我知道我做错了,如果有人可以在此问题上提供帮助,那就太好了。我也不确定要在Java文档中寻找什么类。 问题答案: 您可以检查较大的数组是否包含较小数组中的

  • 从 array 中获取 n 个唯一键随机元素。 使用Fisher-Yates算法 对数组进行打乱。 使用 Array.slice() 获取第一个 n 元素。 省略第二个参数,n 从数组中随机取得 1 个元素。 const sampleSize = ([...arr], n = 1) => { let m = arr.length; while (m) { const i = Mat