【问题标题】:Trying to get a random sort order试图获得随机排序顺序
【发布时间】:2017-01-17 12:05:07
【问题描述】:

使用 C#/Asp.Net。

我正在努力实现以下目标:

我有一个报价清单 - 有时有多个产品价格相同。

此外,有些结果是附属(赞助)的,因此我们也需要优先考虑这些结果。

这是调用的方法:

    public IEnumerable<PriceQuote> BestQuote(int take = 0)
    {
        var q = Quotes.Where(x => x.TotalRepayable == MinPrice)
            .Shuffle()
            .OrderByDescending(x => x.ProductDetail.Product.IsSponsored);

        return take == 0 ? q : q.Take(take);
    }

代码选择具有最低可用价格的商品。然后的想法是将它们按完全随机的顺序排序,然后按赞助标志降序(赞助 = 1 而不是 0)再次排序,然后取所需的结果。

我首先将它们洗牌以获得随机顺序 - 从随机列表中我想首先获取赞助项目 - 然后在必要时用非赞助项目填充空间。理论上,赞助商和非赞助商每次都会随机排列。

Example in natural order:

product1 (not sponsored)
product2 (sponsored)
product3 (not sponsored)
product4 (sponsored)
product5 (not sponsored)
product6 (sponsored)

Shuffle randomly:

product3 (not sponsored)
product1 (not sponsored)
product2 (sponsored)
product6 (sponsored)
product5 (not sponsored)
product4 (sponsored)

Order by sponsored first keeping randomness:

product2 (sponsored) <-- pick these first
product6 (sponsored)
product4 (sponsored)
product3 (not sponsored)
product1 (not sponsored)
product5 (not sponsored)

这是我的随机播放方法:

    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> @this)
    {
         if (@this.Count() <= 1) return @this;

        return @this.ShuffleIterator(new Random());
    }

    static IEnumerable<T> ShuffleIterator<T>(this IEnumerable<T> source, Random rng)
    {
        var buffer = source.ToList();

        for (int i = 0; i < buffer.Count; i++)
        {
            int j = rng.Next(i, buffer.Count);
            yield return buffer[j];

            buffer[j] = buffer[i];
        }
    }

我遇到的问题是,当我为不同的报价连续多次调用 BestQuote 方法时,我往往会得到相同的结果。例如,我的列表包含 6 种产品,我每次选择第一个结果进行 3 次调用,很可能所有 3 次调用的顺序相同。并非总是如此 - 存在一些差异,但匹配的比不匹配的多。

Call 1: product2 <-- 
Call 2: product2 <--
Call 3: product2 <-- this is a common scenario where there seems to be no randomness

【问题讨论】:

  • 当您使用单个静态 Random 实例时会发生什么?没有参数 new Random() 使用当前时间作为种子,如果你将它们实例化在一起,你会得到相同的时间 = 相同的种子 = 相同的值。
  • 首先,检查this almost-duplicate question 以获得正确的随机播放实现。其次,Random 返回一个 缩放 到您请求的范围的浮点数。如果您的列表包含 4 个项目,则有 25% 的机会下一个双精度数将缩放为相同的整数。如果您请求一个 large 整数然后取其模数对计数,您会得到更好的结果,例如 rng.Next(int.MaxValue)%buffer.Count
  • Random() 使用基于时间的种子。如果你快速调用你的方法,你会得到同样的结果。您应该使用静态 Random() 类。
  • @PanagiotisKanavos 在 NET Core 中不再适用。仍然是 applies 到完整的 .NET Framework。

标签: c# linq random


【解决方案1】:

试试这个:

        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> @this)
        {
            if (@this.Count() <= 1) return @this;
            Random rand = new Random();
            return @this.Select(x => new { x = x, r = rand.Next() }).OrderBy(x => x.r).Select(x => x.x);
        }

【讨论】:

  • 这只会为每个对象生成一个随机数。 +1
  • 为什么每个对象需要多个随机数?
  • 我不是。其他答案确实如此。你的答案是更好的答案,这是一种恭维。
  • 您应该将new Random() 的声明作为静态类级变量从方法中推出。否则,如果Shuffle 被快速连续调用,结果将不是随机的。
【解决方案2】:

我是这样随机排序的:

().OrderBy(p => Guid.NewGuid())

因此,每个项目都有一个唯一且随机的 Guid,并且在每次调用中,您都可以获得完全不同的排序 IEnumerable。

在你的情况下,我会这样做,没有任何扩展方法:

public IEnumerable<PriceQuote> BestQuote(int take = 0)
{
    var q = Quotes.Where(x => x.TotalRepayable == MinPrice)
        .OrderBy(x => Guid.NewGuid())
        .ThenByDescending(x => x.ProductDetail.Product.IsSponsored);

    return take == 0 ? q : q.Take(take);
}

我不确定顺序应该是什么顺序,也许两者都一样,但如果上面的代码不起作用,你可以这样尝试:

public IEnumerable<PriceQuote> BestQuote(int take = 0)
{
    var q = Quotes.Where(x => x.TotalRepayable == MinPrice)
        .OrderBy(x => x.ProductDetail.Product.IsSponsored)
        .ThenByDescending(x => Guid.NewGuid());

    return take == 0 ? q : q.Take(take);
}

编辑

感谢@Maarten,这是使用扩展的最终解决方案:

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> @this)
{
    if (@this.Count() <= 1) return @this;
    return @this.Select(x => new { x = x, g = Guid.NewGuid() }).OrderBy(x => x.g).Select(x => x.x);
}

如果您的列表中有一些项目,那么您使用我的第一个还是最后一个解决方案都没有关系。但是正如@Maarteen 在 cmets 中的警告,可能有比项目数量更多的不必要的 Guid。对许多项目进行多重比较可能是个问题。 所以我将@jdweng 的答案与我的结合起来。

【讨论】:

  • 这将起作用,但请注意,每次比较都会为每个对象创建一个新的 guid。因此,将创建更多的 guid,然后创建对象。更好的是首先为每个对象生成一个 guid,然后使用它对其进行排序。
  • @Maarten 你是对的。 Quote 类中的 Guid 属性会更好。
  • 最好使用带有 guid 和对象的匿名类型。见jdweng的回答:stackoverflow.com/a/41697115/261050
  • 谢谢@Maarten。现在好多了。我总是懒得做一个随机排序的扩展方法,现在我有了一个:)
  • Guid.NewGuid() 保证是随机的。它只保证是唯一的。它不应该用来产生随机性。
猜你喜欢
  • 2012-11-19
  • 2011-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 2016-10-03
  • 1970-01-01
  • 2017-12-24
相关资源
最近更新 更多