我是C#的新手。
我想做什么
我想在这里创建一个机会游戏系统。
基本上就是这样:
我的问题:如何完成我想要做的事情?
答案 0 :(得分:5)
您的示例代码有一个难题:您已撰写150/208
和190/209
。这是一个整数除法,都会导致零。您应该编写:150.0/208
和190.0/209
来指示编译器将它们分为double而不是整数。
编辑:
假设系统的RNG是平坦的,您的表格如下:
[item] [amount]
0 3 000 000
25 1 500 000
50 2 000 000
75 300 000
100 10 000
150 10 000 (no typo)
sum = 6820000
然后你的随机数发生器看起来像:
int randomItemNumber = Random.Next(6820000); // 0..6819999
if(randomItemNumber < 3000000)
Console.WriteLine("Aah, you've won the Item type #0\n");
else if(randomItemNumber < 3000000+1500000)
Console.WriteLine("Aah, you've won the Item type #1\n");
else if(randomItemNumber < 3000000+1500000+2000000)
Console.WriteLine("Aah, you've won the Item type #2\n");
else if(randomItemNumber < 3000000+1500000+2000000+300000)
Console.WriteLine("Aah, you've won the Item type #3\n");
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000)
Console.WriteLine("Aah, you've won the Item type #4\n");
else if(randomItemNumber < 3000000+1500000+2000000+300000+10000+10000)
Console.WriteLine("Aah, you've won the Item type #5\n");
else
Console.WriteLine("Oops, somehow you won nothing, the code is broken!\n");
这个想法是你把所有物品一个接一个地放在一条宽松的线上,但是你把它们放在他们的小组中。所以,一开始有第一种类型的三百万,然后是第二种类型的数百万,依此类推。该行共有6820000项。现在,您随机选择1到6820000(或从0到6819999)的数字,并将其用作LINE中元素的NUMBER。
由于项目存在于具有正确统计分布的行中,如果随机化1-6820000是FLAT,那么得到的“抽奖”将具有与您想要的完全分布。
唯一需要解释的技巧是如何猜测挑选的物品。这就是我们将这些项目分组的原因。 3000000项的第一部分是第一部分,所以如果数字小于3000000那么我们就打第一类。如果超过这个数,但低于下一个1500000(低于450000),则第二个类型被击中..依此类推。
答案 1 :(得分:1)
正如其他人所说,你的代码有一个整数除法错误。
在任何情况下,您都需要查看:逆变换采样。
基本上,它允许您采用统一的随机数(大多数PRNG提供给您)并将其转换为来自任何分布的随机样本。为此,您需要使用目标分发的CDF。
参考文献&amp;有用的页面:
编辑: 我实际上是指分类分布,而不是多项分布。这两种分布通常是混淆的(特别是在我的领域),但差异很重要。只有当多项分布参数化为n = 1(即一次试验)时,这两个分布才是等价的。
答案 2 :(得分:0)
我在我的应用中做了类似的事情,并将其转换为您的问题如下: 在伪代码中:
类Items看起来如下(删除了一些不重要的行,并添加了//
的备注public class Items : List<Item>
{
public Items()
{
Add(new Item( 0, 3000000));
Add(new Item(25, 1500000));
Add(new Item(50, 2000000));
// etc
}
/// <summary>
/// Returns a random item based on value.
/// </summary>
/// <returns></returns>
public Item GetRandomItem()
{
var sum = this.Sum(item => item.Value);
var randomValue = new Random().Next(sum);
// Iterate through itemsuntil found.
var found = false;
var itemIndex = 0;
var visitedValue = 0;
while (!found)
{
var item = this[itemIndex];
if ((visitedValue + item.Value ) > randomValue)
{
found = true;
}
else
{
itemIndex++;
visitedValue += item.value;
}
}
return this[itemIndex];
}
Item类只不过是Name和Value的占位符。
看起来很长,但它有一些好处:
答案 3 :(得分:0)
一个除数必须是双重才能防止零除。要计算您需要将它们累积到100%(或1)的概率:
// Element - Probability - Cumulative Probability
// Item100 10000 / 6820000 0.001466275659824
// Item75 300000 / 6820000 0.0439882697947214 + 0.001466275659824
// Item50 2000000 / 6820000 0.2932551319648094 + 0.0454545454545454
// Item25 1500000 / 6820000 0.219941348973607 + 0.3387096774193548
const double Item100 = 0.001466275659824;
const double Item75 = 0.0454545454545454;
const double Item50 = 0.3387096774193548;
const double Item25 = 0.5586510263929618;
int getRandomItem(Random rnd)
{
double value = rnd.NextDouble();
if (value <= Item100)
{
// use one of both possible items (100 or 150)
int which = rnd.Next(0, 2);
return which == 0 ? 100 : 150;
}
else if (value <= Item75)
return 75;
else if (value <= Item50)
return 50;
else if (value <= Item25)
return 25;
else
return 0;
}
你将如何使用它:
var rnd = new Random();
var items = new List<int>();
for (int i = 0; i < 100; i++)
items.Add(getRandomItem(rnd));
Console.Write(string.Join(Environment.NewLine, items));
请注意,我重用了随机实例。如果我在循环中创建它,“随机值将会变得相同,因为它将在同一时间播种。
答案 4 :(得分:0)
这样的事应该适合你。也许不是世界上最好的例子,但它应该足够了:
class Item
{
public string Name { get ; private set ; }
public int Amount { get ; private set ; }
public Item( string name , int amount )
{
if ( string.IsNullOrWhiteSpace(name) ) throw new ArgumentException("name") ;
if ( amount < 0 ) throw new ArgumentException("amount") ;
this.Name = name ;
this.Amount = amount ;
return ;
}
}
static void Main( string[] args )
{
Random rng = new Random() ;
Item[] items = { new Item( "item--0" , 3000000 ) ,
new Item( "item-25" , 1500000 ) ,
new Item( "item-50" , 2000000 ) ,
new Item( "item-75" , 300000 ) ,
new Item( "item-100" , 10000 ) ,
new Item( "item-150" , 10000 ) ,
} ;
int total = items.Sum( x => x.Amount ) ;
for ( int i = 0 ; i < 100 ; ++i )
{
int r = rng.Next(0, total ) ; // get a random integer x such that 0 <= x < total
int n = 0 ;
Item selected = null ;
int lo = 0 ;
int hi = 0 ;
for ( int j = 0 ; j < items.Length ; ++j )
{
lo = n ;
hi = n + items[j].Amount ;
n = hi ;
if ( r < n )
{
selected = items[j] ;
break ;
}
}
Console.WriteLine( "iteration {0}. r is {1} <= {2} < {3}. Selected item is {4}" ,
i ,
lo ,
r ,
hi ,
selected.Name
) ;
}
return;
}