Случайное число с фиксированным средним значением

Я хочу создать 100 случайных чисел от 1 до 10. Но среднее из этих 100 случайных чисел должно быть 7. Как я могу это сделать? Я делаю следующее:

//generating random number
Random random = new Random();
int value = random.Next(1,10);

И сохранение каждого значения в массиве. Если в среднем по 100 элементов в массиве нет 7, мне нужно получить еще 100 случайных чисел. Может ли кто-нибудь предложить лучший способ сделать это?

Ответ 1

  • Инициализировать A[0], ..., A[99] до 1.
  • Инициализировать I = {0, 1, ..., 99}.
  • Повторите шаги 4-6 600 раз.
  • Выберите случайный i равномерно из i.
  • Приращение A[i].
  • Если A[i] == 10, удалите i из i.

Это гарантирует, что sum(A) равен 700 и, следовательно, avg(A) равен 7.

Обратите внимание, однако, что это не дает равномерного распределения по всем таким массивам из 100 целых чисел в {1,..., 10}, что они суммируют до 700. Чтобы разработать алгоритм для равномерного отбора проб, было бы гораздо более сложным упражнения.

Ответ 2

public int RandomNumberThatAveragesToSeven()
{
    //Chosen by fair dice roll
    //Guaranteed to be random
    return 7;
}

Без дополнительных параметров этот алгоритм удовлетворяет каждому требованию.

  • Возврат должен быть между 1 и 10
  • Среднее число множественных вызовов должно иметь тенденцию к 7, так как n стремится к inf.

РЕДАКТИРОВАТЬ Поскольку в этом ответе было так много споров... Я добавил этот ответ... который определенно случайен.

public List<int> ProduceRandom100NumbersWithAverageOfSeven()
{
    var rand = new Random();
    var seed = rand.Next();
    if(seed > 0.5)
    {
        return new List(Enumerable.Concat(
                 Enumerable.Repeat(6, 50),
                 Enumerable.Repeat(8, 50)));
    }
    else
    {
        return new List(Enumerable.Concat(
                 Enumerable.Repeat(8, 50),
                 Enumerable.Repeat(6, 50)));

    }
}

Ответ 3

Хорошо, может быть сложно сделать что-то подобное.

Если вам нужно получить 100 разных номеров, и вам нужно, чтобы они были равны 7, вам понадобится их 700.

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

Когда наступает момент, когда сумма полученных вами значений меньше значений, которые вам нужно получить, тогда вы измените это последнее число на 10, положите 10 оставшихся номеров, которые вам нужны до конца вашего списка, и, на последнем номере вы получаете разницу между 700 и суммой ваших 99 предыдущих псевдослучайных значений.

Перемешивая ваш массив et voilá, у вас есть 100 псевдослучайных массивов с числами от 1 до 10, в среднем равными 7. Конечно, у него будет больше 10 секунд, чем вам будет угодно, но вы наверняка сможете настройте этот "алгоритм", чтобы сделать его чуть менее 10.

Mmmm, подождите немного, что, если вы получите случайные значения, которые имеют среднее значение выше 7? Вам также нужно будет отслеживать, что сумма ваших текущих значений меньше, чем числа, которые вы еще не получили. Если вы превзошли это значение в любой момент, вам нужно будет преобразовать свой последний номер в 1, поместите 1 на оставшиеся ваши необходимые значения и снова получите свой последний номер в качестве разницы между 700 и вашими 99 более ранними значениями.

Ответ 4

Что-то вроде этого может это сделать:

public static void Main(string[] args)
    {
        var randomList = new List<int>();
        var random = new Random();
        var avg = 0;
        while (avg != 7)
        {
            randomList = new List<int>();
            GenerateList(randomList, random);
            avg = (int) randomList.Average();
        }

        for (var i = 0; i < randomList.Count; i++)
        {
            Console.WriteLine(string.Format("Index: {0}, Number: {1}", i, randomList.ElementAt(i)));
        }
    }

    private static void GenerateList(List<int> refList, Random random)
    {
        for (var i = 0; i < 100; i++)
        {
            refList.Add(random.Next(1, 10));
        }
    }

Ответ 5

edit: изменение кода всегда приводит к получению в среднем ровно 7.

Это в основном оптимизированная версия того, что вы делали. Вместо того, чтобы генерировать еще 100 номеров, он генерирует только 10 перед выполнением проверки.

using System.Collections.Generic;
using System.Linq;

var r = new Random();
var numbers = new List<int>();

