Shuffle list of strings consistently - c#

Using C# 6 I have a list of names alphabetically ordered:
List<String> names = getAlphabeticallyOrderedNames();
I need to shuffle the names but I want to get the same result every time. So I cannot use:
List<String> shuffledNames = names.OrderBy(x => Guid.NewGuid());
I then tried something like:
List<String> shuffledNames = names.OrderBy(x => "d2fda3b5-4089-43f9-ba02-f68d138dee49");
Or
List<String> shuffledNames = names.OrderBy(x => Int32.MaxValue);
But the names are not shuffled ...
How can I solve this?

You can use a standard shuffle algorithm, such as the one from this answer:
Suitably modified to add a seed parameter, it would look like this:
public static void Shuffle<T>(IList<T> list, int seed)
{
var rng = new Random(seed);
int n = list.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Then to shuffle in a repeatable way, just specify the same seed for the shuffle for each repeated shuffle:
List<String> names = getAlphabeticallyOrderedNames();
int seed = 12345;
Shuffle(names, seed);

An Enumerable extension using Yield and having Seed value as parameter (Online Example):
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Int32? seed = null) {
List<T> buffer = source.ToList();
Random random = seed.HasValue ? new Random(seed.Value) : new Random();
Int32 count = buffer.Count;
for (Int32 i = 0; i < count; i++) {
Int32 j = random.Next(i, count);
yield return buffer[j];
buffer[j] = buffer[i];
}
}

Try ordering by the hash value
var shuffled = names.OrderBy(n=>n.GetHashCode());

Related

Take 30 random elements from list

