简体   繁体   English

如何用RandomOrDefault替换FirstOrDefault来“平衡”调用?

[英]How to replace FirstOrDefault with something like RandomOrDefault to “balance” calls?

I have such code 我有这样的代码

    senders.FirstOrDefault(sender => !sender.IsBusy);

This line is called pretty often. 这条线经常被称为。

The problem is that it always returns the first non-busy sender ; 问题是它总是返回第一个非忙碌的sender ; the same first sender is returned pretty often, but the last one is returned very rarely. 相同的第一个发件人经常被返回,但最后一个很少返回。 How to easily balance this? 如何轻松平衡这个?

Ideally, on every call I should return the most rarely used sender. 理想情况下,在每次通话时我都应该返回最少使用的发件人。 Ie between all non-busy senders select the one that was selected the least number of times during the last second. 即,在所有非忙碌的发送者之间选择在最后一秒期间被选择的次数最少的发送者。

Maybe something like: 也许是这样的:

public static T RandomOrDefault<T>(this IEnumerable<T> dataSet)
{ 
    return dataSet.RandomOrDefault(y => true);
}

public static T RandomOrDefault<T>(this IEnumerable<T> dataSet, Func<T, bool> filter)
{
    var elems = dataSet.Where(filter).ToList();
    var count = elems.Count;
    if (count == 0)
    {
        return default(T);
    }
    var random = new Random();
    var index = random.Next(count - 1);
    return elems[index];
}

then you can call it with: 然后你可以用:

senders.RandomOrDefault(sender => !sender.IsBusy);

If you want to get the least used one efficiently you will be probably good with the following non-Linq 'list rotation' solution, which is O(n) effiency and O(1) space unlike most of others: 如果你想有效地使用最少的一个,你可能会对以下非Linq'列表轮换'解决方案很好,这是O(n)效率和O(1)空间不像其他大多数:

// keep track of these
List<Sender> senders;
int nSelected = 0;          // number of already selected senders

// ...

// solution
int total = senders.Count;  // total number of senders
// looking for next non-busy sender
Sender s = null;
for (int i = 0; i < total; i++)
{
    int ind = (i + nSelected) % total; // getting the one 'after' previous
    if (!senders[ind].IsBusy)
    {
        s = senders[ind];
        ++nSelected;
        break;
    }
}

Of course this adds the must-be-indexable constraint on senders container. 当然,这会在senders容器上添加必须可索引的约束。

您可以在FirstOrDefault之前使用此帖子中的“Shuffle”扩展方法

Keep a look-up of senders that you have used and the time when they were used. 查看您使用过的发件人以及使用它们的时间。

var recentlyUsed = new Dictionary<Sender, DateTime>();
var sender = senders.FirstOrDefault(sender => !sender.IsBusy && (!recentlyUsed.ContainsKey(sender) || recentlyUsed[sender] < DateTime.Now.AddSeconds(-1)));
if (sender != null)
    recentlyUsed[sender] = DateTime.Now;

You could use Skip with a random number less than the total number of non-busy senders. 您可以使用Skip,其随机数小于非繁忙发件人的总数。

senders.Where(sender => !sender.IsBusy).Skip(randomNumber).FirstOrDefault();

Identifying a sensible limit for the random number might be a bit tricky though. 确定随机数的合理限制可能有点棘手。

You could easily reorder by a new Guid, like this: 您可以通过新的Guid轻松重新排序,如下所示:

senders.Where(sender => !sender.IsBusy).OrderBy(x => Guid.NewGuid()).FirstOrDefault();

You don't mess with random numbers, you don't have to identify a "range" for these numbers. 你不会乱用随机数,你不必为这些数字确定一个“范围”。 It just plain works and it's pretty elegant, I think. 我认为它只是简单的作品而且非常优雅。

Based on algorithm from the "Real world functional programming" book here's the O(n) implementation of extension method for taking random or default value from IEnumearble. 基于“真实世界函数式编程”一书中的算法,这里是从IEnumearble中获取随机或默认值的扩展方法的O(n)实现。

public static class SampleExtensions
{
    // The Random class is instantiated as singleton 
    // because it would give bad random values 
    // if instantiated on every call to RandomOrDefault method
    private static readonly Random RandomGenerator = new Random(unchecked((int)DateTime.Now.Ticks));

    public static T RandomOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        IEnumerable<T> filtered = source.Where(predicate);

        int count = 0;
        T selected = default(T);
        foreach (T current in filtered)
        {
            if (RandomGenerator.Next(0, ++count) == 0)
            {
                selected = current;
            }
        }

        return selected;
    }

    public static T RandomOrDefault<T>(this IEnumerable<T> source)
    {
        return RandomOrDefault(source, element => true);
    }
}

Here's the code to ensure that this algorithm really gives the Uniform distribution: 这是确保此算法真正给出Uniform分布的代码:

[Test]
public void TestRandom()
{
    IEnumerable<int> source = Enumerable.Range(1, 10);
    Dictionary<int, int> result = source.ToDictionary(element => element, element => 0);
    result[0] = 0;

    const int Limit = 1000000;
    for (int i = 0; i < Limit; i++)
    {
        result[source.RandomOrDefault()]++;
    }

    foreach (var pair in result)
    {
        Console.WriteLine("{0}: {1:F2}%", pair.Key, pair.Value * 100f / Limit);
    }

    Console.WriteLine(Enumerable.Empty<int>().RandomOrDefault());
}

The output of TestRandom method is: TestRandom方法的输出是:

1: 9,92%
2: 10,03%
3: 10,04%
4: 9,99%
5: 10,00%
6: 10,01%
7: 9,98%
8: 10,03%
9: 9,97%
10: 10,02%
0: 0,00%
0

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何用接口ID之类的替代枚举? - How to replace enumeration with something like interface ID? FirstOrDefault 如何评估带有条件的内部 FirstOrDefault? - How does FirstOrDefault evaluate an inner FirstOrDefault with a condition? 如何返回 FirstOrDefault - How to return FirstOrDefault 为什么LINQ FirstOrDefault或First生成的调用数 - Why the number of calls generated by LINQ FirstOrDefault or First regexp如何替换不在评论中的内容? - How to regexp replace something not in comment? 为什么petapoco中没有像FirstOrDefault这样的LastOrDefault方法? - Why there is no LastOrDefault method in petapoco like FirstOrDefault? 如何在linq中优化FirstOrDefault语句 - how to optimize FirstOrDefault Statement in linq FirstOrDefault 如何测试平等? - How Does FirstOrDefault Test for Equality? 在C#中,有没有一种方法可以将诸如Microsoft.VisualBasic.Interaction.MsgBox()之类的引用调用缩短为诸如myMsgBox()之类的引用? - In C# is there a way to shorten reference calls like Microsoft.VisualBasic.Interaction.MsgBox() to something like myMsgBox()? 在处理间隔时,是否有一种优雅的方法可以用开关之类的东西代替? - Is there an elegant way to replace if by something like switch when dealing with intervals?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM