私はC#を初めて使用します。
私がやろうとしていること
ここで運が左右するゲームシステムを作成しようとしています。
基本的にこれはそれがどのようであるかです:
私の質問:私がやろうとしていることを達成するためにどのようにすればよいですか?
私はC#を初めて使用します。
私がやろうとしていること
ここで運が左右するゲームシステムを作成しようとしています。
基本的にこれはそれがどのようであるかです:
私の質問:私がやろうとしていることを達成するためにどのようにすればよいですか?
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");
アイデアは、すべてのアイテムを次々にゆるい列に並べますが、それらをグループに保持するというものです。したがって、最初は第1の種類のミリオンが3つあり、次に第2のタイプのミリオンと半分があります。合計6820000個のアイテムが並んでいます。ここで、1から6820000(または0から6819999)の数値をランダムに選択し、それをLINEの要素のNUMBERとして使用します。
アイテムは正しい統計分布と一致しているため、ランダム化1-6820000がFLATの場合、結果の「宝くじ」は希望どおりに分布します。
説明するために残された唯一のトリックは、どのアイテムが選ばれたかを推測する方法です。これが、アイテムをグループにまとめた理由です。3000000アイテムの最初の部分は最初のタイプであるため、数が3000000未満の場合は、最初のタイプにヒットします。それよりも大きいが、次の1500000よりも小さい(4500000よりも小さい)場合は、2番目のタイプがヒットします。
他の人が言っているように、あなたのコードには整数除算のバグがあります。
いずれにせよ、あなたは見たいと思うでしょう:逆変換サンプリング。
基本的に、それはあなたが均一な乱数(ほとんどのPRNGがあなたに与えるもの)を取り、それを任意の分布からのランダムサンプルに変換することを可能にします。これを行うには、ターゲット分布のCDFを使用する必要があります。
参考資料と役立つページ:
編集済み:私は実際には、多項分布ではなく、カテゴリ分布を意味していました。これらの2つの分布は(特に私の分野では)しばしば混同されますが、違いは重要です。2つの分布は、多項分布がn = 1でパラメーター化されている場合(つまり、1回の試行)にのみ同等です。
私は自分のアプリで同様のことを行い、それを以下の問題に変換します。擬似コード:
クラス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クラスは、名前と値のプレースホルダーにすぎません。
長く見えますが、いくつかの利点があります。
ゼロが除算されないようにするには、1つの除数をdoubleにする必要があります。確率を計算するには、それらを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));
ランダムインスタンスを再利用することに注意してください。ループ内で作成する場合、「ランダムな値は、同じ時間にシードされるため、常に同じになります。
このような何かがあなたをするはずです。世界で最高の例ではないかもしれませんが、それで十分です。
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;
}