【问题标题】:Randomise an array of integers [duplicate]随机化一个整数数组[重复]
【发布时间】:2013-05-10 06:24:53
【问题描述】:

你能帮我找到一种随机化数组的方法吗?例如:

int[] arrayInt = { 1, 2, 3, 4, 5, 6, 7 };

随机化后,结果应该存储在另一个数组中。

当你再次随机化时,它应该与存储在第二个数组中的值进行比较,如果该值存在,程序必须再次随机化。

【问题讨论】:

  • 虽然我同意这不是一个措辞或研究过的问题,但我认为在没有至少解释如何改进他们的答案的情况下对一个全新用户投反对票有点粗鲁
  • 使用Random 创建一个随机索引,例如:Random rand = new Random(); int randomIndex = rand.Next(0, arrayInt.Length);,稍后检查您的其他数组/列表以查看该项目是否存在于原始数组中的该索引。
  • SO 有翻译吗?有时感觉如果可以用自己的母语提问并由专业翻译人员翻译会更好(他也熟悉上下文 - 在这种情况下是 C# 或一般编程)。
  • 我认为@LukeMcGregor 提到了值得一说的为什么你会被否决...如果你将鼠标悬停在你的投票数旁边的向下箭头上,它会告诉你你的问题没有研究工作并且不清楚.无论如何,看看dotnetperls.com/random - (我没有读过这篇文章,但如果它没有提到这一点,请确保你查看播种,因为随机并不是真正随机的)
  • @Artemix 响应您的编辑 - 您假设“随机化”OP 意味着随机播放。我并不完全相信是这种情况。

标签: c# arrays random


【解决方案1】:

这是一种使用Enumerable.OrderBy 对带有random 变量的输入数组进行排序的方法。生成新序列后,它将与 SequenceEqual 的输入数组进行比较:

public static T[] UniqueRandomArray<T>(T[] input, Random rnd)
{
    if (input.Length <= 1) throw new ArgumentException("Input array must have at least two elements, otherwise the output would be the same.", "input");
    IEnumerable<T> rndSeq;
    while (input.SequenceEqual(rndSeq = input.OrderBy(x => rnd.Next())));
    return rndSeq.ToArray();
}

此示例代码生成 10 个新数组,这些数组被添加到列表中。确保新的数组与之前的不同:

Random rnd = new Random();
List<int[]> randomArrays = new List<int[]>();
int[] arrayInt1 = { 1, 2, 3, 4, 5, 6, 7 };
randomArrays.Add(arrayInt1);

for (int i = 0; i < 10; i++)
{
    int[] lastArray = randomArrays[randomArrays.Count - 1];
    int[] randomArray = UniqueRandomArray(lastArray, rnd);
    randomArrays.Add(randomArray);
}

Demo

【讨论】:

  • +1,但是很慢,orderby在O(n log n)内工作,shuffle可以在O(n)内完成
  • @ArsenMkrt:谢谢。在大多数情况下应该足够快。我假设您在遇到性能问题之前会收到OutOfMemoryException。此外,由于 OP 明确表示他想要新数组,因此旧数组的洗牌失去了重要性,因为它还需要首先创建一个新集合。
  • 两种情况都应该分配新数组,所以不是这样。但是,是的,同意在大多数情况下 orderby 就足够了。
【解决方案2】:

使用 linq

        Random rand = new Random();
        int[] arrayInt =
            new[] {1, 2, 3, 4, 5, 6, 7}.Select(x => new {x, r = rand.Next()})
                                       .OrderBy(x => x.r)
                                       .Select(x => x.x)
                                       .ToArray();

你可以像这样随机化任何类型

public static class RandomExt
{
    public static T[] RandomizeOrder<T>(this T[] array)
    {
        var rand = new Random();
        return array.Select(x => new {x, r = rand.Next()})
                                       .OrderBy(x => x.r)
                                       .Select(x => x.x)
                                       .ToArray();
    }
}

int[] arrayInt = new[] {1, 2, 3, 4, 5, 6, 7}.RandomizeOrder();

【讨论】:

  • 这不会创建随机数组,因为随机实例是在方法中初始化的。尝试在循环中创建多个数组。他们将使用从时间派生的相同种子,因此结果将是相同的数组。
  • 这说明了问题:ideone.com/HXv9tn 所以将随机作为参数传递给方法(或使用随机实例作为类字段)。
  • yup.. 这些事情发生在提交前未运行代码时。很难与你的灵活手指竞争 :)
【解决方案3】:

您问题的第一部分是关于洗牌的。一个好的算法是Fisher-Yates shuffle

