如何将已排序的已排序列表重新排序为批次,以便每个批次具有尽可能相互远离的整数?

时间:2012-11-28 17:22:25

标签: c# algorithm list math

我有一个List int的{​​{1}} s(按降序排列)我需要重新排序,这样每个批次(例如20s)都有int s,如同尽可能远离彼此。

因此,例如,如果列表包含以下预订:

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

我将以4批次处理它们,一个好方法是将预订分成4个列表(12/3)并向下移动每个4个列表中的每个列表并附加到新创建的列表中订购清单?因此,新列表将按以下顺序包含预订:

  • 1
  • 5
  • 9
  • 2
  • 6
  • 10
  • 3
  • 7
  • 11
  • 4
  • 8
  • 12

我很欣赏我的问题并不容易理解,因此查看我的代码可能更容易!我提出了以下方法,并想知道它是否是实现我所追求的最好/最高效的方式:

var bookings = new List<int>();

// <snip>import bookings ints from csv file into bookings list</snip>

bookings = bookings.Distinct().OrderByDescending(x => x).ToList();

int chunkSize = 20;

// get number of chunks/batches
int chunkCount = (int)Math.Ceiling((double)bookings.Count/(double)chunkSize);

// split booking list into number of chunks
// add the ith booking from each chunk into new list
// ToChunks() extension method from http://stackoverflow.com/a/6852288/1578713
var chunkList = bookings.ToChunks(chunkCount).ToList();
var reorderedBookings = new List<int>();
for (int i=0; i<chunkCount; i++)
{
    // last chunk may be smaller than chunkSize hence the check
    reorderedBookings.AddRange(from chunk in chunkList where chunk.Count() > i 
        select chunk.ToList()[i]);
}

bookings = reorderedBookings;

背景(如果你真的想知道)

我的问题是我正在处理List intint(从csv文件导入并订购的预订)并行批次(比如说20个)和更接近的(更连续)这些预订在列表中,更有可能抛出异常(超出我的控制范围)。

这是在同步运行数千个预订(因此不会抛出异常)或并行运行20多个之间的权衡,并且偶尔会抛出所述异常的可能性。我选择了后一种方法,因为它更快,任何导致异常的预订都可以在异步任务完成后同步运行。

*拨打几项服务,依次传递每个预订{{1}}

2 个答案:

答案 0 :(得分:1)

好的,所以这很简单:

public static IEnumerable<IEnumerable<T>> BatchAndSeparate<T>(IList<T> source, int batchSize)
{
    int numBatches = (int)Math.Ceiling(source.Count / (double)batchSize);

    for (int i = 0; i < numBatches; i++)
    {
        var buffer = new List<T>();
        for (int j = i; j < source.Count; j += numBatches)
        {
            buffer.Add(source[j]);
        }
        yield return buffer;
    }
}

一般算法是,对于每个批次,您的索引都以该批次的编号开始(零索引)并添加每次迭代的批次数。

可以像:

一样使用
var batches = BatchAndSeparate(Enumerable.Range(1, 13).ToList(), 3);

foreach (var batch in batches)
{
    Console.WriteLine(string.Join(" ", batch));
}

这将打印:

1 6 11
2 7 12
3 8 13
4 9
5 10

以这种方式显示时,很容易看出它是正确的。只需沿着每一列向下看,数字会增加1,然后从最左边的列向右移动。

答案 1 :(得分:1)

假设Servy对你想要的是正确的,你可以这样做

int i = 0;
var grouped = bookings.OrderBy(x => (i++) % chunkSize);

int i = 0;
var grouped = bookings.GroupBy(x => (i++) % chunkSize);

第一个会给你一个列表,第二个会把它分成单独的列表。