while (numbers.Count < 100)
{
    var stack = new Stack<int>();
    for (int i = 0; i < 10; i++)
    {
        stack.Push(r.Next(10));
    }

    if (stack.Sum() == 70)
    {
        numbers.AddRange(stack);
    }
}

Console.WriteLine(numbers.Average());

Ответ 6

Мои 2 цента

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            Stopwatch watch = Stopwatch.StartNew();

            int count = 100;
            Double min = 0;
            Double max = 10;
            Double target = 7;
            Double tolerance = 0.00000001;
            Double minAverage = target - tolerance;
            Double maxAverage = target + tolerance;

            Random r = new Random();
            List<Double> numbers = new List<double>();
            Double sum = 0;
            for (int i = 0; i < count; i++)
            {
                Double d = RangedDouble(min, max, r);
                numbers.Add(d);
                sum += d;
            }


            int Adjustments = 0;

            while((sum / count < minAverage || (sum / count) > maxAverage))
            {
                while ((sum / count) < minAverage)
                {
                    Double oldDbl = numbers.First(d => d < minAverage);
                    Double newDbl = oldDbl + RangedDouble(minAverage - oldDbl, 10 - oldDbl, r);

                    numbers.Remove(oldDbl);
                    sum -= oldDbl;
                    numbers.Add(newDbl);
                    sum += newDbl;
                    Adjustments++;
                }

                while ((sum / count) > maxAverage)
                {
                    Double oldDbl = numbers.First(d => d > maxAverage);
                    Double newDbl = oldDbl - RangedDouble(oldDbl - maxAverage, oldDbl, r);

                    numbers.Remove(oldDbl);
                    sum -= oldDbl;
                    numbers.Add(newDbl);
                    sum += newDbl;
                    Adjustments++;
                }
            }
            watch.Stop();

            int x = 0;
            while (x < count)
            {
                Console.WriteLine("{0:F7}  {1:F7}  {2:F7}  {3:F7}", numbers.Skip(x).Take(1).First(), numbers.Skip(x + 1).Take(1).First(), numbers.Skip(x + 2).Take(1).First(), numbers.Skip(x + 3).Take(1).First());
                x += 4;
            }

            Console.WriteLine();
            Console.WriteLine(watch.ElapsedMilliseconds);
            Console.WriteLine(numbers.Average());
            Console.WriteLine(Adjustments);
            Console.ReadKey(true);
        }

        private static double RangedDouble(Double min, Double max, Random r)
        {
            return (r.NextDouble() * (max - min) + min);
        }
    }
}

И вывод:

8.1510368 7.2103030 7.9909210 9.6693311
8.2275382 7.2839244 8.8634567 7.9751014
7.8643791 7,2262462 9,8914455 9,6875690
8.4396683 8.4308401 7.5380218 8.6147181
8,2760663 7,7399011 7,4312152 9,2115622
9.7850111 9.1061378 9.8672965 9.5610411
7.0415607 8.8446195 9.3562218 8.5279759
7.5227340 9.3572417 9.8927997 9.5880645
9,0908564 7,0918394 9,6213258 8,6528169
9.3803283 9.6869223 1.4006790 3.3310691
7.0719214 2.6370854 9.7558776 8.9180391
3.0486700 5.0082988 8.8624504 5.0497899
0,9692377 7,7140550 9,8495115 6,4933865
4.4939760 9.3187625 5.4353003 6.5405668
9.5693118 5.0339998 6.9644440 4.6902072
0,5241568 9,7748420 0,1406617 8,4731427
9.8064604 6.3113773 0.8628048 9.2417028
8.9148867 9.3111336 3.2424080 9.6710544
4.3794982 5.1687718 9.8207783 0.3283217
9.8321869 2.8093698 7.4377070 4.1130959
5.9840738 9.2560763 3.6691865 2.5498863
7.3242246 7.0179332 5.8906831 9.3340545
0,3735044 7,2442886 0,4409532 9,0749754
9.6716409 8.4097246 2.8069123 7.2970794
2.4964238 8.2826350 9.1115787 3.7373927

1
6.99992266645471
729

Ответ 7

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

Я бы решил это рекурсивно. В базовом случае нам нужно сгенерировать список длины 1, который суммируется с некоторым числом s. Это просто: список содержит только s:

rand 1 s = [s]

Теперь мы можем решить рекурсивный случай rand n s, где n - искомая длина списка, а s - желаемая сумма. Для этого мы создадим два списка x и y и объединим их вместе, с учетом заданных ограничений:

