我想生成32张卡片组的所有排列,我将卡片表示为数字0-7,所以我不在乎卡片的颜色。游戏非常简单(将套牌分成两个大块,比较两张卡,将两张卡添加到较大卡组中)。我已经编写了游戏的这一部分,但是卡组现在是随机生成的,并且我想查看纸牌的所有可能性,并对其进行一些统计。我该如何编码生成的卡?我完全不知道如何编码。
答案 0 :(得分:1)
因为我只是在研究Aaron Williams 2009年的论文“通过前缀移位无循环生成多集置换”,所以我将贡献他的算法的一个版本,该算法正是解决了这个问题。我认为它比通常为此问题引用的标准C ++ next_permutation
更快,因为它不依赖于在输入向量中搜索枢轴点。但是,需要更广泛的基准测试才能得出确定的答案。很有可能最终会移动更多数据。
威廉姆斯算法的实现通过将置换存储在链接列表中来避免数据移动,这允许仅修改两个{{1}就可以实现“前缀移位”(将向量的前缀旋转一个位置) }指针。这使算法无环。
我的版本在几个方面有所不同。
首先,它使用普通数组存储值,这意味着移位确实需要循环。另一方面,它避免了必须实现链接列表数据类型,并且对数组的许多操作都更快。
第二,它使用后缀移位而不是前缀移位;实际上,它产生每个排列的相反结果(与Williams的实现相比)。我这样做是因为它简化了开始条件的描述。
最后,它只执行一个排列步骤。 Williams算法的一大优点是置换序列的状态可以封装在单个索引值中(当然也可以封装置换本身)。此实现返回要提供给下一个调用的状态。 (由于状态变量最后将为0,因此返回值将作为终止指示符加倍。)
代码如下:
next
万一评论不清楚,您可以使用以下代码来制作4 * k张卡片的所有混洗,而无需考虑西装的问题:
/* Do a single permutation of v in reverse coolex order, using
* a modification of Aaron Williams' loopless shift prefix algorithm.
* v must have length n. It may have repeated elements; the permutations
* generated will be unique.
* For the first call, v must be sorted into non-descending order and the
* third parameter must be 1. For subsequent calls, the third parameter must
* be the return value of the previous call. When the return value is 0,
* all permutations have been generated.
*/
unsigned multipermute_step(int* v, unsigned n, unsigned state) {
int old_end = v[n - 1];
unsigned pivot = state < 2 || v[state - 2] > v[state] ? state - 1 : state - 2;
int new_end = v[pivot];
for (; pivot < n - 1; ++pivot) v[pivot] = v[pivot + 1];
v[pivot] = new_end;
return new_end < old_end ? n - 1 : state - 1;
}
实际上尝试对k == 8进行此操作需要一段时间,因为存在32!/(4!) 8 可能的随机播放。大约是2.39 * 10 24 。但是我确实在0.3秒内完成了16张牌的全部洗牌,而且我估计半小时内可以完成20张牌。