这是一个非常有趣的挑战。我使用 java 来说明我的方法。
将问题分解
我已将整个问题拆分为更小的部分:
- 我们需要根据拆分大小为子数组设置存储
- 子数组应包含相同数量的元素,除非有余数(例如,
10 elements 拆分为 k = 3 子数组导致数组长度为:3, 3, 4)
- 以这样的方式将元素拆分到子数组中,以使重复项最少
1 + 2 - 将数组分成相等的部分
我已经用array of length 10 和k = 3 做了这个例子。由于除法给出的余数,子数组将是length 3, 3 and 4。
在 sn-p 中,我确保用0 填充数组,每个子数组将有0 to 1 额外元素。如果有余数,多余的元素将在所有子数组上拆分。
在我的示例中,我使用了 array with length of 13 和 k = 3,所以它看起来像这样:
[[0, 0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
3 - 减少重复的策略
我在想我们可以从分析给定的数组开始。我们可以通过计数找出每个单独的数字存在多少次。一旦我们知道这些数字存在多少次,我们就可以按值对地图进行排序,最后得到一个地图,其中包含每个数字的出现次数,并从最高出现次数开始。
在我的例子中:
1=4 // contains 1 exactly 4 times
2=4 // contains 2 exactly 4 times
3=3 // ...
4=1
5=1
我们从中得到什么?好吧,我们肯定知道,我们不希望所有这些1s 在同一个子数组中,因此我们的想法是平均分割所有子数组上的所有出现。如果我们最终得到4x 1 和4x 2 和k = 3(如上例所示),那么我们可以将1 和2 放入每个子数组中。这让我们每个人都有 1 个重复项(一个额外的 1 和一个额外的 2)。
在我的示例中,这看起来像:
[[1, 2, 3, 4, 5], [1, 2, 3, 0], [1, 2, 3, 0]]
// 1 used 3 times => 1 duplicate
// 2 used 3 times => 1 duplicate
// 3 used 3 times => ok
// 4 used 1 time => ok
// 5 used 1 time => ok
为此,我们循环遍历出现映射,添加键并跟踪我们可以使用的剩余数字(在 sn-p 中,这是使用映射)。
我们可以对每个键执行此操作,直到只剩下重复项。此时子数组只包含唯一的数字。现在对于重复项,我们可以再次重复整个过程,并将它们平均分配到尚未完全填充的子数组上。
最后是这样的:
// the duplicate 1 got placed in the second subarray
// the duplicate 2 got placed in the third subarray
[[1, 2, 3, 4, 5], [1, 2, 3, 1], [1, 2, 3, 2]]
Java 代码
我不确定你能走多远以及它的表现如何。至少在我做的一些测试中,它似乎工作得很好。您可能会找到一个性能更高的解决方案,但我可以想象,这是解决此问题的一种方法。
无论如何,这是我的尝试:
public static void main(String args[]) {
final List<Integer> list = Arrays.asList(1, 2, 3, 1, 3, 4, 3, 5, 1, 2, 1, 2, 2);
final Map<Integer, Integer> occurrenceMap = findOccurrences(list);
final Map<Integer, Integer> occurrenceMapSorted = occurrenceMap;
occurrenceMapSorted.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEach(System.out::println);
final List<List<Integer>> sublists = setupSublists(list.size(), 3);
System.out.println(sublists);
final Map<Integer, Integer> usageMap = new HashMap<>(occurrenceMapSorted.size());
for (int i = 0; i < sublists.size(); i++) {
final List<Integer> sublist = sublists.get(i);
populateSublist(occurrenceMapSorted, usageMap, sublist);
}
System.out.println(sublists);
}
public static void populateSublist(Map<Integer, Integer> occurrenceMapSorted, Map<Integer, Integer> usageMap, List<Integer> sublist) {
int i = 0;
int skipp = 0;
while (i < sublist.size() && sublist.get(i) == 0) {
for (Map.Entry<Integer, Integer> entry : occurrenceMapSorted.entrySet()) {
if (skipp > 0) {
skipp--;
continue;
}
final int entryKey = entry.getKey();
final Integer usageCount = usageMap.getOrDefault(entryKey, null);
if (usageCount == null || usageCount < entry.getValue()) {
if (usageCount == null) {
usageMap.put(entryKey, 1);
} else {
usageMap.put(entryKey, usageCount + 1);
}
sublist.set(i, entryKey);
System.out.println("i: " + i);
System.out.println("sublist: " + sublist);
System.out.println("usage: ");
usageMap.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.forEach(System.out::println);
System.out.println();
i++;
skipp = i;
break;
}
}
}
}
public static List<List<Integer>> setupSublists(int listLength, int numberOfSublists) {
if (numberOfSublists <= 1 || numberOfSublists > listLength) {
throw new IllegalArgumentException("Number of sublists is greater than the number of elements in the list or the sublist count is less or equal to 1.");
}
final List<List<Integer>> result = new ArrayList<>(numberOfSublists);
final int minElementCount = listLength / numberOfSublists;
int remainder = listLength % numberOfSublists;
for (int i = 0; i < numberOfSublists; i++) {
final List<Integer> sublist = new ArrayList();
boolean addRemainder = true;
for (int j = 0; j < minElementCount; j++) {
sublist.add(0);
if (remainder > 0 && addRemainder) {
sublist.add(0);
addRemainder = false;
remainder--;
}
}
result.add(sublist);
}
return result;
}
public static Map<Integer, Integer> findOccurrences(List<Integer> list) {
final Map<Integer, Integer> result = new HashMap();
for (int i = 0; i < list.size(); i++) {
final int listElement = list.get(i);
final Integer entry = result.getOrDefault(listElement, null);
if (entry == null) {
result.put(listElement, 1);
} else {
result.put(listElement, entry.intValue() + 1);
}
}
return result;
}