如何从具有重复元素且其和等于n
的数组中随机找到一个组合。
示例
数组
是[1,2,2,3]
和n
是3
- 答案是
12
,12
,3
- 如果
randomSubsetSum(array,n)
是解决方案,那么randomSubsetSum([1,2,2,3],3)
将返回12
,12
,3
中的一个。注意: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)
)或使用大量时间。如何修复这个错误,或者有其他解决方案吗?
如果我做对了,核心任务是过滤具有特定总和的子集,并返回一个随机的。由于任务不是想出如何构建给定列表的所有子集的算法,我建议使用一个库来为你完成这项任务。其中一个库是组合库。如果你碰巧已经使用了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