length x + length y = n
sum x + sum y = s
1  * length x <= sum x  -- Minimum value is 1
10 * length x >= sum x  -- Maximum value is 10
1  * length y <= sum y
10 * length y >= sum y

Эти уравнения/неравенства еще не решены, поэтому первое, что мы делаем, это выбор длины списков. Чтобы сохранить уровень рекурсии, мы можем выбрать lx = round (n / 2), а затем установить следующее:

length x = lx
length y = n - lx = ly

Следовательно:

sum x + sum y = s
1  * lx <= sum x
10 * lx >= sum x
1  * ly <= sum y
10 * ly >= sum y

Мы используем первое уравнение для переписывания неравенств:

1  * lx <= sum x
10 * lx >= sum x
1  * ly <= s - sum x
10 * ly >= s - sum x

Мы можем переставить нижние два, чтобы сделать sum x объектом:

sum x + 1  * ly <= s
sum x + 10 * ly >= s

sum x <= s - 1  * ly
sum x >= s - 10 * ly

Мы знаем ly и s, поэтому они дают нам определенные оценки для sum x, которые мы объединяем, беря наибольшую нижнюю грань и наименьшую верхнюю границу:

max (1  * lx) (s - 10 * ly) <= sum x
min (10 * lx) (s - 1  * ly) >= sum x

Эти границы имеют смысл: они учитывают случаи, когда каждый элемент из x равен 1 или 10, и они гарантируют, что остаток можно обработать с помощью sum y. Теперь мы просто генерируем случайное число B между этими границами, а затем устанавливаем:

sum x = B
sum y = s - B

Отсюда мы можем выполнить нашу рекурсию (предполагая некоторую функцию случайных чисел randInt):

rand n s = let lx    = round (n / 2)
               ly    = n - lx
               lower = max (1  * lx) (s - 10 * ly)
               upper = min (10 * lx) (s - 1  * ly)
               b     = randInt lower upper
           in rand lx b ++ rand ly (s - b)

Теперь ваш список можно создать, вызвав:

myList = rand 100 700

Я написал это в Haskell для краткости, но это просто арифметика, поэтому нужно легко перевести на С#. Здесь версия Python, если это помогает:

def rand(n, s):
    if n == 1:
        return [s]
    lx    = int(n / 2)
    ly    = n - lx
    lower = max(1  * lx, s - 10 * ly)
    upper = min(10 * lx, s - 1  * ly)
    b     = randint(lower, upper)
    result = rand(lx, b)
    result.extend(rand(ly, s - b))
    return result

Пожалуйста, укажите любые ошибки, которые я сделал!

Изменить: хотя я сомневаюсь в этом случае для С#, на некоторых языках мы могли бы сделать это проще и эффективнее, используя хвостовую рекурсию. Сначала мы переключаемся на создание одного элемента за раз:

-- Generate one number then recurse
rand 1 s = [s]
rand n s = let ly    = n - 1
               lower = max 1  (s - 10 * ly)
               upper = min 10 (s - 1  * ly)
               x     = randInt lower upper
            in x : rand (n - 1) s

Затем мы накапливаем результат, а не строим незавершенные продолжения:

rand' xs 1 s = s:xs
rand' xs n s = let ly    = n - 1
                   lower = max 1  (s - 10 * ly)
                   upper = min 10 (s - 1  * ly)
                   x     = randInt lower upper
                in rand' (x:xs) (n-1) s
rand = rand' []

Ответ 8

Этот метод генерирует последовательность случайных чисел, а затем добавляет/вычитает, пока мы не получим правильное общее число (700), пока число, которое мы изменяем, все еще находится в диапазоне 1-10

List<int> randomNumbers = new List<int>();
for (int i = 0; i < 100; i++) {
    numbers.Add(r.Next(1, 10));
}

int total = randomNumbers.Sum();

// Now fiddle until we get the correct total (700)

if (total < 700) {
    while (total < 700) {
        for (int i = 0; i < 100; i++) {
            if (numbers[i] < 10) {
                numbers[i]++;
                total = randomNumbers.Sum();
                if (total == 700) break;
            }
        }
    }
}

else if (total > 700) {
    while (total > 700) {
        for (int i = 99; i >= 0; i--) {
            if (numbers[i] > 0) {
                numbers[i]--;
                total = randomNumbers.Sum();
                if (total == 700) break;
            }
        }
    }
}