C# 随机播放列表算法
我需要创建一个随机顺序范围内的数字列表(例如从x到y),这样每个顺序都有相同的机会 我需要一个我用C#写的音乐播放器,以随机顺序创建播放列表 有什么想法吗 谢谢 编辑:我对更改原始列表不感兴趣,只需按随机顺序从范围中选取随机索引,以便每个顺序都有相同的机会 以下是我到目前为止写的:C# 随机播放列表算法,c#,algorithm,playlist,smart-playlist,C#,Algorithm,Playlist,Smart Playlist,我需要创建一个随机顺序范围内的数字列表(例如从x到y),这样每个顺序都有相同的机会 我需要一个我用C#写的音乐播放器,以随机顺序创建播放列表 有什么想法吗 谢谢 编辑:我对更改原始列表不感兴趣,只需按随机顺序从范围中选取随机索引,以便每个顺序都有相同的机会 以下是我到目前为止写的: public static IEnumerable<int> RandomIndexes(int count) { if (count > 0) {
public static IEnumerable<int> RandomIndexes(int count)
{
if (count > 0)
{
int[] indexes = new int[count];
int indexesCountMinus1 = count - 1;
for (int i = 0; i < count; i++)
{
indexes[i] = i;
}
Random random = new Random();
while (indexesCountMinus1 > 0)
{
int currIndex = random.Next(0, indexesCountMinus1 + 1);
yield return indexes[currIndex];
indexes[currIndex] = indexes[indexesCountMinus1];
indexesCountMinus1--;
}
yield return indexes[0];
}
}
公共静态IEnumerable随机索引(int计数)
{
如果(计数>0)
{
int[]索引=新的int[count];
int indexConsontMinus1=计数-1;
for(int i=0;i0)
{
int currIndex=random.Next(0,INDEXESCONNTMINUS1+1);
收益率指数[货币指数];
索引[currIndex]=索引[IndexExcontMinus1];
IndexeConntMinus1--;
}
收益率指标[0];
}
}
它正在工作,但唯一的问题是我需要在内存中分配一个大小为count
的数组。我正在寻找不需要内存分配的东西
谢谢。如果你不小心(例如,使用幼稚的洗牌算法),这实际上可能会很棘手。请看一看正确的值分布 一旦你有了洗牌算法,剩下的应该很简单 这是杰夫·阿特伍德的来信 最后,这是乔恩·斯基特的 编辑 我不相信有一种解决方案能够满足您的两个相互冲突的需求(第一,随机无重复,第二,不分配任何额外内存)。我相信您可能过早地优化了您的解决方案,因为内存影响应该可以忽略不计,除非您是嵌入式的。或者,也许我不够聪明,无法想出一个答案 下面的代码将使用Knuth-Fisher-Yates算法(稍加修改)创建一个均匀分布的随机索引数组。您可以缓存生成的数组,或者根据实现的其余部分执行任意数量的优化
private static int[] BuildShuffledIndexArray( int size ) {
int[] array = new int[size];
Random rand = new Random();
for ( int currentIndex = array.Length - 1; currentIndex > 0; currentIndex-- ) {
int nextIndex = rand.Next( currentIndex + 1 );
Swap( array, currentIndex, nextIndex );
}
return array;
}
private static void Swap( IList<int> array, int firstIndex, int secondIndex ) {
if ( array[firstIndex] == 0 ) {
array[firstIndex] = firstIndex;
}
if ( array[secondIndex] == 0 ) {
array[secondIndex] = secondIndex;
}
int temp = array[secondIndex];
array[secondIndex] = array[firstIndex];
array[firstIndex] = temp;
}
private static int[]BuildShuffledIndexArray(int size){
int[]数组=新的int[size];
Random rand=新的Random();
对于(int currentIndex=array.Length-1;currentIndex>0;currentIndex--){
int-nextIndex=rand.Next(当前索引+1);
交换(数组、currentIndex、nextIndex);
}
返回数组;
}
专用静态无效交换(IList数组、int firstIndex、int secondIndex){
if(数组[firstIndex]==0){
数组[firstIndex]=firstIndex;
}
if(数组[secondIndex]==0){
数组[secondIndex]=secondIndex;
}
int temp=数组[secondIndex];
数组[secondIndex]=数组[firstIndex];
数组[firstIndex]=温度;
}
注意:只要播放列表中的项目不超过65535个,就可以使用ushort
而不是int
将内存大小减半。如果大小超过ushort.MaxValue
,则始终可以通过编程方式切换到int
。如果我个人在一个播放列表中添加了超过65K个项目,我不会对内存利用率的提高感到震惊
还要记住,这是一种托管语言。虚拟机将始终保留比您使用的内存更多的内存,以限制它向操作系统请求更多RAM和限制碎片的次数
private static int[] BuildShuffledIndexArray( int size ) {
int[] array = new int[size];
Random rand = new Random();
for ( int currentIndex = array.Length - 1; currentIndex > 0; currentIndex-- ) {
int nextIndex = rand.Next( currentIndex + 1 );
Swap( array, currentIndex, nextIndex );
}
return array;
}
private static void Swap( IList<int> array, int firstIndex, int secondIndex ) {
if ( array[firstIndex] == 0 ) {
array[firstIndex] = firstIndex;
}
if ( array[secondIndex] == 0 ) {
array[secondIndex] = secondIndex;
}
int temp = array[secondIndex];
array[secondIndex] = array[firstIndex];
array[firstIndex] = temp;
}
编辑
好的,最后一次尝试:我们可以调整性能/内存权衡:您可以创建整数列表,然后将其写入磁盘。然后在文件中保留一个指向偏移量的指针。然后,每当您需要一个新号码时,您只需要处理磁盘I/O。也许您可以在这里找到一些平衡,只需将N个大小的数据块读入内存,其中N是您熟悉的数字
洗牌算法似乎需要做很多工作,但如果你死心塌地地想保存内存,那么至少这是一个选择。就个人而言,对于音乐播放器而言,我不会生成一个洗牌列表,然后播放该列表,然后在该列表用完时生成另一个洗牌列表,但可以执行以下操作:
IEnumerable<Song> GetSongOrder(List<Song> allSongs)
{
var playOrder = new List<Song>();
while (true)
{
// this step assigns an integer weight to each song,
// corresponding to how likely it is to be played next.
// in a better implementation, this would look at the total number of
// songs as well, and provide a smoother ramp up/down.
var weights = allSongs.Select(x => playOrder.LastIndexOf(x) > playOrder.Length - 10 ? 50 : 1);
int position = random.Next(weights.Sum());
foreach (int i in Enumerable.Range(allSongs.Length))
{
position -= weights[i];
if (position < 0)
{
var song = allSongs[i];
playOrder.Add(song);
yield return song;
break;
}
}
// trim playOrder to prevent infinite memory here as well.
if (playOrder.Length > allSongs.Length * 10)
playOrder = playOrder.Skip(allSongs.Length * 8).ToList();
}
}
IEnumerable GetSongOrder(列出所有歌曲)
{
var playOrder=新列表();
while(true)
{
//此步骤为每首歌曲分配整数权重,
//对应于下一次播放的可能性。
//在更好的实现中,这将查看
//歌曲,并提供更平滑的上升/下降。
var-weights=allSongs.Select(x=>playOrder.LastIndexOf(x)>playOrder.Length-10?50:1);
int position=random.Next(weights.Sum());
foreach(可枚举范围(allSongs.Length)中的int i)
{
位置-=重量[i];
如果(位置<0)
{
var song=所有歌曲[i];
播放顺序。添加(歌曲);
回归歌曲;
打破
}
}
//修剪播放顺序,以防止此处出现无限内存。
如果(playOrder.Length>allSongs.Length*10)
playOrder=playOrder.Skip(allSongs.Length*8.ToList();
}
}
这将使歌曲按顺序挑选,只要它们最近没有播放过。这提供了从一次洗牌结束到下一次洗牌结束的“更平滑”过渡,因为下一次洗牌的第一首歌曲可能与上一次洗牌的歌曲相同,概率为1/(总歌曲),而此算法的概率较低(且可配置)再次听到最后一首x歌的机会。我认为您应该坚持当前的解决方案(o
0: {1, 2, 3}
1: {1, 3, 2}
2: {2, 1, 3}
3: {2, 3, 1}
4: {3, 1, 2}
5: {3, 2, 1}
public class MaximalLFSR
{
private int GetFeedbackSize(uint v)
{
uint r = 0;
while ((v >>= 1) != 0)
{
r++;
}
if (r < 4)
r = 4;
return (int)r;
}
static uint[] _feedback = new uint[] {
0x9, 0x17, 0x30, 0x44, 0x8e,
0x108, 0x20d, 0x402, 0x829, 0x1013, 0x203d, 0x4001, 0x801f,
0x1002a, 0x2018b, 0x400e3, 0x801e1, 0x10011e, 0x2002cc, 0x400079, 0x80035e,
0x1000160, 0x20001e4, 0x4000203, 0x8000100, 0x10000235, 0x2000027d, 0x4000016f, 0x80000478
};
private uint GetFeedbackTerm(int bits)
{
if (bits < 4 || bits >= 28)
throw new ArgumentOutOfRangeException("bits");
return _feedback[bits];
}
public IEnumerable<int> RandomIndexes(int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException("count");
int bitsForFeedback = GetFeedbackSize((uint)count);
Random r = new Random();
uint i = (uint)(r.Next(1, count - 1));
uint feedback = GetFeedbackTerm(bitsForFeedback);
int valuesReturned = 0;
while (valuesReturned < count)
{
if ((i & 1) != 0)
{
i = (i >> 1) ^ feedback;
}
else {
i = (i >> 1);
}
if (i <= count)
{
valuesReturned++;
yield return (int)(i-1);
}
}
}
}
static void Main(string[] args)
{
while (true)
{
Console.Write("Enter a count: ");
string s = Console.ReadLine();
int count;
if (Int32.TryParse(s, out count))
{
MaximalLFSR lfsr = new MaximalLFSR();
foreach (int i in lfsr.RandomIndexes(count))
{
Console.Write(i + ", ");
}
}
Console.WriteLine("Done.");
}
}
public static IEnumerable<int> RandomIndexes(int count) {
Random rand = new Random();
bool[] used = new bool[count];
int i;
for (int counter = 0; counter < count; counter++) {
while (used[i = rand.Next(count)]); //i = some random unused value
used[i] = true;
yield return i;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
class Program
{
static void Main(string[] a)
{
Random random = new Random();
List<int> list1 = new List<int>(); //source list
List<int> list2 = new List<int>();
list2 = random.SequenceWhile((i) =>
{
if (list2.Contains(i))
{
return false;
}
list2.Add(i);
return true;
},
() => list2.Count == list1.Count,
list1.Count).ToList();
}
}
public static class RandomExtensions
{
public static IEnumerable<int> SequenceWhile(
this Random random,
Func<int, bool> shouldSkip,
Func<bool> continuationCondition,
int maxValue)
{
int current = random.Next(maxValue);
while (continuationCondition())
{
if (!shouldSkip(current))
{
yield return current;
}
current = random.Next(maxValue);
}
}
}
}
const int MaxItemsToShuffle = 20;
public static IEnumerable<int> RandomIndexes(int count)
{
Random random = new Random();
int indexCount = Math.Min(count, MaxItemsToShuffle);
int[] indexes = new int[indexCount];
if (count > MaxItemsToShuffle)
{
int cur = 0, subsetCount = MaxItemsToShuffle;
for (int i = 0; i < count; i += 1)
{
if (random.NextDouble() <= ((float)subsetCount / (float)(count - i + 1)))
{
indexes[cur] = i;
cur += 1;
subsetCount -= 1;
}
}
}
else
{
for (int i = 0; i < count; i += 1)
{
indexes[i] = i;
}
}
for (int i = indexCount; i > 0; i -= 1)
{
int curIndex = random.Next(0, i);
yield return indexes[curIndex];
indexes[curIndex] = indexes[i - 1];
}
}