Arrays 在间隔重复元素时洗牌数组

Arrays 在间隔重复元素时洗牌数组,arrays,matlab,random,shuffle,Arrays,Matlab,Random,Shuffle,我正试图编写一个函数来洗牌一个数组,该数组包含重复元素,但要确保重复元素之间不会太接近 此代码有效,但在我看来效率低下: function shuffledArr = distShuffle(myArr, myDist) % this function takes an array myArr and shuffles it, while ensuring that repeating % elements are at least myDist elements away from on a

我正试图编写一个函数来洗牌一个数组,该数组包含重复元素,但要确保重复元素之间不会太接近

此代码有效,但在我看来效率低下:

function shuffledArr = distShuffle(myArr, myDist)
% this function takes an array myArr and shuffles it, while ensuring that repeating 
% elements are at least myDist elements away from on another    

% flag to indicate whether there are repetitions within myDist
reps = 1;
while reps 

    % set to 0 to break while-loop, will be set to 1 if it doesn't meet condition
    reps = 0;  

    % randomly shuffle array
    shuffledArr = Shuffle(myArr);

    % loop through each unique value, find its position, and calculate the distance to the next occurence
    for x = 1:length(unique(myArr))
        % check if there are any repetitions that are separated by myDist or less
       if any(diff(find(shuffledArr == x)) <= myDist)
           reps = 1;
       break;
   end
end
end
函数shuffledArr=distShuffle(myArr,myDist) %此函数获取数组myArr并将其洗牌,同时确保重复 %元素之间至少有myDist元素 %指示myDist中是否存在重复的标志 重复次数=1; 当销售代表 %设置为0可中断while循环,如果不满足条件,则将设置为1 重复次数=0; %随机洗牌数组 shuffledArr=随机播放(myArr); %循环遍历每个唯一值,找到它的位置,并计算到下一次出现的距离 对于x=1:长度(唯一(myArr)) %检查是否有任何重复以myDist或更小的值分隔
如果有(diff(find(shuffledArr==x))我认为检查以下条件足以防止无限循环:

[~,num, C] = mode(myArr);
N = numel(C);
assert( (myDist<=N)  || (myDist-N+1) * (num-1) +N*num <= numel(myArr),...
'Shuffling impossible!');
我们可以找到模式
6
,其出现时间为
3
。我们安排
6
s,通过
2=myDist
空格分隔它们:

6 _ _ 6 _ _6
必须有
(3-1)*myDist=4
数字来填充空格。现在我们又有五个数字,这样数组就可以被洗牌了

如果我们有多种模式,问题会变得更加复杂。例如,对于这个数组
[4 6 5 1 6 7 4 6 4 4]
我们有
N=2
模式:
6
4
。它们可以安排为:

6 4 _ 6 4 _ 6 4 
我们有两个空格和三个以上的数字
[5 1 7]
,可以用来填充空格。例如,如果我们只有一个数字
[5]
,则无法填充空格,也无法洗牌数组

对于第三点,您可以使用稀疏矩阵来加速计算(我在倍频程中的初始测试表明它更有效):

函数shuffledArr=distShuffleSparse(myArr,myDist)
[U,~,idx]=唯一(myArr);
重复次数=真;
当销售代表
S=随机(idx);
shuffledBin=sparse(1:numel(idx),S,true,numel(idx)+myDist,numel(U));

reps=any(diff(find(shuffledBin))如果您只想找到一个可能的解决方案,您可以使用类似的方法:

x = [1   1   1   2   2   2   3   3   3   3   3   4   5   5   6   7   8   9];
n = numel(x);
dist = 3;           %minimal distance
uni = unique(x);    %get the unique value
his = histc(x,uni); %count the occurence of each element
s = [sortrows([uni;his].',2,'descend'), zeros(length(uni),1)];

xr = [];            %the vector that will contains the solution

%the for loop that will maximize the distance of each element
for ii = 1:n
    s(s(:,3)<0,3) = s(s(:,3)<0,3)+1;
    s(1,3) = s(1,3)-dist;
    s(1,2) = s(1,2)-1; 
    xr = [xr s(1,1)];
    s = sortrows(s,[3,2],{'descend','descend'})
end

if any(s(:,2)~=0)
    fprintf('failed, dist is too big')
end
解释:

我创建了一个向量
s
,此时
s
等于:

s =

   3   5   0
   1   3   0
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0

%col1 = unique element; col2 = occurence of each element, col3 = penalities
s =

   1   3   0  %1 is the next element that will be placed in our array.
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0
   3   4  -3  %3 has now 5-1 = 4 occurence and a penalities of -3 so it won't show up the next 3 iterations.
在for循环的每次迭代中,我们都会选择出现次数最多的元素,因为该元素将更难放置在数组中

在第一次迭代后,s等于:

s =

   3   5   0
   1   3   0
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0

%col1 = unique element; col2 = occurence of each element, col3 = penalities
s =

   1   3   0  %1 is the next element that will be placed in our array.
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0
   3   4  -3  %3 has now 5-1 = 4 occurence and a penalities of -3 so it won't show up the next 3 iterations.

最后,如果不是最小距离太大,第二列的每一个数字都应该等于0。

例如,如果给定数组有8个可能的洗牌,那么您的算法必须只找到一个解,还是随机给您一个概率为1/8的洗牌?第二种情况很难达到。您知道boge吗y排序?它试图通过随机化对数组进行排序,然后检查数组是否已排序,如果未排序,则再次进行随机化。显然,这不是任何人都应该选择的排序算法,但您所做的事情听起来非常相似(承认有点弱)。随机化是真的吗?还是会,例如,
sum(abs(diff)的最大化(shuffledar))
足够吗?@obchardon任何解决方案都可以。@NickyMattsson:我从来没有听说过bogey sort,谢谢你的提示!在这种情况下,随机是必要的,因为这是为了为心理实验创建一个随机的刺激顺序。不幸的是,最大化距离并不能满足我让刺激呈现自然化的目标c(即伪随机)。如果数组不是太大,您可以生成所有排列,保留满足最小距离的排列,然后选择一个。这是一个选项吗?非常好的方法。由于我的实验需要伪随机顺序,我通过在每次迭代中洗牌所有可接受的答案,稍微调整了您的代码(即惩罚为0且出现次数最大的所有s行)。非常感谢!我认为obchardon的方法更适合这里,但这让我意识到了稀疏矩阵的优点!我用一种不使用稀疏矩阵的解决方案更新了我的答案。
s =

   3   5   0
   1   3   0
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0

%col1 = unique element; col2 = occurence of each element, col3 = penalities
s =

   1   3   0  %1 is the next element that will be placed in our array.
   2   3   0
   5   2   0
   4   1   0
   6   1   0
   7   1   0
   8   1   0
   9   1   0
   3   4  -3  %3 has now 5-1 = 4 occurence and a penalities of -3 so it won't show up the next 3 iterations.