специальное случайное число - PullRequest
7 голосов
/ 31 июля 2009

Я хотел бы иметь случайное число, подобное этому: (в C #)

Random r = new Random();
r.next (0,10)

НО важно, чтобы случайное число было ближе к 8 (или оно обычно было большим), Я имею в виду, если мы используем для:

for (int i =0; i<...;i++)
{
  write: r.next (0,10)
}

результат будет таким;

8 7 6 9 1 0 5 3 2
2 3 8 9 7 7 6 2 3
8 8 9 7 2 8 2 8 4
3

Ответы [ 6 ]

29 голосов
/ 31 июля 2009

Вам нужно взвесить ваши результаты. Вы можете сделать это примерно так:

private int[] _distribution = new int[] { 0, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9 };
Random _r = new Random();

public int GetWeightedRandom()
{
    return _distribution[_r.Next(0, _distribution.Length)];
}

Если бы я знал, что мой диапазон маленький и непротиворечивый, я бы использовал таблицу - тривиально сделать ее собственным классом.

Для полноты картины я также добавлю этот класс. Этот класс заимствует из обработки изображений и использует функцию гамма-коррекции: значение между 0 и 1 возводится в гамму, которое возвращает значение между 0 и 1, но распределяется больше в нижний предел, если гамма <1,0, и более высокий уровень, если гамма> 1,0.

public class GammaRandom {
    double _gamma;
    Random _r;

    public GammaRandom(double gamma) {
        if (gamma <= 0) throw new ArgumentOutOfRangeException("gamma");
        _gamma = gamma;
        _r = new Random();
    }
    public int Next(int low, int high) {
       if (high <= low) throw new ArgumentOutOfRangeException("high");
       double rand = _r.NextDouble();
       rand = math.Pow(rand, _gamma);
       return (int)((high - low) * rand) + low;
    }
}

(из комментариев, убран r из GetWeightedRandom (). Также добавлена ​​проверка диапазона в Next ())

Хорошо, давайте действительно пойдем в город здесь. Для этого я направляю Джона Скита - это абстрактный класс со свойством шаблона, которое возвращает функцию преобразования, которая отображает диапазон [0..1) в [0..1) и масштабирует случайное число в этом диапазоне. Я также переопределил гамму с точки зрения этого и реализовал грех и cos.

public abstract class DelegatedRandom
{
    private Random _r = new Random();
    public int Next(int low, int high)
    {
        if (high >= low)
            throw new ArgumentOutOfRangeException("high");
        double rand = _r.NextDouble();
        rand = Transform(rand);
        if (rand >= 1.0 || rand < 0) throw new Exception("internal error - expected transform to be between 0 and 1");
        return (int)((high - low) * rand) + low;
    }
    protected abstract Func<double, double> Transform { get; }
}

public class SinRandom : DelegatedRandom
{
    private static double pihalf = Math.PI / 2;
    protected override Func<double, double> Transform
    {
        get { return r => Math.Sin(r * pihalf); }
    }
}
public class CosRandom : DelegatedRandom
{
    private static double pihalf = Math.PI / 2;
    protected override Func<double, double> Transform
    {
        get { return r => Math.Cos(r * pihalf); }
    }
}
public class GammaRandom : DelegatedRandom
{
    private double _gamma;
    public GammaRandom(double gamma)
    {
        if (gamma <= 0) throw new ArgumentOutOfRangeException("gamma");
        _gamma = gamma;
    }
    protected override Func<double, double> Transform
    {
        get { return r => Math.Pow(r, _gamma); }
    }
}
2 голосов
/ 31 июля 2009

Вместо использования варианта массива вы также можете взглянуть на этот SO ответ , который имеет ссылку на Math.NET Iridium , который реализует неоднородные случайные генераторы.

Преимущества варианта массива в том, что вы получаете более динамичный подход без необходимости переписывать массив все время. Вы также можете сделать некоторые вещи, которые были бы практически невозможны с вариантом массива (большие неоднородные случайные числа).

2 голосов
/ 31 июля 2009

Вам нужна функция распределения, которая принимает число от 0 до 1 и преобразует его в число в нужном диапазоне с более высоким весом для определенного числа. Вы можете создать такую ​​функцию с помощью тригонометрических функций (sin, cos, ...), экспоненциальных или, возможно, полинома.

ОБНОВЛЕНИЕ: посмотрите на эту страницу для получения дополнительной информации о распределении вероятностей

1 голос
/ 31 июля 2009

Не совсем то, что вы ищете, но очень простой способ приблизить нормальное распределение чисел - сложить несколько поколений вместе.

Классическим примером этой техники является игра Dungeons and Dragons, где сила персонажа может быть определена путем бросания трех шестигранных кубиков и сложения результатов. Это дает диапазон от 3 до 18 с номерами около 10 наиболее вероятным. Варианты включают в себя:

  • Бросок 4 кубика и отбрасывание самого низкого. Это искажает распределение в сторону больших чисел.
  • Усреднение результатов, а не их добавление. Это облегчает понимание диапазона вывода.

В качестве альтернативы это довольно близко ...

1 голос
/ 31 июля 2009

С некоторым дополнительным взвешиванием, которое должно быть возможным. Зависит от того, как вы укажете «около восьми». Очень простой способ сделать это так:

for (int i =0; i<...;i++)
{
    n = r.next (0,100);
    write: (n*n) / 1000
}

Квадрат будет взвешивать числа в направлении нижнего предела, то есть в этом случае в 33% случаев вы получите 0, в то время как в 9 вы получите только примерно 5% времени.

Этот метод, конечно, должен быть адаптирован к конкретному случаю.

0 голосов
/ 31 июля 2009

Мне кажется, что вы хотите, чтобы ваши случайные числа были взвешены в верхний предел - это будет справедливой оценкой?

Что-то вроде , это может помочь вам (это Java, но принципы применимы)

...