我有这样的代码
senders.FirstOrDefault(sender => !sender.IsBusy);
此行经常被调用。
问题是它总是返回第一个非忙sender
;相同的第一个发件人经常被返回,但最后一个很少返回。如何轻松平衡这个?
理想情况下,在每次通话时,我都应该返回最少使用的发件人。即在所有非繁忙发件人之间选择在最后一秒内选择的次数最少的那个。
答案 0 :(得分:3)
可能是这样的:
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];
}
然后你可以用:
来调用它senders.RandomOrDefault(sender => !sender.IsBusy);
答案 1 :(得分:2)
如果你想有效地使用最少的一个,你可能会对以下非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;
}
}
当然,这会在senders
容器上添加必须可索引的约束。
答案 2 :(得分:0)
您可以在FirstOrDefault之前使用this post中的“随机播放”扩展方法
答案 3 :(得分:0)
查看您使用过的发件人及其使用时间。
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;
答案 4 :(得分:0)
您可以使用Skip,其随机数小于非繁忙发件人的总数。
senders.Where(sender => !sender.IsBusy).Skip(randomNumber).FirstOrDefault();
确定随机数的合理限制可能有点棘手。
答案 5 :(得分:0)
您可以轻松地通过新的Guid重新排序,例如:
senders.Where(sender => !sender.IsBusy).OrderBy(x => Guid.NewGuid()).FirstOrDefault();
你不会乱用随机数字,你不必识别一个&#34;范围&#34;对于这些数字。我认为它只是简单的作品而且非常优雅。
答案 6 :(得分:0)
基于the "Real world functional programming" book的算法,这里是从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);
}
}
这是确保此算法真正给出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());
}
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