关于将结果与原始结果进行比较的下一部分有点模糊。我假设您想要创建一个随机洗牌,以保证所有元素都被洗牌到一个新位置。例如

  • [0, 1, 2] => [1, 2, 0] 可以,但是
  • [0, 1, 2] => [2, 1, 0] 不正确,因为 1 保持原位

我为此创建了一些扩展(请注意,这是一个元素类型为 T 的通用解决方案):

static class EnumerableExtensions {

  static Random random = new Random();

  public static IEnumerable<T> Randomize<T>(this IEnumerable<T> source) {
    var list = source.ToList();
    for (var k = 0; k < list.Count; k += 1) {
      var j = random.Next(k, list.Count);
      Swap(list, k, j);
    }
    return list;
  }

  public static IEnumerable<T> RandomizeUniquely<T>(this IEnumerable<T> source) {
    while (true) {
      var randomized = source.Randomize();
      var isNotUnique = source
        .Zip(randomized, (a, b) => Equals(a, b))
        .Any(equal => equal);
      if (!isNotUnique)
        return randomized;
    }
  }

  static void Swap<T>(IList<T> list, Int32 i, Int32 j) {
    var temp = list[i];
    list[i] = list[j];
    list[j] = temp;
  }

}

Randomize 方法实现了 Fisher-Yates 洗牌。 RandomizeUniquely 使用此方法并尝试创建满足上述条件的随机播放。该方法简单地尝试直到找到令人满意的洗牌。请注意,此算法可能不会终止。例如。如果源只有一个元素,则无法找到唯一的 shuffle。此外,如果源包含重复项,则可能不存在解决方案。

要使用该方法,只需像这样调用它:

var randomized = Enumerable.Range(1, 7).RandomizeUniquely();

可以通过验证参数和决定如何处理上述非终止问题来改进代码。

【讨论】:

    【解决方案4】:

    希望这会有所帮助。使用安全加密提供程序和安全哈希进行比较 - 矫枉过正,因此请随意调整使用的提供程序 :)

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Security.Cryptography;
    using System.Collections;
    using System.Collections.Concurrent;
    
    namespace RandomiseArray
    {
        static class Program
        {
            static void Main(string[] args)
            {
                // source array
                string[] s = new string[] { "a", "b", "c", "d", "e", "f", "g" };
    
                // number of unique random combinations required
                int combinationsRequired = 5;
                var randomCombinations = s.Randomise(System.Text.UnicodeEncoding.Unicode.GetBytes, combinationsRequired);
    
                foreach (var c in randomCombinations)
                    Console.WriteLine(c.Aggregate((x, y) => x + "," + y));
    
                Console.ReadLine();
            }
    
            /// <summary>
            /// given a source array and a function to convert any item in the source array to a byte array, produce x unique random sequences
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="source"></param>
            /// <param name="byteFunction"></param>
            /// <param name="x"></param>
            /// <returns></returns>
            public static IEnumerable<IEnumerable<T>> Randomise<T>(this IEnumerable<T> source, Func<T, byte[]> byteFunction, int x)
            {
                var foundValues = new ConcurrentDictionary<byte[], T[]>();
                int found = 0;
                do
                {
                    T[] y = source.Randomise().ToArray();
                    var h = y.Hash(byteFunction);
                    if (!foundValues.Keys.Contains(h))
                    {
                        found++;
                        foundValues[h] = y;
                        yield return y;         // guaranteed unique combination  (well, within the collision scope of SHA512...)
                    }
                } while (found < x);
            }
    
            public static IEnumerable<T> Randomise<T>(this IEnumerable<T> source)
            {
                using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
                    return source.OrderBy(i => rng.Next());
            }
    
            public static int Next(this RNGCryptoServiceProvider rng)
            {
                byte[] buf = new byte[4];
                rng.GetBytes(buf);
                return BitConverter.ToInt32(buf, 0);
            }
    
            public static byte[] Hash<T>(this IEnumerable<T> items, Func<T, byte[]> getItemBytes)
            {
                using (SHA512CryptoServiceProvider sha = new SHA512CryptoServiceProvider())
                    return sha.ComputeHash(items.SelectMany(i => getItemBytes(i)).ToArray());
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      OrderBy 是一种很好的洗牌方式,但它使用 O(n log n) 的排序。 Shuffle 可以在 O(n) 内完成。

      这是来自wikipedia的伪代码

       for i from n − 1 downto 1 do
             j ← random integer with 0 ≤ j ≤ i
             exchange a[j] and a[i]
      

      【讨论】:

        猜你喜欢
        • 2014-10-17
        • 1970-01-01
        • 1970-01-01
        • 2012-02-10
        • 2012-10-23
        • 1970-01-01
        • 1970-01-01
        • 2019-02-15
        相关资源
        最近更新 更多