0

I was trying to make smth like this... Basically im trying to shuffle music by their names. Everything works nicely, but i was wondering if theres a way for tihs random funcion not to repeat same numbers?

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
     button1.Enabled = true;

     DirectoryInfo dir = new DirectoryInfo(comboBox1.SelectedItem.ToString());
     count = dir.GetFiles().Length;
     label3.Text = "Files loaded: " + count.ToString();
}
private void button1_Click(object sender, EventArgs e)
{

     DirectoryInfo d = new DirectoryInfo(comboBox1.SelectedItem.ToString());
     FileInfo[] infos = d.GetFiles("*.mp3");
     int i = 1;

     foreach (FileInfo f in infos)
     {
         File.Move(f.FullName, Path.Combine(f.DirectoryName, i + ". " + f.Name));
         i = rnd.Next(1, count+1);
     }
}
maccettura
  • 10,514
  • 3
  • 28
  • 35
  • 1
    Have a look at [this](https://codereview.stackexchange.com/questions/61338/generate-random-numbers-without-repetitions) – Matt.G Feb 27 '20 at 17:39

3 Answers3

1

You could try something like this:

string path = comboBox1.SelectedItem.ToString();

var files = Directory.GetFiles(path, "*.mp3");
// Create list of shuffled numbers
var shuffledNumbers = Enumerable.Range(1, files.Length).ToArray().OrderBy(i => Guid.NewGuid());
// put the shuffled numbers into a stack
var indexes = new Stack<int>(shuffledNumbers);
foreach(var file in files) {
    // extract first item from the stack
    var index = indexes.Pop();
    // move file
    File.Move(file, Path.Combine(path, $"{index}. {Path.GetFileName(file)}"));
}

Basically I shuffle an array of sequential numbers using Guid.NewGuid as a order key.

Every time OrderBy will compare two items of the array, it will get a different, totally random value.

Using a Stack allows me to just pop the next number without having to use an indexer variable (but it's totally fine if you prefer that way).

Claudio Valerio
  • 2,302
  • 14
  • 24
  • I found this very helpfull, it does the job! – Aleksandar Gusic Feb 27 '20 at 19:27
  • `Every time OrderBy will compare two items of the array, it will get a different, totally random value.` No, it won't. While GUIDs would be unique, order might stay the same. F.e. UID pairs (12345, 23456) and (54321, 65432), while being unique, produce the same ordering – Severin Pappadeux Feb 28 '20 at 17:05
0

An easy way to do this is by creating a List of your files, where you put all the names. Then you shuffle that list by using something like the Fisher-Yates shuffle which gives you a guaranteed random sequence. Then you can just do a regular for-loop over the List and rename the files according to your counting variable.

This way, instead of trying to generate random numbers to rename objects, you just shuffle them like a deck of cards and rename them based on the new order. Also, note that a Fisher-Yates shuffle is O(n) as there is only a single execution needed to randomize any set of values.

Example code, based on your code:

DirectoryInfo d = new DirectoryInfo(comboBox1.SelectedItem.ToString());
List<FileInfo> infos = new List<FileInfo>(d.GetFiles("*.mp3"));

infos.FisherYatesShuffle(rnd);

for (int i = 0; i < files.Count; ++i)
{
    File.Move(f.FullName, Path.Combine(f.DirectoryName, i + ". " + f.Name));
}

And some (untested) Fisher-Yates-Shuffle algorithm I just hacked together as an extension method for lists:

public static void FisherYatesShuffle<T>(this IList<T> list, Random random)
{
    for (int i = 0; i < list.Count; i++)
    {
        int index = rnd.Next(0, list.Count - i);
        int lastIndex = list.Count - i - 1;
        T value = list[index];
        {
            T temp = list[num1];
            list[num1] = list[num2];
            list[num2] = temp;
        }
        list.RemoveAt(lastIndex);
        list.Add(value);
    }
}
Max Play
  • 3,717
  • 1
  • 22
  • 39
0

I am in agreement with Max's answer. If anyone finds it useful, I have a Nuget package called RandomShuffler:

DataJuggler.RandomShuffler.Core (Dot Net Core)

DataJuggler.Core.RandomShuffler (.Net Framework)

The difference between a Shuffler and a Random Number Generator is a shuffler pulls all the times, like a deck of cards and leads to more even distribution where random numbers can even out over several million years or more.

enter image description here

Get your min and max bounds of your List Or Array

using DataJuggler.RandomShuffler.Core;

int min = 0;
int max = List.Count -1;

// Create a new instance of a 'RandomShuffler' object.
Shuffler = new RandomShuffler(min, max, 1, 3);

This creates a list between your min and max, and the 1 as the 3rd parameter, means only 1 set (no duplicates).

Then each time you need a need a new value simply call Shuffler.PullNextItem:

// pull the next White value
int index = Shuffler.PullNextItem();

var songToPlay = List[index]; // not sure what properties you need

It automatically shuffles if you run out, and there are also overrides for cards.

Machavity
  • 30,841
  • 27
  • 92
  • 100