おおよその解決策は次のとおりです。ゼロベースのインデックスとアイテムを含むタプルを返します。(あなたx
のようなダミー値だけでなく、アイテムが重要かもしれないと思いました)場合によっては最適な間隔を選択しませんが、常に近いと思います(つまり、ギャップは必要以上に大きくなりません) 、常に正しい数のアイテムを返します。
public static IEnumerable<Tuple<int, T>> SplitItems<T>(IEnumerable<T> items, int count)
{
var itemList = items.ToList();
int lastRowCount = itemList.Count % count;
int wholeRowItemCount = itemList.Count - lastRowCount;
// return full rows: 0 <= i < wholeRowCount * count
for (int i = 0; i < wholeRowItemCount; i++)
{
yield return Tuple.Create(i % count, itemList[i]);
}
if (lastRowCount > 0)
{
//return final row: wholeRowCount * count <= i < itemList.Count
double offset = (double)count / (lastRowCount + 1);
for (double j = 0; j < lastRowCount; j++)
{
int thisIntPos = (int)Math.Round(j * count / (lastRowCount + 1) + offset, MidpointRounding.AwayFromZero);
yield return Tuple.Create(thisIntPos, itemList[wholeRowItemCount + (int)j]);
}
}
}
使用方法の例として:
Console.WriteLine(string.Join("\r\n", SplitItems(Enumerable.Range(1, 12), 5)));
// prints
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(0, 6)
(1, 7)
(2, 8)
(3, 9)
(4, 10)
(2, 11)
(3, 12)
(最後の行には 2 ~ 3 の項目があり、0 ~ 1 および 4 に空のスペース/ギャップがあるのに対し、y
s を使用したソリューションにはサイズ 1 のギャップしかないため、これは最適ではありません)
また、あなたの例とは一致しませんが (ゼロベースのインデックスでは 0、2、4 になります)、次の例は、ギャップ サイズが最小化されているため、これまでに定義したアルゴリズムを満たします。(1 と 3 にギャップがあるあなたのものではなく、インデックス 0 と 2 に 1 サイズのギャップ)0, 2, 4
が実際に よりも優れている場合は、その理由を1, 3, 4
正確に決定し、それをアルゴリズム定義に追加する必要があります。
Console.WriteLine(string.Join("\r\n", SplitItems(Enumerable.Range(1, 3), 5)));
// prints
(1, 1)
(3, 2)
(4, 3)
実際、これは一種の制限付きパーティションの問題です。d
時間ごとにアイテムを分割する場合、可能な限り最小の部分を超えないh
パーティションを検索する必要があります。たとえば、2 つの項目を 5 時間で分割する場合: 最適な解は 1+1+1です。これは、部分が 3 つ以下であるためです。これが最善の方法です。解が一つではない例として、5時間で3項目が1+1になっていますが、 、 、など、並べ方はさまざまです。h-d
h-d
max(parts)
max(parts) == 1
0,2,4
1,3,4
0,2,3