2

これは、リストの最初のN要素をランダムに並べ替えるための私のコードです。

int upper = 1;
if (myList.Count > 1)
{
    Random r = new Random();
    upper = Math.Min(maxNumberOfPackets, myList.Count);
    for (int i = 0; i < upper; i++)
    {
        int randInd = r.Next(i, myList.Count);
        var temp = myList[i];
        myList[i] = myList[randInd];
        myList[randInd] = temp;
    }
}

さて、今私はEnumerableのみを使用する「必要性」を持っています(何らかの理由で、それをEnumerableに変換したくありません)。

ご存知のとおり、Enumerableでも同じことができますか?苦痛はX位置の要素へのアクセスだと思います...

興味深々...

4

6 に答える 6

6

IEnumerable[<T>]ソースの場合、通常は1回だけ読み取ることができると想定する必要があります。もちろん、ソースを消費するまで、長さを確実に知ることはできません。ランダム化するには、通常、バッファリングが必要になります。あなたはただするほうがよいでしょう:

var list = source.ToList();

既存のリストランダム化コードを使用するだけです。IEnumerable[<T>]もちろん、入力と出力を同じように保ちたい場合は、そのリストをもう一度返すことができます。

本当にやりたいのであれば、最初からスクランブルをかけることができます。

static IEnumerable<T> ScrambleStart(this IEnumerable<T> source,
        int numberToScramble)
{
  using(var iter = source.GetEnumerator()) {
    List<T> list = new List<T>();
    while(iter.MoveNext()) {
        list.Add(iter.Current);
        if(list.Count == numberToScramble) break;
    }
    ScrambleList(list); // your existing code
    foreach(var item in list) yield return item;
    while(iter.MoveNext()) {
        yield return iter.Current;
    }
  }
}
于 2012-06-11T09:27:55.007 に答える
3

IEnumerable'転送専用カーソル'を表します。これは、(たとえばデータベースから)ストリーミングされたデータを返す場合があります。したがって、列挙可能ファイルを(ランダムであるかどうかに関係なく)順序付けできるようにするには、順序付けを決定できるようにすべての値が必要であるという理由だけで、それをキャッシュする必要があることを意味します。とは言うものの、これには演算子を使用できますがEnumerable.OrderBy、繰り返しになりますが、内部でキャッシュを実行します。

var r = new Random();

IEnumerable sorted = myList.OrderBy(i => r.Next());
于 2012-06-11T09:28:38.707 に答える
2

リザーバーサンプリングを使用する必要があります。

于 2012-06-11T09:35:10.963 に答える
1

あなたは常にIEnumerableそれを何らかの方法でランダム化するためにあなたを消費しなければならないので、あなたはそれを呼び出すだけ.ToList()であなたのソート方法を使うことができます。

IEnumerableは無限の数のアイテムを持つことができるという事実を考慮してください。

于 2012-06-11T09:30:03.560 に答える
1

以下の実装では、元の入力から(疑似)ランダムシーケンスが生成されます。(ランダムが疑似ランダムであることを除けば)疑似性はシーケンスの長さを推測することに基づいていますが、もちろんほとんどの場合、これは間違っています。maxJumpの長さより短い長さのシーケンスではランダムになります。要素の数がmaxJumpの半分またはそれより多いことが判明した場合は、より高度なランダム性を考慮して増加します。

public IEnumerable<T> Randomize(this IEnumerable<T> source,int maxJump = 1000){
    var cache = new List<T>();
    var r = new Random();
    var enumerator = source.GetEnumerator();
    var totalCount = 1;
    while(enumerator.MoveNext()){
       var next = r.Next(0,maxJump);
       if(next < cache.Count){
          //the element we are searching for is in the cache
          yield return cache[next];
          cache.RemoveAt(next);
       } else {
         next = next - cache.Count;
         do{
          totalCount++;
          if(next == 0){
             //we've found the next element so yield
             yield return enumerator.Current;
          } else {
             //not the element we are looking for so cache
             cache.Insert(r.Next(0,cache.Count),enumerator.Current);
          }
          --next;
        }while(next > 0 && enumerator.MoveNext());
        //if we've reached half of the maxJump length
        //increase the max to allow for higher randomness
        if("*totalCount == maxJump){
            maxJump *= 2;
        }
      }
    }
    //Yield the elements in the cache
    //they are already randomized
    foreach(var elem in cache){
          yield return elem;
    }
}
于 2012-06-11T10:01:55.380 に答える
0

これは機能しますが、比較的少数の要素をランダムに選択する大きなシーケンスでは非常に非効率的です(ソースシーケンスのすべての要素を反復処理するため)。

/// <summary>Randomly selects items from a sequence.</summary>
/// <typeparam name="T">The type of the items in the sequence.</typeparam>
/// <param name="sequence">The sequence from which to randomly select items.</param>
/// <param name="count">The number of items to randomly select from the sequence.</param>
/// <param name="sequenceLength">The number of items in the sequence among which to randomly select.</param>
/// <param name="rng">The random number generator to use.</param>
/// <returns>A sequence of randomly selected items.</returns>
/// <remarks>This is an O(N) algorithm (N is the sequence length).</remarks>

public static IEnumerable<T> RandomlySelectedItems<T>(IEnumerable<T> sequence, int count, int sequenceLength, System.Random rng)
{
    if (sequence == null)
    {
        throw new ArgumentNullException("sequence");
    }

    if (count < 0 || count > sequenceLength)
    {
        throw new ArgumentOutOfRangeException("count", count, "count must be between 0 and sequenceLength");
    }

    if (rng == null)
    {
        throw new ArgumentNullException("rng");
    }

    int available = sequenceLength;
    int remaining = count;
    var iterator  = sequence.GetEnumerator();

    for (int current = 0; current < sequenceLength; ++current)
    {
        iterator.MoveNext();

        if (rng.NextDouble() < remaining/(double)available)
        {
            yield return iterator.Current;
            --remaining;
        }

        --available;
    }
}
于 2012-06-11T09:45:35.890 に答える