随机整数(无排列)

时间:2013-11-08 15:16:51

标签: matlab random matrix

假设我有这个有效的例子:

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个值的行。任何人都可以帮我找到一个快速算法来做到这一点?

2 个答案:

答案 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 ...