【问题标题】:Get random values from an Array从数组中获取随机值
【发布时间】:2016-06-19 10:44:41
【问题描述】:

我正在尝试通过更改索引来随机更改数组的元素。没关系。现在的问题是,由于随机总是随机的,我可以得到两个相同的结果。

例如:

星期一:

歌曲 1

歌曲 2

歌曲 3

星期二:

歌曲 2

歌曲 1

歌曲 3

星期三:

歌曲 1

歌曲 2

歌曲 3

等等……

还有来自

的列表

星期一

星期三

在这种情况下是一样的。我需要控制它,但正如您在代码中看到的那样,一旦我从某一天得到列表,我就打印它。我想把它放在一个数组或元组上并检查该元组是否存在,但我认为它太复杂了。我想也许我可以制作自己的随机函数。但是,我也不确定该解决方案。关于如何解决这种情况的任何想法?谢谢!

这是我目前的代码:

    static string[] songs = new string[] { "song1", "song2", "song3" };
    static string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
    private static Random random = new Random();

/* Random number between lower and higher, inclusive */
    public static int rand(int lower, int higher)
    {
        int r = lower + (int)(random.Next(0, 2) * (higher - lower));

        return r;
    }

    /* pick M elements from original array. Clone original array so that
   7 * we don’t destroy the input. */
    public static string[] pickMRandomly()
    {
        string[] subset = new string[songs.Length];
        string[] array = (string[])songs.Clone();
        for (int j = 0; j < songs.Length; j++)
        {
            int index = rand(j, array.Length - 1);
            subset[j] = array[index];
            array[index] = array[j]; // array[j] is now “dead”
        }
        return subset;
    }

    public static void playListCreation()
    {
        for (int j = 0; j < days.Length; j++)
        {
            var result =pickMRandomly();
            System.Console.WriteLine(days[j]);
            foreach (var i in result)
            {
                System.Console.WriteLine(i + " ");
            }
            System.Console.WriteLine("/n");
        }
    }
}

【问题讨论】:

  • 我对这个问题有点困惑。是否正在生成重复项?如果您使用 3 首歌曲进行测试,则只有 6 种不同的排列方式和一周中的 7 天,因此您将得到一个副本。您测试的歌曲越多,重复出现的可能性就越小。无论如何,您是在问如何比较两个数组以查看它们是否相同?
  • 我不明白这个问题。对于任何长度的歌曲列表,该列表的可能排序数量有限 N。正如前面的评论所指出的,在 N 天后,您可以保证重复一个订单。对于一个显着长度的列表,N 很大,随机重复序列的可能性很小,并且试图避免在 N 天之前重复序列将需要存储所有先前的排列,这可能是很多数据。这真的是你想做的吗?
  • 不管怎样,您当然可以改进您现在拥有的代码:无需创建 两个 不同的数组;只需复制原件和shuffle it。此外,由于您无论如何都想要一个非破坏性的随机播放,而不是随机播放字符串引用本身,而是随机播放一组索引,按顺序初始化(即{ 0, 1, 2, ... })。对索引数组进行洗牌还可以最大限度地减少您需要存储的数据的大小,以便记住最后(最多)N 次洗牌,这样您就可以避免重复顺序。
  • 您知道您的public static int rand(int lower, int higher) 方法返回lowerhigher 吗?它不返回/* Random number between lower and higher, inclusive */

标签: c# arrays random


【解决方案1】:

如果我的理解正确,您不仅希望每天随机排列歌曲,还希望每天独特(且随机)排列歌曲。

我能想到的保证这一点的唯一方法是找出所有可能的歌曲组合并随机排序 - 然后从列表中为每天挑选不同的组合。

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflowAnswer
{
    class Program
    {
        static string[] songs = new string[] { "song1", "song2", "song3" };
        static string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };

        static void Main(string[] args)
        {
            var rnd = new Random();
            var allCombinationsInRandomOrder = GetCombinations(songs, songs.Length)
                .Select(combination => new { Combination = combination, Order = rnd.Next() })
                .OrderBy(entry => entry.Order)
                .Select(entry => entry.Combination);

            var dayIndex = 0;
            foreach (var combination in allCombinationsInRandomOrder)
            {
                var day = days[dayIndex];
                Console.WriteLine(day);
                Console.WriteLine(string.Join(", ", combination));

                dayIndex++;
                if (dayIndex >= days.Length)
                    break;
            }
            Console.ReadLine();
        }

        private static IEnumerable<IEnumerable<string>> GetCombinations(IEnumerable<string> songs, int numberOfSongsInGeneratedLists)
        {
            if (songs == null)
                throw new ArgumentNullException(nameof(songs));
            if (numberOfSongsInGeneratedLists <= 0)
                throw new ArgumentOutOfRangeException(nameof(numberOfSongsInGeneratedLists));
            if (numberOfSongsInGeneratedLists > songs.Count())
                throw new ArgumentOutOfRangeException("can't ask for more songs in the returned combinations that are provided", nameof(numberOfSongsInGeneratedLists));

            if (numberOfSongsInGeneratedLists == 1)
            {
                foreach (var song in songs)
                    yield return new[] { song };
                yield break;
            }

            foreach (var combinationWithOneSongTooFew in GetCombinations(songs, numberOfSongsInGeneratedLists - 1))
            {
                foreach (var song in songs.Where(song => !combinationWithOneSongTooFew.Contains(song)))
                    yield return combinationWithOneSongTooFew.Concat(new[] { song });
            }
        }
    }
}

