多个List <int> </int>的混洗组合

时间:2013-11-27 12:09:45

标签: c# linq list generics combinations

与此类似的问题:Combination of List<List<int>>

然而,改组使得这非常重要,IMO需要一个完全不同的解决方案。我很高兴被证明是错的,但这并不像改组结果那么容易,因为结果列表不适合内存:

我需要合并3个 LARGE 列表:

List<int> A = new List<int> {1, 2, 3, ...};
List<int> B = new List<int> {4, 5, 6, ...};
List<int> C = new List<int> {7, 8, 9, ...};

输出是新的List<object> { {1, 4, 7}, {1, 4, 8}, ... }

但是,我需要对结果列表进行洗牌。如果它们在组合时仍然是图案化的,那么对每个单独的列表进行混洗将是不够的,并且虽然3个列表将适合于存储器,但是所有3个列表的组合将不会。一个混洗的索引列表显然太大而无法存储在内存中。

我已经尝试了许多不同的方法,但我找不到一种方法来随机化订单而不先加载每个项目。这可能吗?谢谢!

3 个答案:

答案 0 :(得分:0)

如果所有列表都具有相同的大小:

var aShuffle = new List<int>(A.Count);
aShuffle.AddRange(A.Shuffle());
var bShuffle = new List<int>(B.Count);
bShuffle.AddRange(B.Shuffle());
var cShuffle = new List<int>(C.Count);
cShuffle.AddRange(C.Shuffle());

List<Trio> trios = new List<Trio>(aShuffle.Count);
for (int i = 0; i < aShuffle.Count; i++)
{
    trios.Add(new Trio { Value1 = aShuffle[i], Value2 = bShuffle[i], Value3 = cShuffle[i] });
}

使用此结构:

public struct Trio
{
    public int Value1 { get; set; }
    public int Value2 { get; set; }
    public int Value3 { get; set; }
}

我已经使用此扩展程序来收集(Fisher-Yates)集合:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
{
    return source.Shuffle(new Random());
}

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    if (source == null) throw new ArgumentNullException("source");
    if (rng == null) throw new ArgumentNullException("rng");

    return source.ShuffleIterator(rng);
}

private static IEnumerable<T> ShuffleIterator<T>(
    this IEnumerable<T> source, Random rng)
{
    List<T> buffer = source.ToList();
    for (int i = 0; i < buffer.Count; i++)
    {
        int j = rng.Next(i, buffer.Count);
        yield return buffer[j];

        buffer[j] = buffer[i];
    }
}

Demonstration

Value1: 3 Value2: 5 Value3: 8
Value1: 1 Value2: 6 Value3: 9
Value1: 6 Value2: 4 Value3: 7
Value1: 2 Value2: 8 Value3: 33

答案 1 :(得分:0)

您是否尝试过使用Union LINQ-Extension?

您可以使用它来统一列表,然后随机播放结果:)

答案 2 :(得分:0)

扩展我的评论...

首先,确定你不能在物理内存中进行这种随机播放。如果您的项目少于UInt32.MaxValue,您仍然可以use managed arrays。另外,您只需要为每件商品存储int(或可能long),而不是商品本身。

那么,你有这么多的项目你不能在内存中计算它们吗?

首先为文件中的每个项目存储一个数字。

using (var f = File.Create("shuffle.bin"))
{
    var length = A.Count * B.Count * C.Count;

    f.SetLength(length * sizeof(long));
    for (long i = 0; i < length; i++)
    {
        var bytes = BitConverter.GetBytes(i);
        f.Write(bytes, 0, sizeof(long));
    }
}

彻底改变这个文件需要花费很长时间。因此,我们可以根据需要随时将一个条目随机播出。提取一个数字不需要太长时间。

using (var f = File.Open("shuffle.bin", FileMode.Open))
{
    // Calculate how many numbers are left in the file.
    long itemCount = f.Length / sizeof(long);
    if (itemCount == 0)
    {
        // We have used all the items - create another file, or throw
        // an exception, or whatever you want.
    }

    // You need an equivalent of `Random.Next(int max)` here that works on `long`s.
    long index = NextLong(itemCount);

    // Read out the number you've shuffled out.
    f.Seek(index * sizeof(long), SeekOrigin.Begin);
    var rtnBytes = new byte[sizeof(long)];
    f.Read(rtnBytes, 0, sizeof(long));

    // Read out the last number.
    f.Seek((itemCount - 1) * sizeof(long), SeekOrigin.Begin);
    var rplcBytes = new byte[sizeof(long)];
    f.Read(rplcBytes, 0, sizeof(long));

    // Replace the shuffled-out number with the last number.
    f.Seek(index * sizeof(long), SeekOrigin.Begin);
    f.Write(rplcBytes, 0, sizeof(long));

    // Trim the now-duplicate last number off the end of the file.
    f.SetLength((itemCount - 1) * sizeof(long));

    // Get the long and do with it what you want.
    long rtnLong = BitConverter.ToInt64(rtnBytes, 0);
}

然后,您可以将此long转换为三个索引,如下所示:

int indexA = (int)(rtnLong % (long)A.Count);
rtnLong /= a.Count;
int indexB = (int)(rtnLong % (long)B.Count);
rtnLong /= b.Count;
int indexC = (int)rtnLong;