I have a list of strings containing loads of hashtags with texts.
ex #csharp #java ect.
each hashtag is a own string in a List. I now want to randomly always console.writeline 30 items of that list.
List
List<string> Hashtags = new List<string>();
Writeline
foreach (var x in Hashtags) {
Console.WriteLine(x);
}
Ideally i never wanna have the same hashtag in the random 30.
You should try this:
var rnd = new Random();
foreach (var x in Hashtags.OrderBy(x => rnd.Next()).Take(30))
{
Console.WriteLine(x);
}
This has O(n^2) complexity, but is easily readable.
If you want efficiency try a Fisher-Yates Shuffle, it's O(n), but less readable:
var take = 30;
var rnd = new Random();
for (var i = 0; i < (Hashtags.Count < take ? Hashtags.Count : take); i++)
{
var j = rnd.Next(Hashtags.Count);
(Hashtags[i], Hashtags[j]) = (Hashtags[j], Hashtags[i]);
}
foreach (var x in Hashtags.Take(take))
{
Console.WriteLine(x);
}
Keep in mind, though, that it's best to instantiate Random once per thread, so this would be a better way:
[ThreadStatic]
private static Random rnd = new Random();
This should do. It is efficient, as it shuffles only the required amount of items, not the whole collection. You pass how many elements you want to fetch from the array as parameter(elementCount).
private static Random randomGenerator = new Random();
static void Main()
{
var hashtags = new List<string>() { "c#", "javascript", "ef", "asp.net" };
var result = GetRandomItems<string>(hashtags, 2);
foreach (var item in result)
{
Console.WriteLine(item);
}
}
private static IEnumerable<T> GetRandomItems<T>(IEnumerable<T> collection, int elementCount)
{
var collectionCount = collection.Count();
if (elementCount > collectionCount)
{
elementCount = collectionCount;
}
var collectionCopy = collection.ToList();
var randomIndex = randomGenerator.Next(0, collectionCopy.Count);
for (var index = 0; index < elementCount; index++)
{
var tempElement = collectionCopy[index];
collectionCopy[index] = collectionCopy[randomIndex];
collectionCopy[randomIndex] = tempElement;
randomIndex = randomGenerator.Next(index + 1, collectionCopy.Count);
}
return collectionCopy.Take(elementCount);
}
Call 30 times Random.next
https://learn.microsoft.com/en-us/dotnet/api/system.random.next
var random = new Random();
//calls this n times in a loop:
Console.writeline(Hashtags[random.next(Hashtags.Count])
Got it working myself, little more verbose but hopefully easier to follow.
var random = new Random();
var uniques = Hashtags;
for (var i = 0; i < 30; i++) {
var index = random.Next(0, uniques.Count());
Console.WriteLine(uniques[index]);
uniques.RemoveAt(index);
}

Array Contains Performance

I have a program which works with arrays and at some point I have to check if a lsit of values are inside an array, the function that does this takes about 70% of the program CPU time, so I was wondering if there's a way to do this more efficiently.
These are my functions:
private static int[] GenerateRow(int length, Random RNG)
{
int[] row = InitalizeRow(length);
int index = 0;
while (!AreAllNumbersGenerated(row))
{
int value = RNG.Next(0, length);
if (!RowContains(row, value))
{
row[index] = value;
index++;
}
}
return row;
}
private static bool AreAllNumbersGenerated(int[] row)
{
for (int i = 0; i < row.Length; i++)
if(!RowContains(row, i))
return false;
return true;
}
private static bool RowContains(int[] row, int value)
{
for (int i = 0; i < row.Length; i++)
if (row[i] == value)
return true;
return false;
}
All the work is by AreAllNumbersGenerated(), then by RowContains() and finally by this line:
if (row[i] == value)
That this functions take most of the time is normal, since they do heavy work, but I wanted to know if there's a better way to do this.
EDIT:
private static int[] InitalizeRow(int length)
{
int[] row = new int[length];
for (int i = 0; i < length; i++)
row[i] = -1;
return row;
}
You're wasting a lot of work by doing a linear scan for every number throughout the array. What you should instead do is walk the array once and start observing the last number you've seen. If you see any gaps, return false. If you reach the end of the array, return true. This is O(N) instead of O(N^2) like you have it now.
Like so:
public static bool AreAllNumbersGenerated(int[] row)
{
var last = 0;
return row.Aggregate(true, (l, r) => l && r == last++);
}
You can make some additional optimizations that won't affect your big-O complexity, e.g. using a proper loop and early-exiting.
EDIT: I'm assuming here that you're expecting the items to occur sequentially. If this isn't the case, you can easily convert the array to a structure with ~O(1) lookups in O(N), then do the check in O(N) as well. For large input, this is still better than your O(N^2) approach above
Like so:
public static bool AreAllNumbersGenerated2(int[] row)
{
var available = row.ToDictionary(x => x);
return Enumerable.Range(0, row.Length).All(i => available.ContainsKey(i));
}
EDIT: From what I'm seeing in the comments it looks like the goal of this code is to populate an array with a contiguous sequence of numbers in a random order. I didn't realize that was the goal, and if that is the case, shuffling is certainly better than reattempting to generate over and over. But what is even better than generating the array then shuffling (again, for sufficiently large input) is to simply generate the array by assigning each number to a randomly sampled index without substitution. Not as easy to LINQ out, but you can figure it out.
As others in the comments have pointed out, the most efficient way to do this is to generate an array with the values you want and shuffle it:
public static void Shuffle<T>(T[] arr, Random r)
{
for (int i = arr.Length - 1; i > 0; --i)
{
int j = r.Next(i + 1);
T tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
public static int[] Generate(int length, Random r)
{
var arr = new int[length];
for(int i = 0; i < length; ++i)
{
arr[i] = i;
}
Shuffle(arr, r);
return arr;
}
then
var arr = Generate(10, new Random());
foreach (int i in arr) { Console.WriteLine(i); }
I found a method, thanks to #sous2817 comment, that works perfectly for me.
public static int[] GetRandomNumbers(int count, Random RNG)
{
HashSet<int> randomNumbers = new HashSet<int>();
for (int i = 0; i < count; i++)
while (!randomNumbers.Add(RNG.Next(count))) ;
return randomNumbers.ToArray();
}
This works ~10 times faster then what I was doing and works nicely with the rest of my program.I needed the code to be "linear" so other solutions mess my program. Anyway, thanks to everyone that helped :)
Instead of shuffle, you can generate the possible values in a list and take values at random positions. That way you can start using the items even before the whole row is generated:
IEnumerable<int> RandomRange(int count) {
var random = new Random();
var list = new int[count];
for (int i = 0; i < count; i++) list[i] = i;
while (count > 0) {
var i = random.Next(count);
yield return list[i] ;
list[i] = list[--count];
}
}
sample use:
foreach(var item in RandomRange(10))
// ...

How can I add same numbers in existing list?

My function to generate random numbers is this:
public static List<int> GetRandomNumbers(int count)
{
HashSet<int> randomNumbers = new HashSet<int>();
Random random = new Random();
for (int i = 0; i < count; i++)
{
while(!randomNumbers.Add(random.Next(100, 999)));
}
return randomNumbers.ToList();
}
This is how I call it:
private void button1_Click(object sender, EventArgs e)
{
List<int> list = new List<int>();
list = GetRandomNumbers(5);
for (int i = 0; i < list.Count; i++)
{
MessageBox.Show(list[i].ToString());
}
}
Ok, so, what I want to do is, for example, if the list generated is the following:
152 582 254 891 421
I want to make the list be:
152 152 582 582 254 254 891 891 421 421
Basically, each number to be added one more time after itself in the list. How can I do this? (It would be good if you include a simple way to do this considering time not a problem, and maybe a better way to save time)
Thanks
Your question is unclear.
You can take a list (not a HashSet) with repeating elements and move every repetition to immediately follow the first occurrence like this:
var numbers = Enumerable.Range(1, count).Select(i => random.NextInt(0, 300));
return numbers.GroupBy(i => i).SelectMany(s => s).ToList();
You can take an existing list and make each element repeat twice like this:
list.SelectMany(o => Enumerable.Repeat(o, 2));
You could change your function slightly to:
private static Random random = new Random();
public static List<int> GetRandomNumbers(int count)
{
int value;
List<int> values = new List<int>();
HashSet<int> randomNumbers = new HashSet<int>();
for (int i = 0; i < count; i++)
{
while (!randomNumbers.Add(value = random.Next(100, 999)));
values.Add(value);
values.Add(value);
}
return values;
}
I think you just want something like this:
var list2 = new List<int>(list1.Count * 2);
foreach (var n in list1)
{
list2.Add(n);
list2.Add(n);
}
That will loop through the list and put each value into the second list twice.
I think you need to change your function as follows:
public static List<int> GetRandomNumbers(int count)
{
List<int> Result = new List<int>();
Random random = new Random();
for (int i = 0; i < count; i++)
{
int NewNumber = random.Next(100, 999);
Result.Add(NewNumber);
Result.Add(NewNumber);
}
return Result;
}
I tried the function above, it worked just like you said.

Array randomizing method. moving to a list

so i've been working on a project, and i made this method to take up to 16 values in an array and randomize them into a list. i thought this should have worked but it didnt, whenever it runs it crashes the program but it compiles just fine.
array has "numOfTeams" amount of indexes
private List<string> randomizer()
{
List<string> myList = new List<string>();
Random rand = new Random();
int randomVar;
while (myList.Count < numOfTeams)
{
randomVar = rand.Next(0, numOfTeams + 1);
if (array[randomVar] != "null")
{
myList.Add(array[randomVar]);
array[randomVar] = "null";
}
}
return myList;
}
randomVar = rand.Next(0, numOfTeams);
I implemented a method for shuffeling a IList, based on the algorythm of D. E. Knuth (Knuth shuffle or Fisher–Yates shuffle).
Maybe this gives you some hints:
public static void Shuffle<T>(IList<T> list)
{
Random rand = new Random();
int index;
T tmp;
for (int i = 1; i < list.Count; ++i)
{
index = rand.Next(i + 1);
tmp = list[i];
list[i] = list[index];
list[index] = tmp;
}
}

preserving the indecies before shuffling an array

I use the following to shuffle my byte arrays which is working great and it preserves the indecies of the original order.
because i want to write a deshuffle method, which returns the items to the same order the were in before shuffling, to do this i need to record the indecies before shuffeling so i did the following
public static Random rnd=new Random();
public static void Shuffle(this byte[] Bytes,out int[] indecies)
{
var list = Bytes.Select((b, i) => new Tuple<int, byte>(i, b)).ToArray();
int n = list.Length;
while (n > 1)
{
n--;
int k = rnd.Next(n + 1);
Tuple<int, byte> value = list[k];
list[k] = list[n];
list[n] = value;
}
Bytes = list.Select(tuple => tuple.Item2).ToArray();
indecies = list.Select(tuple => tuple.Item1).ToArray();
}
i am not sure if its the best way but could anyone suggest a way to avoid creating Tuple or creating an object?
Why not simply create another, shuffled array, and keep original array? That even will take less memory, than keeping array of integer indexes.
public static byte[] ToShuffledArray(this byte[] bytes)
{
return bytes.OrderBy(b => rnd.Next()).ToArray();
}
Usage:
byte[] array = { 1, 2, 3, 4, 5 };
byte[] shuffledArray = array.ToShuffledArray();
No output parameters, no integer array of indexes.
if anyone wants it the c way then here it is
var indecies =new byte[Bytes.Length];
for (byte i = 0; i < Bytes.Length; i++)
{
indecies[i] = i;
}
int n = Bytes.Length;
while (n > 1)
{
n--;
int k = ThreadSafeRandom.ThisThreadsRandom.Next(n + 1);
byte value = Bytes[k];
byte index = indecies[k];
Bytes[k] = Bytes[n];
indecies[k] = indecies[n];
Bytes[n] = value;
indecies[n] = index;
}

Categories