R 求取n个可能值的长度为k的向量的所有非等价置换
我有一个函数,它接受长度为k的输入向量,其中向量中的每个元素最多可以接受n个值 通常k在6:10范围内,n在2:(k-1)范围内 对于任何给定的(n,k),将有n^k-1个可能向量的置换 目前,我正在将每个整数0:(n^k-1)映射到一个唯一的置换,并在该置换处计算函数,以找到所有可能向量上的最佳输入向量 例如,当n=3和k=6时,映射为:R 求取n个可能值的长度为k的向量的所有非等价置换,r,algorithm,function,permutation,combinatorics,R,Algorithm,Function,Permutation,Combinatorics,我有一个函数,它接受长度为k的输入向量,其中向量中的每个元素最多可以接受n个值 通常k在6:10范围内,n在2:(k-1)范围内 对于任何给定的(n,k),将有n^k-1个可能向量的置换 目前,我正在将每个整数0:(n^k-1)映射到一个唯一的置换,并在该置换处计算函数,以找到所有可能向量上的最佳输入向量 例如,当n=3和k=6时,映射为: 0:1,1,1,1,1,1 1:1,1,1,1,1,2 2:1,1,1,1,1,3 3:1,1,1,1,2,1 ... 728:3,3,3,3,3,3
0:1,1,1,1,1,1
1:1,1,1,1,1,2
2:1,1,1,1,1,3
3:1,1,1,1,2,1
...
728:3,3,3,3,3,3
然而,出于我的目的,一些排列是等价的。您可以将向量视为n个类中k个元素的分配
如果以下两种排列都成立,则两种排列A和B是等效的:
1,2,1,1,2,1
2,1,2,2,1,2
1,2,3,1,2,3
1,3,2,1,3,2
2,3,1,2,3,1
2,1,3,2,1,3
3,2,1,3,2,1
3,1,2,3,1,2
它们是等价的。在这两个向量中,元素{1,3,4,6}共享一个类,元素{2,5}共享一个类
当n=3和k=6时,向量
1,2,1,1,2,1
2,1,2,2,1,2
1,2,3,1,2,3
1,3,2,1,3,2
2,3,1,2,3,1
2,1,3,2,1,3
3,2,1,3,2,1
3,1,2,3,1,2
它们都是等价的
我的目标是找到一种比尝试1:(n^k-1)范围内的每个输入更有效的方法来找到最优向量
我可以看到两种可能的前进方式:
备选案文1。列举每一种可能性,然后过滤掉所有等价向量
选项2:减少我需要提前检查的范围。例如,对于n=3,k=6,我相当有信心(但尚未证明)我不需要检查高于161:1,2,3,3,3的任何内容,并且在1:161范围内也应该有一些等价排列
我更喜欢选择2
理想的解决方案是(n,k)的函数,它输出一个向量列表,表示我需要检查的1:n^k-1中的间隔。(n,k)的一个函数输出1:n^k-1中的最大整数/向量,我需要检查它
作为起点,以下是一些示例R代码:
vectorFromID <- function(id, n, k) {
if(id >= n^k) {
stop('ID too large!')
}
remainder <- id
elements <- list()
for(i in (k-1):0) {
elements[[k-i]] <- (remainder %/% (n ^ i))+1
remainder <- remainder %% (n ^ i)
}
return(unlist(elements))
}
vectorToID <- function(inputVector, n, k) {
total <- 0
for(i in 0:(k-1)) {
total <- total + (inputVector[i+1]-1) * (n ^ ((k-1)-i))
}
return(total)
}
# generate all possible vectors for n=3, k=6
all_vectors <- Map(function(x) vectorFromID(x, 3, 6), 0:728)
我还没有完全测试过,但我认为它能满足你的需求。我有三个主要步骤:
expand.grid
提供n和k的所有可能排列c(1,2,3,1,2,3)
和c(3,2,1,3,2,1)
将返回为c(1,2,3,1,2,3)
和c(1,2,3,1,2,3)
(即等效值),因为因子水平的顺序相似n=3
和k=6
,唯一组合的数量从729减少到162:combnmix
:
combnmix让我们首先为每个等价类选择一个代表。假设向量p={x_1…x_k}是所有p_i的字典最小值,使得p_i~p
注意,对于所有j
同样,如果对于每个i,x_i在范围(1..x_j+1)内,那么p是一个代表。
否则存在一些q={y_1…y_n},这样对于一些k,y_i=x_i,对于所有i
因此,对于所有j
void printResult(std::vector<int>& v){
for (auto val : v){
std::cout << val << ' ';
}
std::cout << '\n';
}
void enumerate(std::vector<int>& v, int n, int k, int max){
if (k == 0){
printResult(v);
} else {
for (int i = 1; i <= std::min(n, max + 1); i++){
v.push_back(i);
enumerate(v, n, k - 1, std::max(i, max));
v.pop_back();
}
}
}
void solve(int n, int k){
std::vector<int> v;
enumerate(v, n, k, 0);
}
void打印结果(std::vector&v){
用于(自动值:v){
我不确定你是如何进行比较的,但我发现这两种方法给出的结果并不相同。我提出的方法比另一种方法的结果更独特(例如,n=5,k=7有4875个唯一性,而不是855;n=3,k=6有162个唯一性,而不是122个)。您编写的解决方案生成了一个包含122行的数据帧,但标签增加到162行。请尝试比较rownames(res)和nrow(res)。您说得对-我很抱歉。@denis的算法非常有趣-我以前没有见过在循环中使用自身的函数。
void printResult(std::vector<int>& v){
for (auto val : v){
std::cout << val << ' ';
}
std::cout << '\n';
}
void enumerate(std::vector<int>& v, int n, int k, int max){
if (k == 0){
printResult(v);
} else {
for (int i = 1; i <= std::min(n, max + 1); i++){
v.push_back(i);
enumerate(v, n, k - 1, std::max(i, max));
v.pop_back();
}
}
}
void solve(int n, int k){
std::vector<int> v;
enumerate(v, n, k, 0);
}