1

Can you help me to find a way to randomize arrays? E.g.:

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

after randomizing, the result should be stored in another array.

And when you randomize again it should be compared with the values stored in the second array, if that value exist the program must randomize again.

Artemix
  • 2,113
  • 2
  • 23
  • 34
RevyToxic
  • 75
  • 2
  • 13
    While i agree this isnt a terribly well worded or researched question, i think its a bit rough to downvote a brand new user without at least an explanation on how to improve their answer – undefined May 10 '13 at 06:28
  • Create a random index using `Random` like: `Random rand = new Random(); int randomIndex = rand.Next(0, arrayInt.Length);`, later check your other array/list to see if the item exist against that index from original array. – Habib May 10 '13 at 06:30
  • Does SO has translators? Sometimes it feels like it would be better if one could ask in his own native language and have it translated by a professional translator (who is also familiar with the context - C# or general programming in this case). – SimpleVar May 10 '13 at 06:34
  • 2
    I think as @LukeMcGregor, mentioned its worth saying why you got downvotes... If you hover over the down arrow next to your vote count it will tell you your question shows no research effort and its unclear. In any case, take a look at http://www.dotnetperls.com/random - (I've not read this but if it doesn't mention this, make sure you look at seeding, as random isn't really random) – Sayse May 10 '13 at 06:40
  • @TimSchmelter His English is more than enough to understand the sentences, but it is probably not his native language. If he explains to someone his question in his native language, I'm sure that he can be more understandable and informative. I wasn't crying over grammar. – SimpleVar May 10 '13 at 07:05
  • Is the OP perhaps referring to **shuffling**? – user541686 May 10 '13 at 07:33
  • @Mehrdad I was also thinking it might be shuffle, but "compared with the values stored in the second array" wouldn't really make sense, since they'd be the same, just shuffled. – Bernhard Barker May 10 '13 at 07:39
  • What exactly does 'randomize' mean? Do you have to create an array of random size or are you given an array or array size? Do you have to assign completely random values to each element or do you simply have to reorder (i.e. shuffle) a given array? Also, for [so] questions, one should generally show an attempt at solving the problem oneself. – Bernhard Barker May 10 '13 at 07:57
  • @YoryeNathan Getting off topic... I don't know about professional translators, but community translation may be a viable option, though I'm not sure we want [so] to go that route. The somewhat terrible translation of [Google translate](http://translate.google.com/) is usually sufficient for one to get a general idea of what the asker wants, but not even perfect translation will help an incomplete, generally unclear question. – Bernhard Barker May 10 '13 at 08:19
  • @CodesInChaos: The question is not a duplicate, the answers are using Random and OrderBy to shuffle the array, but OP does not constrain answerers to use them. – Artemix May 10 '13 at 08:39
  • @Artemix Several answers over there don't use `OrderBy`. For example Jon Skeet posted a Fisher-Yates shuffle, which is pretty much the ideal solution. – CodesInChaos May 10 '13 at 08:40
  • 1
    @Artemix In response to your edit - You're assuming by 'randomize' OP means shuffle. I'm not completely convinced that this is the case. – Bernhard Barker May 10 '13 at 08:41
  • @Dukeling Removed 'shuffle', though most answerers think that this is what OP wants to do. – Artemix May 10 '13 at 09:15

5 Answers5

7

Here's an approach using Enumerable.OrderBy to order the input-array with a random variable. After the new sequence is generated it'll be compared with the input array with 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();
}

This example-code generates 10 new arrays which are added to a List. It is ensured that the new array is different to the previous one:

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

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • +1, but it is slow, orderby works in O(n log n), shuffle can be done in O(n) – Arsen Mkrtchyan May 10 '13 at 08:18
  • @ArsenMkrt: Thanks. Should be fast enough in most cases. I assume you'll get an `OutOfMemoryException` before you have performance problems. Also, since OP explicitly said that he wants new arrays, a shuffle of the old loses importance since it also needs to create a new collection first. – Tim Schmelter May 10 '13 at 08:25
  • New array should be allocated in both cases, so it is not a case. but yes, agree that in most of the cases orderby is enough. – Arsen Mkrtchyan May 10 '13 at 10:51
1

using 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();

and you can randomize any type like this

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();
maxlego
  • 4,864
  • 3
  • 31
  • 38
  • 1
    This will not create random arrays since the random instance is initialized in the method. Try to create multiple arrays in a loop. They will use the same seed derived from the time, hence the result will be the same array. – Tim Schmelter May 10 '13 at 07:41
  • This demonstrates the problem: http://ideone.com/HXv9tn So pass the random as parameter to the method (or use a random instance as class-field). – Tim Schmelter May 10 '13 at 08:00
  • yup.. these thing happen when not running the code before submitting. Hard to compete with your nimble fingers :) – maxlego May 10 '13 at 08:07
0

First part of your question is about shuffling an array. A good algorithm is the Fisher-Yates shuffle.

The next part about comparing the result to the original is a bit more vague. I assume that you want to create a random shuffle that guarantees that all elements are shuffled to a new position. E.g.

  • [0, 1, 2] => [1, 2, 0] is OK but
  • [0, 1, 2] => [2, 1, 0] is not OK because 1 stays in place

I have created some extensions for doing this (note that this is a generic solution with element type 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;
  }

}

The Randomize method implements the Fisher-Yates shuffle. The RandomizeUniquely uses this method and tries to create a shuffle that satifies the condition described above. The method simply tries until a satisfactory shuffle is found. Note that this algorithm may not terminate. E.g. if the source only has one element no unique shuffle can be found. Also, if the source contains duplicates a solution may not exist.

To use the method simply call it like this:

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

The code can be improved by validating parameters and deciding how to handle the non-termination problem described above.

Martin Liversage
  • 104,481
  • 22
  • 209
  • 256
0

Hope this helps. Uses a secure crypto provider and a secure hash for the comparison - overkill, so feel free to adjust the providers used :)

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());
        }
    }
}
Nathan
  • 6,095
  • 2
  • 35
  • 61
0

OrderBy is nice way to shuffle, but it uses sorting which is O(n log n). Shuffle can be done in O(n).

Here is pseudo code from wikipedia

 for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]
Arsen Mkrtchyan
  • 49,896
  • 32
  • 148
  • 184