我正在用c#创造一款游戏,而这款游戏的关键部分便是随机性。它本质上是一个摔跤模拟器,其中的"动作"是根据许多因素选择的,比如摔跤手的属性和当前的动量。
然后在所有符合这个标准的移动中,选择一个随机的移动来执行,使用随机对象和跳过/采取在LINQ中,但这真的不够。我想做的是权重移动被选中的概率(我已经在移动表中有一列,用于1到100之间的整数)。如何在随机行选择中实现这个权重呢?
这并不太难。我没有代码工作,所以我假设你有对象Move
与属性Weight
在一个数组和所有权重之和为100.0(真的不重要)。现在按权重降序对数组进行排序,在0到99之间选择一个随机数,然后遍历这些随机数,使随机数递减。当它不再为正时你就停下来,选择当前索引/move
var value = rnd.NextDouble()*100.0;
foreach(var move in moves.OrderByDescending(m => m.Weight))
{
value -= move.Weight;
if (value <= 0) return move;
}
当然,你可以缓存排序,甚至选择到一个大数组中,并使用随机索引(为了性能),但我希望原则应该清楚。
正如George建议的那样——这里有一个版本,你可以放弃假设权重之和为100:
double _weightSum;
...
// initialize the Sum somewhere
_weightSum = moves.SumBy(m => m.Weight);
Move GetRandomMove()
{
var value = rnd.NextDouble()*weightSum;
foreach(var move in moves.OrderByDescending(m => m.Weight))
{
value -= move.Weight;
if (value <= 0) return move;
}
}
一些代码将有助于我确切地了解您需要什么,但我建议使用Random . net库函数。一些文档可以在这里找到:
http://msdn.microsoft.com/en-us/library/system.random.aspx本例生成5个随机整数
Random rand = new Random();
Console.WriteLine("Five random integer values:");
for (int ctr = 0; ctr <= 4; ctr++)
Console.Write("{0,15:N0}", rand.Next());
Console.WriteLine();
这将与当前时间种子,使其"更随机"。如果需要可重复的测试,可以在测试时使用常量作为种子。
将每个可能移动的总权重相加
每一个除以总数,所以你已经将范围归一化为0..1.
获取该范围内的随机数。为每次移动选择一致的顺序,并选择随机数在其中的顺序。
我想做和Carsten基本相同的事情,但是用Linq。
给定的步数是步数的集合,每个步数的权重都为
public Move PickRandomMove()
{
var allMovesWeight = moves.Sum(m => m.Weight);
// pick a unit of weight at random, then shift it along by the weight of the
// first move so that there will always be an element in the TakeWhile results
var randomPick = new Random().Next(0, allMovesWeight) + moves.First().Weight;
return moves.TakeWhile(move => (randomPick -= move.Weight) > 0).Last();
}
我怀疑有一个更清晰的方式来表达如何TakeWhile是工作的,但希望你得到的想法