【发布时间】:2015-03-10 14:08:08
【问题描述】:
我正在尝试实现一种算法,以从一组 n 个元素中获取 k 个元素的所有组合,其中两个连续组合之间的差异被最大化(这种反向格雷码)。换句话说,应该对组合进行排序,以避免元素连续出现两次,从而不会对任何元素进行不必要的区分。
理想情况下,该算法也不会预先计算所有组合并将它们存储到内存中,而是按需提供组合。 我对此进行了广泛搜索,并找到了一些详细的答案,例如https://stackoverflow.com/a/127856/1226020,但我似乎无法应用它。此外,该答案中链接的许多文章都是付费内容。
为了说明我的意思:
从一组 [0, 1, 2, 3, 4] 中,找出两个元素的所有组合。 使用一个简单的算法尝试增加最右边的元素直到不再可能,然后向左移动,增加前一个数字等,我得到以下结果:
[0, 1]
[0, 2]
[0, 3]
[0, 4]
[1, 2]
[1, 3]
[1, 4]
[2, 3]
[2, 4]
[3, 4]
我使用以下 Java 代码生成此结果:
public class CombinationGenerator {
private final int mNrElements;
private final int[] mCurrentCombination;
public CombinationGenerator(int n, int k) {
mNrElements = n;
mCurrentCombination = new int[k];
initElements(0, 0);
// fake initial state in order not to miss first combination below
mCurrentCombination[mCurrentCombination.length - 1]--;
}
private void initElements(int startPos, int startValue) {
for (int i = startPos; i < mCurrentCombination.length; i++) {
mCurrentCombination[i] = i + startValue - startPos;
}
}
public int[] getNextCombination() {
for (int i = 0; i < mCurrentCombination.length; i++) {
int pos = mCurrentCombination.length - 1 - i;
if (mCurrentCombination[pos] < mNrElements - 1 - i) {
initElements(pos, mCurrentCombination[pos] + 1);
return mCurrentCombination;
}
}
return null;
}
public static void main(String[] args) {
CombinationGenerator cg = new CombinationGenerator(5, 2);
int[] c;
while ((c = cg.getNextCombination()) != null) {
System.out.println(Arrays.toString(c));
}
}
}
这不是我想要的,因为我希望每个连续的组合都尽可能与前一个不同。目前,元素“1”连续出现四次,然后再也没有出现。对于这个特定示例,一种解决方案是:
[0, 1]
[2, 3]
[0, 4]
[1, 2]
[3, 4]
[0, 2]
[1, 3]
[2, 4]
[0, 3]
[1, 4]
对于这个特定的 案例,我确实设法通过在生成组合后应用排序算法来完成这个结果,但这并不能满足我对按需组合生成的要求,因为整个组合必须一次生成,然后排序并保存在内存中。我不确定它是否适用于任意 k 和 n 值。最后,我很确定这不是最有效的方法,因为排序算法基本上会遍历一组组合,试图找到一个与前一个组合不共享任何元素的组合。我还考虑为每个元素保留一个“命中计数”表,并使用它来始终获得包含最低组合命中计数的下一个组合。 我的一些经验性结论是,如果 n > 2k,则可以避免元素完全出现在两个连续的组合中。否则,至少应该可以避免元素连续出现两次以上等。
您可以将此问题与使用足球比赛等的标准循环方案在 k = 2 时实现的问题进行比较,但我需要一个任意 k 值的解决方案。我们可以把它想象成某种游戏的锦标赛,我们有 n 个玩家在一组游戏中与所有其他玩家对战,每场游戏有 k 个玩家。玩家应尽可能不必连续打两场比赛,但也不必在两场比赛之间不必要地等待太久。
任何关于如何使用可靠的排序算法生成后解决这个问题的指针,或者 - 最好是 - 按需的,都会很棒!
注意:我们通常假设 n
谢谢
【问题讨论】:
-
我怀疑在组合图上为哈密顿循环运行 Angluin--Valiant 局部搜索算法对于小参数设置是有效的,如果在资源使用方面有些残酷的话。
-
@DavidEisenstat 你如何保证最终输出中没有“关闭”边缘?如果我正确理解了这个问题,那么期望的目标是让所有相邻的组合由 至少 X 个位置分隔 - 使其中许多组合非常不同,而有一些非常接近则不会被认为是好的。
-
@tucuxi 组合图有边,只要组合可以在输出中背靠背出现。
-
我们如何知道给定特定 n 和 k 可实现的最大最小距离?上限是 min(k, n-k),但我不确定它是否总是可以实现的。如果是这样,那么@DavidEisenstat 构建具有可接受边的图并找到哈密顿路径的想法看起来不错(如果昂贵的话)。
-
非常感谢大家。我不得不承认我学习组合学已经有一段时间了,所以我可能需要付出相当大的努力才能掌握这一点。非常感谢您给我一些指点!
标签: algorithm sorting combinations combinatorics gray-code