これまでのところ、「開始」パーティションとして保持することをお勧めします。
このような開始パーティションができたら、平均が最大および最小のパーティションを取得し、平均をグローバル平均に近づけるために、何らかのルールでそれらの間でメンバーを交換することをお勧めします。最大と最小のパーティションを持つ現在のパーティションに切り替える適切なメンバーのペアが見つからなくなるまでループでそれを繰り返す場合(つまり、各スワップ後に最大と最小のパーティションを選択する)、次のようになります。少なくとも局所最適を残しました。
では、交換する適切な番号のペアをどのように選択しますか?さて、あなたはそれらの差がそれらの平均を等しくするために必要な合計の変化からどれだけ離れているかを最小にするような数を探しています、そしてそれらが平均を過剰に補償してそれらをさらに大きくすることを引き起こしてはならないという追加の制限があります離れて。
while 1
sm_idx=find(avg_scores==min(avg_scores),1); %Choose a set with the smallest average
lg_idx=find(avg_scores==max(avg_scores),1); %Same with largest average
pcorr=round((avg_scores(lg_idx)-avg_scores(sm_idx))*...
(length(group_scores{lg_idx})*length(group_scores{sm_idx}))/...
(length(group_scores{lg_idx})+length(group_scores{sm_idx}))); %Calculate perfect correction
M = group_scores{lg_idx}'*ones(size(group_scores{sm_idx}))-...
ones(size(group_scores{lg_idx}'))*group_scores{sm_idx}; %note: assumes group_scores are row vectors
Midx=find(abs(M-pcorr)==min(abs(M-pcorr)(:)),1);
if (abs(M(Midx)-pcorr)>=pcorr)
break;
end
[l_c_idx,s_c_idx] = ind2sub(size(M),Midx);
temp=group_scores{lg_idx}(l_c_idx);
group_scores{lg_idx}(l_c_idx)=group_scores{sm_idx}(s_c_idx);
group_scores{sm_idx}(s_c_idx)=temp;
temp=group_subjects{lg_idx}(l_c_idx);
group_subjects{lg_idx}(l_c_idx)=group_subjects{sm_idx}(s_c_idx);
group_subjects{sm_idx}(s_c_idx)=temp;
avg_scores(lg_idx)=avg_scores(lg_idx)-M(Midx)/length(group_scores{lg_idx});
avg_scores(sm_idx)=avg_scores(sm_idx)+M(Midx)/length(group_scores{sm_idx});
end
コードのフォローアップとしてテストすると、次のような結果が得られます。
私のループの前に:
avg_scores =
54.000
53.525
53.050
52.500
51.925
私のループの後:
avg_scores =
53.000
53.000
53.000
53.000
53.000
ご覧のとおり、完璧な結果が得られる場合があります。
コードに関するちょっとした好奇心の1つは、記述されているように、結果を生成する最初の一致を常に優先することです。最適なソリューションを見つける可能性をもう少し高くするためのもう少しランダム性については、次のように置き換えることができます
Midx=find(abs(M-pcorr)==min(abs(M-pcorr)(:)),1);
と
Midx_options=find(abs(M-pcorr)==min(abs(M-pcorr)(:)));
Midx=Midx(randi(length(Midx)));
これにより、最適なスイッチのセットからランダムに最適なスイッチが選択されます。