一些背景:我正在编写或多或少的强力搜索算法来解决我遇到的问题。为了做到这一点,我需要生成并评估所有可能性,以找出哪个是最好的。由于评估实际上需要一些时间,我宁愿尽可能少地生成完全覆盖我的搜索空间的解决方案。此外,我可以做的更多元素越多越好。对于任何数字K,通常有K!对于高于~10的数字,排列和生成它们都很难。
真正的问题:搜索空间应包含两个元素的所有排列(n次el1和M乘以el2,其中K = M + N),具有以下限制:
如果我能够做到这一点,可能性的数量会急剧减少。由于理想情况下K很大,因此首先生成所有排列然后根据这些标准过滤它们是不可行的。我已经完成了第一个限制(见下文),它将Matlab的正常排列函数(perms)的数量从2 ^ K减少到K!/ N!M !,这是一个巨大的胜利。第二个限制只会将可能性的数量减少一半(在最好的情况下),但我认为第三个也应该能够真正减少可能性的数量。
如果有人知道该怎么做,最好还有如何计算会有多少种可能性,那对我有很大的帮助!我更喜欢解释,但代码也很好(我可以读C语言,Java(脚本),Python,Ruby,Lisp / Scheme)。
对于感兴趣的人:这是迄今为止我只获得唯一排列的算法:
function genPossibilities(n, m, e1, e2)
if n == 0
return array of m e2's
else
possibilities = genPossibilities(n-1, m, e1, e2)
for every possibility:
gain = number of new possibilities we'll get for this smaller possibility*
for i in max(0,(m+n-gain))
if possibility(i) is not e1
add possiblity with e1 inserted in position i
return new possibilities
答案 0 :(得分:2)
你所追求的是2-ary手镯的子集(该子集由字符A的n个n和字符B的m定义)。 所有手镯的集合允许A和B的数量变化。
以下代码打印出您所追求的序列,并以词汇顺序和持续摊销时间执行此操作。它基于this paper by Sawada中的通用算法 - 有关其工作原理的解释,请参阅该论文。
#include <stdlib.h>
#include <stdio.h>
static int *a;
static int n;
void print_bracelet(int n, int a[])
{
int i;
printf("[");
for (i = 0; i < n; i++)
printf(" %c", 'a' + a[i]);
printf(" ]\n");
}
int check_rev(int t, int i)
{
int j;
for (j = i+1; j <= (t + 1)/2; j++)
{
if (a[j] < a[t-j+1])
return 0;
if (a[j] > a[t-j+1])
return -1;
}
return 1;
}
void gen_bracelets(int n_a, int n_b, int t, int p, int r, int u, int v, int rs)
{
if (2 * (t - 1) > (n + r))
{
if (a[t-1] > a[n-t+2+r])
rs = 0;
else if (a[t-1] < a[n-t+2+r])
rs = 1;
}
if (t > n)
{
if (!rs && (n % p) == 0)
print_bracelet(n, a + 1);
}
else
{
int n_a2 = n_a;
int n_b2 = n_b;
a[t] = a[t-p];
if (a[t] == 0)
n_a2--;
else
n_b2--;
if (a[t] == a[1])
v++;
else
v = 0;
if ((u == (t - 1)) && (a[t-1] == a[1]))
u++;
if ((n_a2 >= 0) && (n_b2 >= 0) && !((t == n) && (u != n) && (a[n] == a[1])))
{
if (u == v) {
int rev = check_rev(t, u);
if (rev == 0)
gen_bracelets(n_a2, n_b2, t + 1, p, r, u, v, rs);
if (rev == 1)
gen_bracelets(n_a2, n_b2, t + 1, p, t, u, v, 0);
}
else
gen_bracelets(n_a2, n_b2, t + 1, p, r, u, v, rs);
}
if (u == t)
u--;
if (a[t-p] == 0 && n_b > 0)
{
a[t] = 1;
if (t == 1)
gen_bracelets(n_a, n_b - 1, t + 1, t, 1, 1, 1, rs);
else
gen_bracelets(n_a, n_b - 1, t + 1, t, r, u, 0, rs);
}
}
}
int main(int argc, char *argv[])
{
int n_a, n_b;
if (argc < 3)
{
fprintf(stderr, "Usage: %s <a> <b>\n", argv[0]);
return -2;
}
n_a = atoi(argv[1]);
n_b = atoi(argv[2]);
if (n_a < 0 || n_b < 0)
{
fprintf(stderr, "a and b must be nonnegative\n");
return -3;
}
n = n_a + n_b;
a = malloc((n + 1) * sizeof(int));
if (!a)
{
fprintf(stderr, "could not allocate array\n");
return -1;
}
a[0] = 0;
gen_bracelets(n_a, n_b, 1, 1, 0, 0, 0, 0);
free(a);
return 0;
}
答案 1 :(得分:1)
我想你想要生成2-ary free项链。有关链接,论文和一些代码,请参阅this question。
答案 2 :(得分:0)
如果你只有两个元素,你的空间要小得多:2 ^ k而不是k!。
尝试这样的方法:
如果你有j个可能的符号,而不是只有两个,那么做同样的事情,但使用base j而不是base 2.
答案 3 :(得分:0)
您正在寻找与订单无关的组合。 Matlab用K!/ N!M正确计算了这个!这正是计算组合数的公式。
答案 4 :(得分:0)
假设您有一个包含所有排列的数组,您可以将数组的内容放入哈希。然后这将工作(一点蛮力,但它的开始):
for each (element in array of permutations){
if (element exists in hash){
remove each circular permutation of element in hash except for element itself
}
}
答案 5 :(得分:0)
我对 k-ary 情况有了如下想法:
参考文献:
def findPermutations(n):
"""
Descriptions
------------
Find all possible permutations for a given positive integers n such that the elements are 0, 1, 2,..., n-1,
excluding mirrored or circular repetitions.
Example: if n = 3, there in only one possible permutation:
[0, 1, 2]
Parameters
----------
n : positive integers
Returns
-------
x : list
x[i][:] refers to the site order in the ith permutation
"""
ls = np.arange(0,n).tolist()
permutations = []
for p in itertools.permutations( ls[1:] ):
if p <= p[::-1]:
permutations += [p]
for end in permutations:
yield [ls[0]] + list(end)
x = list(findPermutations(4))
输出应该是
[[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 1, 3]]