假设我有这个有效的例子:
lover_bound = 10;
upper_bound = 180;
steps = 10;
NumeroCestelli = 8;
livello = [lover_bound:steps:upper_bound];
L = length(livello);
n_c = ceil((factorial(L+NumeroCestelli-1))/(factorial(NumeroCestelli)*factorial(L-1)));
randIdxs = randi([1,L],n_c,NumeroCestelli);
PianoSperimentale = single(livello(randIdxs));
我需要执行n_c x NumeroCestelli
矩阵(称为PianoSperimentale
),其中每一行都是唯一的。它不允许任何形式的排列。使用randi我无法执行我的要求。
[10 20 30 40 50 60 70 80] is equal to [80 70 60 50 40 30 20 10]
PianoSperimentale
应为1081575x8
矩阵。在过去,我使用Combinator)函数,但对于非常大的矩阵来说非常慢。
[PianoSperimentale] = combinator(L,NumeroCestelli,'c','r');
for i=1:L
PianoSperimentale(PianoSperimentale==i)=livello(i);
end
那么,有一种方法可以执行combinator
但速度为randi
的相同矩阵吗?
编辑:我允许选择两次相同的号码(NumberOfCombinations = (NumeroCestelli+L-1)!/(NumeroCestelli!(L-1)!
)
建议编辑
我需要生成从18个元素的向量中选择任意8个数字时获得的完整组合(带有重复项)。这可以通过使用Combinator函数来完成,但对于非常大的矩阵来说非常慢。任何人都可以建议更快的方式来生成这个吗?
示例:使用“来自4的向量的样本3”将产生以下结果:
1 1 1
1 1 2
1 1 3
1 1 4
1 2 2
1 2 3
1 2 4
1 3 3
1 3 4
1 4 4
2 2 2
2 2 3
2 2 4
2 3 3
2 3 4
2 4 4
3 3 3
3 3 4
3 4 4
4 4 4
我知道对于18个元素的向量,从中选择8个元素,我将得到总共(18+8-1)!/8!*(18-1)!
个可能的组合,或者1081578个8个值的行。任何人都可以帮我找到一个快速算法来做到这一点?
答案 0 :(得分:2)
由于“历史”的原因,我正在写一个新的答案,而不是删除我的旧答案(这个问题的很多来回是理解这个问题的必要序言,所以这个答案甚至有意义)。 TL; DR:最后的完整代码。
这是一个非常棘手的问题,但我想我已经知道了。关键的见解是,你的结果矩阵(L+H-1)!/(H!(L-1)!)
中正确数量的元素的表达式强烈暗示“从L + H - 1中选择H”和你的解决方案之间存在关系。问题。特里克正在寻找这种关系。我通过首先写出combnk(5, 3)
的结果来做到这一点(在这个尺寸下,你可以手工写出所有的组合并寻找模式):
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
我们如何将此转换为1 2 3
(包括重复)的唯一组合?我注意到有三组连续数字:
1 2 3
2 3 4
3 4 5
这给了我一个想法,我需要对连续数字的差异做一些事情 - 不知何故,如果差异为1,我需要重复这个数字。这种见解很快导致了以下代码:
L = 3; % pick three numbers
H = 3; % from three numbers: 1,2,3
a = combnk(1:L+H-1, L); % generate all "combinations" of 1,2,3,4,5 without repeats
% the "magic" line: compress into "combinations with repeats"
b = cumsum([a(:,1) diff(a,[],2) - 1],2);
对于上面的例子,这给出了
1 1 1
1 1 2
1 1 3
1 2 2
1 2 3
1 3 3
2 2 2
2 2 3
2 3 3
3 3 3
这是怎么发生的?那么,diff
的a(沿着第二维)是
1 1
1 2
1 3
2 1
2 2
3 1
1 1
1 2
2 1
1 1
所以diff(a,[],2)-1)
是
0 0
0 1
0 2
1 0
1 1
2 0
0 0
0 1
1 0
0 0
在此表达式中,0
表示“重复上一个数字”,而1
表示“添加1”,2
表示“添加2”。我们可以通过使用cumsum
(累积和)函数以及从组合的第一个数字开始来完成所有这些添加。这导致表达
b = cumsum([a(:, 1) diff(a, [], 2) - 1]);
作为最后一步,您必须将其转换回您正在使用的索引。您的完整代码将是
L = 8;
H = 18;
a = combnk(1:L+H-1, L);
b = cumsum([a(:,1) diff(a,[],2) - 1],2);
livello = 10:10:180;
PianoSperimentale = livello(b);
将创建一个大小为b
的数组,以及livello
中的值。
我相信这对你有用(我无法测试这个,因为我的家用电脑上没有Matlab),并且它会尽可能快地解决这个问题。
答案 1 :(得分:0)
我正在尝试解析你的问题。从“no permutations”和“10 20 30 40 50 60 70 80”相当于“80 70 60 50 40 30 20 10”我推断您需要从18个样本中抽取8个数字,而从不相同样本两次。
这意味着您要生成所有可能的组合(来自18个可能值的8个样本),并从中进行选择。只有43758种方法可以做到这一点;之后你将不得不包括排列。因此,所述的问题(如果我理解正确的话)无法解决。
编辑现在问题已更新,我认为以下是一个解决方案:
lover_bound = 10;
upper_bound = 180;
steps = 10;
NumeroCestelli = 8;
livello = [lover_bound:steps:upper_bound];
livello = [livello livello];
PianoSperimentale = combnk(livello, 8);
因为每个数字都出现两次,所以可以重复它。不幸的是,这将允许多个“双打”(例如[10 10 20 20 30 30 40 40]将被允许)并且这得到比计算的表达式大得多的数字(即,36!/(28!8!)~30M )。一种可能的方法(允许最多一次)是
livello = lover_bound:steps:upper_bound;
for ii = 1:numel(livello)
PS(ii,:,:) = combnk([livello livello(ii)], 8);
end
PianoSperimentale = reshape(PS, [], 8);
这允许“每个循环一个重复的数字”,我相信这更接近你想到的答案,虽然组合的数量将是18 * (19!/(19-8)!8!) = 1360476
,比你的表达稍微大一些。我现在无法测试,因为我在这台电脑上没有Matlab ...