用随机数重命名
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
如果你 运行 出局,它会自动洗牌,而且还有覆盖卡片的功能。
我正在尝试制作这样的东西... 基本上,我试图通过他们的名字来随机播放音乐。 一切正常,但我想知道是否有办法让随机函数不重复相同的数字?
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
如果你 运行 出局,它会自动洗牌,而且还有覆盖卡片的功能。