同様の問題を調査しながらこの質問に出くわしました。成層を減らすための液体の最適な添加。私の解決策はあなたの状況にも当てはまるようです。
液体A、B、およびCを30、20、10の割合(つまり、Aの30単位、Bの20単位、およびCの10単位)で混合する場合、すべてを追加すると成層になります。 A、次にすべてのB、そしてすべてのCのようになります。小さなユニットをミキシングする方が良いでしょう。たとえば、シーケンス[A、B、A、C、B、A]で単一ユニットの追加を行います。それは完全に層化を防ぎます。
私がそれを見つけたのは、優先度キューを使用して、一種のマージとして扱うことです。追加を説明する構造を作成した場合:
MergeItem
Item, Count, Frequency, Priority
頻度は「Nごとに1つ」として表されます。したがって、6回のうち3回追加されるAの頻度は2(6/3)です。
そして、最初に含まれるヒープを初期化します:
(A, 3, 2, 2)
(B, 2, 3, 3)
(C, 1, 6, 6)
次に、ヒープから最初のアイテムを削除して出力します。次に、カウントを1減らし、頻度による優先度を上げて、ヒープに追加します。結果のヒープは次のとおりです。
(B, 2, 3, 0)
(A, 2, 2, 4)
(C, 1, 6, 6)
次に、ヒープからBを削除し、出力して更新し、ヒープに追加し直します。
(A, 2, 2, 4)
(C, 1, 6, 6)
(B, 1, 3, 6)
そのように続けると、希望の混合物が得られます。カスタムコンパレーターを使用して、等しい優先度のアイテムがヒープに挿入されたときに、最も高い頻度の値(つまり、最も低い頻度)のアイテムが最初に順序付けられるようにします。
私は自分のブログで問題とその解決策のより完全な説明を書き、それを説明するいくつかの実用的なC#コードを提示しました。リスト内のアイテムの均等配布を参照してください。
コメント後に更新
私の問題はOPの問題に似ていると思うので、私の解決策は潜在的に有用だと思います。私は、OPの質問の観点から、私の回答をもっとフレーミングしないことをおpoびします。
私のソリューションは0、1、2ではなくA、B、Cを使用しているという最初の異論は、簡単に修正できます。それは単に命名法の問題です。「2つの1」ではなく「2つのA」について考えて発言する方が簡単でわかりやすいと思います。しかし、この議論のために、OPの命名法を使用するために、以下の出力を変更しました。
もちろん、私の問題は距離の概念を扱っています。「物を均等に広げる」場合は、距離が必要です。しかし、繰り返しますが、私の問題がOPの問題にどのように似ているかを適切に示さなかったことは、私の失敗でした。
OPが提供する2つの例を使用して、いくつかのテストを実行しました。あれは:
[1,1,2,2,3,3] // which I converted to [0,0,1,1,2,2]
[0,0,0,0,1,1,1,2,2,3]
私の命名法では、それらはそれぞれ[2,2,2]および[4,3,2,1]として表されます。つまり、最後の例では、「タイプ0の4アイテム、タイプ1の3アイテム、タイプ2の2アイテム、およびタイプ3の1アイテム」です。
テストプログラムを実行し(すぐ下で説明します)、結果を投稿しました。OPからの入力がないため、私の結果が彼に似ているか、悪いか、良いかはわかりません。また、誰も投稿していないため、自分の結果を他の人の結果と比較することもできません。
ただし、このアルゴリズムは、液体を混合する際の成層化を排除するという私の問題に対する優れたソリューションを提供すると言えます。そして、それは OPの問題に対する合理的な解決策を提供するように見えます。
以下に示す結果では、ブログエントリで詳述したアルゴリズムを使用し、初期優先度をに設定しFrequency/2
、より頻繁な項目を優先するようにヒープ比較子を変更しました。変更されたコードがここに表示され、変更された行がコメント化されています。
private class HeapItem : IComparable<HeapItem>
{
public int ItemIndex { get; private set; }
public int Count { get; set; }
public double Frequency { get; private set; }
public double Priority { get; set; }
public HeapItem(int itemIndex, int count, int totalItems)
{
ItemIndex = itemIndex;
Count = count;
Frequency = (double)totalItems / Count;
// ** Modified the initial priority setting.
Priority = Frequency/2;
}
public int CompareTo(HeapItem other)
{
if (other == null) return 1;
var rslt = Priority.CompareTo(other.Priority);
if (rslt == 0)
{
// ** Modified to favor the more frequent item.
rslt = Frequency.CompareTo(other.Frequency);
}
return rslt;
}
}
OPの最初の例でテストプログラムを実行すると、次のようになります。
Counts: 2,2,2
Sequence: 1,0,2,1,0,2
Distances for item type 0: 3,3
Stddev = 0
Distances for item type 1: 3,3
Stddev = 0
Distances for item type 2: 3,3
Stddev = 0
したがって、私のアルゴリズムは、すべてのカウントが等しいという些細な問題に対して機能します。
OPが投稿した2番目の問題については、次のようになりました。
Counts: 4,3,2,1
Sequence: 0,1,2,0,1,3,0,2,1,0
Distances for item type 0: 3,3,3,1
Stddev = 0.866025403784439
Distances for item type 1: 3,4,3
Stddev = 0.471404520791032
Distances for item type 2: 5,5
Stddev = 0
Distances for item type 3: 10
Stddev = 0
Standard dev: 0.866025403784439,0.471404520791032,0,0
私はそれを改善する明らかな方法を見ていません。アイテム0 [2,3,2,3]または2および3のその他の配置の距離を変更することができますが、それはアイテム1および/または2の偏差を変更します。 「最適」はこの状況です。頻度の高いアイテムまたは頻度の低いアイテムの偏差を大きくする方がよいでしょうか?
OPから他の問題がないため、私は彼の説明を使用して自分のいくつかを作成しました。彼は彼の投稿で言った:
典型的なリストには、さまざまな数量で最大15個の異なる値を持つ最大50個のアイテムがあります。
したがって、私の2つのテストは次のとおりです。
[8,7,6,5,5,4,3,3,2,2,2,1,1,1,1] // 51 items, 15 types
[12,6,5,4,4,3,3,3,2,2,2,1,1] // 48 items, 13 types
そして私の結果:
Counts: 8,7,6,5,5,4,3,3,2,2,2,1,1,1,1
Sequence: 0,1,2,3,4,5,7,6,0,1,2,8,9,10,4,3,0,1,5,2,0,1,3,4,6,7,14,11,13,12,0,2,5,1,0,3,4,2,8,10,9,1,0,7,6,5,3,4,2,1,0
Distances for item type 0: 8,8,4,10,4,8,8,1
Stddev = 2.82566363886433
Distances for item type 1: 8,8,4,12,8,8,3
Stddev = 2.76272565797339
Distances for item type 2: 8,9,12,6,11,5
Stddev = 2.5
Distances for item type 3: 12,7,13,11,8
Stddev = 2.31516738055804
Distances for item type 4: 10,9,13,11,8
Stddev = 1.72046505340853
Distances for item type 5: 13,14,13,11
Stddev = 1.08972473588517
Distances for item type 6: 17,20,14
Stddev = 2.44948974278318
Distances for item type 7: 19,18,14
Stddev = 2.16024689946929
Distances for item type 8: 27,24
Stddev = 1.5
Distances for item type 9: 28,23
Stddev = 2.5
Distances for item type 10: 26,25
Stddev = 0.5
Distances for item type 11: 51
Stddev = 0
Distances for item type 12: 51
Stddev = 0
Distances for item type 13: 51
Stddev = 0
Distances for item type 14: 51
Stddev = 0
2番目の例では:
Counts: 12,6,5,4,4,3,3,3,2,2,2,1,1
Sequence: 0,1,2,0,3,4,7,5,6,0,1,8,9,10,0,2,0,3,4,1,0,2,6,7,5,12,11,0,1,0,3,4,2,0,1,10,8,9,0,7,5,6,0,
4,3,2,1,0
Distances for item type 0: 3,6,5,2,4,7,2,4,5,4,5,1
Stddev = 1.68325082306035
Distances for item type 1: 9,9,9,6,12,3
Stddev = 2.82842712474619
Distances for item type 2: 13,6,11,13,5
Stddev = 3.44093010681705
Distances for item type 3: 13,13,14,8
Stddev = 2.34520787991171
Distances for item type 4: 13,13,12,10
Stddev = 1.22474487139159
Distances for item type 5: 17,16,15
Stddev = 0.816496580927726
Distances for item type 6: 14,19,15
Stddev = 2.16024689946929
Distances for item type 7: 17,16,15
Stddev = 0.816496580927726
Distances for item type 8: 25,23
Stddev = 1
Distances for item type 9: 25,23
Stddev = 1
Distances for item type 10: 22,26
Stddev = 2
Distances for item type 11: 48
Stddev = 0
Distances for item type 12: 48
Stddev = 0