【讨论】:

  • 感谢您的回答!我有一个问题。为什么在这个 foreach foreach (var combinationWithOneSong in GetCombinations(songs, numberOfSongsInGeneratedLists - 1)) 我需要 recursevly 调用函数?为什么我不能只遍历 songs 数组?谢谢!!
  • 简单循环的问题在于,我在编写代码时不知道歌曲列表应该有多长,我想不出一个好的方法来做到这一点。如果我只想要每个播放列表中的两首歌曲,那么我将遍历歌曲数组,然后在嵌套循环中再次遍历它。如果我想要三首歌曲,那么我将有第三个嵌套循环,如果我想要四首,那么我需要第四个嵌套循环。使用递归函数一次完成每个级别对我来说似乎是最容易的,因为我可以轻松地将“n”级称为深度。
【解决方案2】:

据我了解,您想创建一个随机播放列表,如果该播放列表之前已创建,您想生成另一个(直到它是唯一的)。您可以这样做的一种方法是将某种散列添加到HashSet 并查看它之前是否已生成。例如,

bool HashSet<int> playlistHashes = new HashSet<int>();
private bool CheckIfUnique(string[] playlist)
{
    //HashSet returns false if the hash already exists 
    //(i.e. playlist already likely to have been created)
    return playlistHashes.Add(string.Join("",playlist).GetHashCode());
}

然后,一旦您生成了播放列表,您就可以调用该方法并查看它是否返回 false。如果它返回 false,则该播放列表顺序之前已创建,因此您可以再次生成。使用上述技术意味着song1, song2, song3song3, song2, song1 不同,因此顺序很重要。

正如我在对该问题的评论中所提到的,如果您使用 3 首歌曲进行测试,那么一周中只有 7 天和 6 种不同的排列,所以您将得到一个副本。

旁注,GetHashCode 可能会抛出“误报”,但由您决定它的可能性有多大,以及影响是否真的有任何意义,因为无论如何都会生成新的播放列表。更多信息的好线程here。如果 GetHashCode 在这里不够用,那么有许多哈希技术可能会降低冲突的可能性。

【讨论】:

  • 为什么这被否决了,这个问题并不完全清楚,但似乎是 OP 所要求的?
【解决方案3】:

假设您手头有3 Songs,并且想要为一周中的每一天(7 天)分配唯一的组合。这是不可能的,因为您只能用这三个组合制作六个独特的组合。所以肯定有一个重复的序列。如果您在此收藏中添加另一首歌曲(让它成为 "song4"),您将获得 24 独特的歌曲序列。我已经包含了一个 sn-p,它可以帮助您获得这些独特歌曲序列的组合。

string[] songs = new string[] { "song1", "song2", "song3", "song4" };       
int numberOfSongs = songs.Count();
var collection = songs.Select(x => x.ToString()); ;
for (int i = 1; i < numberOfSongs; i++)
{
    collection = collection.SelectMany(x => songs, (x, y) => x + "," + y);                            
}
List<string> SongCollections = new List<string>();
SongCollections.AddRange(collection.Where(x => x.Split(',')
                                   .Distinct()
                                   .Count() == numberOfSongs)
                                   .ToList());

现在SongCollections 将包含24 独特的4 歌曲序列。 (如果您选择 3 首歌曲,那么您将获得 6 独特的序列)。您可以从这些集合中随机选择序列并根据需要分配给日期。

现在让我使用Dictionary&lt;string, int&gt; dayCollectionMap 将集合映射到一天(注意:这里我使用4 歌曲的集合,因为3 对7 days 来说是不够的)。考虑下面的 sn-p:

Dictionary<string, int> dayCollectionMap = new Dictionary<string, int>();
string[] days = new string[] { "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday" };
Random randomCollection = new Random();
foreach (string day in days)
{
    int currentRandom = randomCollection.Next(0, SongCollections.Count());
    if (!dayCollectionMap.Any(x => x.Value == currentRandom))
    {
        dayCollectionMap.Add(day, currentRandom);
    }
    else
    {
        // The collection is already taken/ Add another random sequence
        while (true)
        {
            currentRandom = randomCollection.Next(0, SongCollections.Count());
            if (!dayCollectionMap.Any(x => x.Value == currentRandom))
            {
                dayCollectionMap.Add(day, currentRandom);
                break;
            }

        }
    }

}

这样您就可以使用代码选择Wednesday的歌曲集

  var songCollectionForWed = SongCollections[dayCollectionMap["Wednesday"]];

【讨论】:

  • @down-voter : 请告诉我原因,如果合理,那么它将帮助我改进答案
  • 有人也对另一个答案投了反对票,但不明白为什么。 +1 以平衡它,直到给出一个有效的理由
猜你喜欢
  • 2018-06-19
  • 1970-01-01
  • 2022-10-08
  • 2010-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多