用随机数重命名

Rename with random 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);
     }
}

一个简单的方法是创建文件列表,在其中放置所有名称。然后你通过使用 Fisher-Yates shuffle 之类的东西来洗牌这个列表,它给你一个有保证的随机序列。然后你可以在列表上做一个常规的 for 循环,并根据你的计数变量重命名文件。

这样,您无需尝试生成随机数来重命名对象,您只需像一副纸牌一样洗牌并根据新顺序重命名它们。另外,请注意 Fisher-Yates 洗牌是 O(n),因为只需要执行一次即可随机化任何一组值。

示例代码,基于您的代码:

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));
}

还有一些(未经测试的)Fisher-Yates-Shuffle 算法,我刚刚将其组合在一起作为列表的扩展方法:

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);
    }
}

您可以尝试这样的操作:

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)}"));
}

基本上,我使用 Guid.NewGuid 作为排序键来随机排列一组序列号。

每次OrderBy比较数组的两个项目,它会得到一个不同的、完全随机的值。

使用 Stack 可以让我只弹出下一个数字,而无需使用索引器变量(但如果您喜欢这种方式,那完全没问题)。

我同意Max的回答。如果有人觉得它有用,我有一个名为 RandomShuffler 的 Nuget 包:

DataJuggler.RandomShuffler.Core(点网核心)

DataJuggler.Core.RandomShuffler(.Net 框架)

洗牌器和随机数生成器之间的区别在于洗牌器一直在拉动,就像一副纸牌一样,并导致更均匀的分布,其中随机数可以在数百万年或更长时间内均匀分布。

获取列表或数组的最小和最大范围

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

这会在您的最小值和最大值之间创建一个列表,第 3 个参数为 1,表示只有 1 组(没有重复项)。

然后每次需要一个新值时只需调用 Shuffler.PullNextItem:

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

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

如果你 运行 出局,它会自动洗牌,而且还有覆盖卡片的功能。