std :: next_permutation(和std :: prev_permutation)置换总共为n的[first, last)
范围内的所有值!排列(假设所有元素都是唯一的)。
是否可以编写这样的函数:
template<class Iter>
bool next_permutation(Iter first, Iter last, Iter choice_last);
这会置换[first, last)
范围内的元素,但只会选择范围[first, choice_last)
中的元素。也就是说,我们可能有20个元素,并希望迭代10个选项的所有排列,20个P 10选项和20个P 20。
这样的功能是否可以实现?有没有人知道任何现有的实现?
基本上我正在做的就是解决这个问题。关于如何改善这一点的建议也是受欢迎的。
V
个N
元素的向量开始,我要访问其中R
元素的R <= N
元素的每个排列。{/ 1}。I
的向量R
,其值为{ 0, 1, 2, ... R - 1 }
,以作为V
C
的向量R
,其值为{ V[I[0]], V[I[1]], ... V[I[R - 1]] }
C
中的值执行某些操作。I
的元素并重新迭代(如果能够)。该功能如下所示:
bool NextPermutationIndices(std::vector<int> &I, int N)
{
const int R = I.size();
for (int i = R - 1; ; --i) {
if (I[i] < N - R + i) {
++I[i];
return true;
}
if (i == 0)
return false;
if (I[i] > I[i-1] + 1) {
++I[i-1];
for (int j = i; j < R; ++j)
I[j] = I[j-1] + 1;
return true;
}
}
}
由于所有可能的逐个错误,该功能非常复杂,使用它的一切都比可能需要的更复杂。
编辑:
事实证明,它比我想象的更容易显着。从here开始,我能够找到我需要的许多精确算法的精确实现(组合,排列等)。
template<class BidirectionalIterator>
bool next_partial_permutation(BidirectionalIterator first,
BidirectionalIterator middle,
BidirectionalIterator last)
{
std::reverse(middle, last);
return std::next_permutation(first, last);
}
此外,还有一种组合算法以类似的方式工作。然而,实现这一点要复杂得多。
答案 0 :(得分:3)
要迭代nPk排列,我之前使用了this old CUJ article中提供的for_each_permutation()
算法。它使用了Knuth的一个很好的算法,它可以原位旋转元素,最后将它们保留在原始顺序中。因此,它满足您无需外部存储器的要求。它也适用于BidirectionalIterators。它不符合您next_permutation()
的要求。但是,我认为这是一个胜利 - 我不喜欢有状态的API。
答案 1 :(得分:1)
Java组合生成器的源代码位于 http://www.merriampark.com/comb.htm 。删除Java习语,它几乎就是你正在寻找的东西,作为一个生成器实现,以限制你的内存使用。
此问题来自称为 Combinatorics 的数学领域,它是离散数学的一部分。离散数学对计算机科学从业者至关重要,因为它包括我们日常使用的几乎所有数学(如逻辑,算法,计数,关系,图论等)。我强烈推荐Discrete and Combinatorial Mathematics: An applied introduction或 Discrete Mathematics and Its Applications,如果你负担得起的话。
(注意:这个问题与“Algorithm for Grouping”有关,但不完全重复,因为这个问题要求在一般情况下解决它。)
答案 2 :(得分:0)
算法简化将分为两个单独的步骤。
通过交错这些操作,您可以避免分配中间列表。
可以通过跳过非选定项来在双向迭代器上实现选择。生成所有选择,例如通过置换R
个和(N-R)
个零的序列。这将需要O(N)额外的内存,但是您可以将原始序列置换到位。
答案 3 :(得分:0)
它的价值在于,这是一种有效的实现。
它要求上面选择的元素按排序顺序开始。它只有在序列中没有重复元素时才有效(如果有,它会错过一些排列,并且不会以正确的方式结束)。它也可能缺少一些边缘情况,因为我没有真正测试它,因为我没有计划实际使用它。
这种方式相对于this answer's的一个好处是,这种方式不会访问字典顺序中的排列,这可能(但可能不是)很重要。使用boost :: bind有时候创建一个函子来传递给for_each也是一种痛苦。
template<class Iter>
bool next_choice_permutation(Iter first, Iter choice, Iter last)
{
if (first == choice)
return false;
Iter i = choice;
--i;
if (*i < *choice) {
std::rotate(i, choice, last);
return true;
}
while (i != first) {
Iter j = i;
++j;
std::rotate(i, j, last);
--i;
--j;
for (; j != last; ++j) {
if (*i < *j)
break;
}
if (j != last) {
std::iter_swap(i, j);
return true;
}
}
std::rotate(first, ++Iter(first), last);
return false;
}