Return 可能重复的数组元素的所有唯一排列
Return all unique permutations of possibly-repeating array elements
假设我有一个长度为 N
的数组,其中包含 M
个不同的对象 (M < N
),因此其中一些对象会重复 N_i ... N_M
次。我想找到此类数组元素的所有可能 unique 配置(如排列),而无需事先计算整个排列列表(时间和内存限制)。
当然,天真的解决方案是使用 perms
生成所有可能的排列,然后 select 唯一的排列:
A = [1, 1, 2];
all_perms = perms(A)
% all_perms =
% 2 1 1
% 2 1 1
% 1 2 1
% 1 1 2
% 1 2 1
% 1 1 2
unique_perms = unique(all_perms, 'rows')
% unique_perms =
% 1 1 2
% 1 2 1
% 2 1 1
但这将生成 所有 N!
排列,而不仅仅是 N! / (N_1! * N_2! * ... * N_M!)
。对于 N = 3
,这对内存消耗和计时都没有太大影响,但随着 N
的增加和唯一元素数量的减少,差异可能会很大。所以:
是否有一种(希望是内置的)方法来列出包含非不同对象的数组的所有唯一排列,而无需中间保留 all 排列?
以下是 Bruno Luong 在 2014 年针对此问题建议的代码:
function p = uperm(a)
[u, ~, J] = unique(a);
p = u(up(J, length(a)));
end % uperm
function p = up(J, n)
ktab = histc(J,1:max(J));
l = n;
p = zeros(1, n);
s = 1;
for i=1:length(ktab)
k = ktab(i);
c = nchoosek(1:l, k);
m = size(c,1);
[t, ~] = find(~p.');
t = reshape(t, [], s);
c = t(c,:)';
s = s*m;
r = repmat((1:s)',[1 k]);
q = accumarray([r(:) c(:)], i, [s n]);
p = repmat(p, [m 1]) + q;
l = l - k;
end
end
可以通过将 nchoosek
替换为 Jan Simon's functions.
之一来进一步改进上述内容
假设我有一个长度为 N
的数组,其中包含 M
个不同的对象 (M < N
),因此其中一些对象会重复 N_i ... N_M
次。我想找到此类数组元素的所有可能 unique 配置(如排列),而无需事先计算整个排列列表(时间和内存限制)。
当然,天真的解决方案是使用 perms
生成所有可能的排列,然后 select 唯一的排列:
A = [1, 1, 2];
all_perms = perms(A)
% all_perms =
% 2 1 1
% 2 1 1
% 1 2 1
% 1 1 2
% 1 2 1
% 1 1 2
unique_perms = unique(all_perms, 'rows')
% unique_perms =
% 1 1 2
% 1 2 1
% 2 1 1
但这将生成 所有 N!
排列,而不仅仅是 N! / (N_1! * N_2! * ... * N_M!)
。对于 N = 3
,这对内存消耗和计时都没有太大影响,但随着 N
的增加和唯一元素数量的减少,差异可能会很大。所以:
是否有一种(希望是内置的)方法来列出包含非不同对象的数组的所有唯一排列,而无需中间保留 all 排列?
以下是 Bruno Luong 在 2014 年针对此问题建议的代码:
function p = uperm(a)
[u, ~, J] = unique(a);
p = u(up(J, length(a)));
end % uperm
function p = up(J, n)
ktab = histc(J,1:max(J));
l = n;
p = zeros(1, n);
s = 1;
for i=1:length(ktab)
k = ktab(i);
c = nchoosek(1:l, k);
m = size(c,1);
[t, ~] = find(~p.');
t = reshape(t, [], s);
c = t(c,:)';
s = s*m;
r = repmat((1:s)',[1 k]);
q = accumarray([r(:) c(:)], i, [s n]);
p = repmat(p, [m 1]) + q;
l = l - k;
end
end
可以通过将 nchoosek
替换为 Jan Simon's functions.