Подтвердить что ты не робот

Создайте серию случайных чисел, которые добавляют до N в С#

Как мне создать 30 случайных чисел между 1-9, что все добавляют до 200 (или некоторых произвольных N) в С#?

Я пытаюсь сгенерировать строку цифр, которые могут быть объединены, чтобы быть N.

4b9b3361

Ответ 1

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

static void Main()
{
    int count = 30;
    int[] numbers = getNumbers(count, 155);
    for (int index = 0; index < count; index++)
    {
        Console.Write(numbers[index]);
        if ((index + 1) % 10 == 0)
            Console.WriteLine("");
        else if (index != count - 1)
            Console.Write(",");
    }
    Console.ReadKey();
}
static int[] getNumbers(int count, int total)
{
    const int LOWERBOUND = 1;
    const int UPPERBOUND = 9;

    int[] result = new int[count];
    int currentsum = 0;
    int low, high, calc;

    if((UPPERBOUND * count) < total ||
        (LOWERBOUND * count) > total ||
        UPPERBOUND < LOWERBOUND)
        throw new Exception("Not possible.");

    Random rnd = new Random();

    for (int index = 0; index < count; index++)
    {
        calc = (total - currentsum) - (UPPERBOUND * (count - 1 - index));
        low = calc < LOWERBOUND ? LOWERBOUND : calc;
        calc = (total - currentsum) - (LOWERBOUND * (count - 1 - index));
        high = calc > UPPERBOUND ? UPPERBOUND : calc;

        result[index] = rnd.Next(low, high + 1);

        currentsum += result[index];
    }

    // The tail numbers will tend to drift higher or lower so we should shuffle to compensate somewhat.

    int shuffleCount = rnd.Next(count * 5, count * 10);
    while (shuffleCount-- > 0)
        swap(ref result[rnd.Next(0, count)], ref result[rnd.Next(0, count)]);

    return result;
}
public static void swap(ref int item1, ref int item2)
{
    int temp = item1;
    item1 = item2;
    item2 = temp;
}

У меня не было много времени, чтобы проверить это, так что извини, если в моей логике есть недостаток.

ИЗМЕНИТЬ:

Я провел несколько тестов, и все кажется твердым. Если вы хотите красивое распространение, похоже, что вы хотите что-то в строках Total = Count * ((UPPER + LOWER) / 2). Хотя я уверен, что, поскольку разница между UPPER и LOWER увеличивается, это становится более гибким.

Ответ 2

Проблема состоит в том, что мы хотим, чтобы все числа были ограничены 1-9 и, добавляли к N. Таким образом, мы должны генерировать каждое число один за другим и определять реальные оценки для следующего числа.

Это, конечно, приведет к статистическому смещению к концу списка, поэтому я рекомендую перетасовать массив один раз после генерации.

Чтобы определить следующие оценки числа, сделайте следующее: Верхняя граница = возьмите оставшуюся сумму минус (количество оставшихся элементов * min). Нижняя граница = взять оставшуюся сумму минус (количество оставшихся элементов * max).

Что-то вроде (untested):

public static List<int> RandomList(int digitMin, int digitMax, 
                                   int targetSum, int numDigits)
{
    List<int> ret = new List<int>(numDigits);

    Random random = new Random();
    int localMin, localMax, nextDigit;
    int remainingSum = targetSum;

    for(int i=1; i<=numDigits; i++)
    {
          localMax = remainingSum - ((numDigits - i) * min);
          if(localMax > max)
              localMax = max;

          localMin = remainingSum - ((length - i) * max);
          if(localMin > min)
              localMin = min;

          nextDigit = random.Next(localMin, localMax);
          ret.Add(nextDigit);
          remainingSum -= nextDigit;
    }

    return ret;
}

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

Изменить: мне пришлось изменить цикл for на 1-й, потому что мы хотим, чтобы число элементов осталось после генерации этого.

Edit2: Поместите его в метод для полноты и измените length на numDigits для удобства чтения.

Ответ 3

Мое оригинальное выражение:

Вы можете генерировать только 29 случайных чисел. 30-е число будет определяться другими 29 и суммой. Это статистически важно...

Я хотел добавить некоторые пояснения, подумав об этом, и pinging сообщество...

Теперь я считаю, что мое первоначальное утверждение ложно. Это было слишком снисходительно (на что указал lc). Вы даже не можете генерировать 29 по-настоящему случайных чисел. По мере приближения и приближения к 30 конечные цифры не являются случайными, так как rnd [1..9] является случайным. lc попытался смягчить это, чтобы придумать решение, но я считаю, что решение, которое он придумал (и Спенсер), отвечает на совсем другой вопрос. Этот вопрос: "Из всех наборов из 30 цифр от 1 до 9, которые составляют до 200, постройте одно случайно".

Я считаю, что вопрос, который был сформулирован, неразрешимый, который, я считаю, можно доказать с помощью Принцип Pigeonhole (также используемый Кнутом, чтобы показать, что некоторые "случайные" перетасовки не были действительно случайными), но я не сделал математику.

Хорошо поговорите с каждым.

Ответ 4

Эта программа попытается дать вам ответ. Но поскольку вы имеете дело со случайными числами, есть вероятность, что это никогда не даст вам ответа.

public static IEnumerable<int> GetRandom()
{
    var rand = new Random();
    while (true)
    {
        yield return
        rand.Next(1, 9);
    }
}

public static List<int> GetThirtyThatAddToTwoHundred()
{
    do
    {
        var current = GetRandom().Take(30);
        if (200 == current.Sum())
        {
            return current.ToList();
        }
    } while (true);
}

Ответ 5

После всех обсуждений здесь есть еще один способ создания списка, который не вносит предвзятости. Да, он отличается от того, что задает вопрос, но вместо случайного выбора цифр вы можете произвольно увеличивать цифры до тех пор, пока не достигнете суммы. Как и следующие (опять же непроверенные):

public static List<int> RandomListByIncrementing(int digitMin, int digitMax, 
                                                 int targetSum, int numDigits)
{
    if(targetSum < digitMin * numDigits || targetSum > digitMax * numDigits)
        throw new ArgumentException("Impossible!", "targetSum");

    List<int> ret = new List<int>(Enumerable.Repeat(digitMin, numDigits));
    List<int> indexList = new List<int>(Enumerable.Range(0, numDigits-1));

    Random random = new Random();
    int index;

    for(int currentSum=numDigits * digitMin; currentSum<targetSum; currentSum++)
    {
        //choose a random digit in the list to increase by 1
        index = random.Next(0,indexList.Length-1);

        if(++ret[indexList[index]] == digitMax)
        {
            //if you've increased it up to the max, remove its reference
            //because you can't increase it anymore
            indexList.RemoveAt(index);
        }
    }

    return ret;
}

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

Теперь в конце дня не будет делаться перетасовка, хотя, возможно, это все равно произведет один из доступных наборов ответов на вопрос, и это вопрос о том, какой из них "чувствует себя лучше" или быстрее запускается.

Ответ 6

Поэтому я должен спросить: Есть ли реальная цель для этого, или это просто упражнение или домашнее задание? Проводится большая работа по предотвращению "предвзятости". Является ли это реальным требованием или будет какое-либо довольно случайное решение? Не зная требований, очень легко тратить много времени. Если это реальная проблема, пожалуйста, объясните, каковы фактические требования.

Ответ 7

Если статистическое отклонение от истинной случайности допустимо, вы можете добавить числа до N - [максимальное случайное число], а затем выбрать последнее число как N - сумму (выбранную до сих пор).

Ответ 8

Алгоритм:

  • Установить total = 200 (или что-то еще)
  • Генерировать случайное число между 1-9
  • Проверьте, если (total - newRandomNumber >= 0), если нет goto 6
  • total - = newRandomNumber
  • Добавить newRandomNumber в массив, goto 2.
  • newRandomNumber = total
  • Добавить newRandomNumber в массив, если newRandomNumber!= 0
  • End

Ответ 9

Этот метод возвращает 30 случайных чисел, которые составляют до произвольного N. Можно сделать, чтобы иметь некоторые 0 значений. если это невозможно, просто инициализируйте массив все к одному, и если сумма больше для произвольного N, установите vals [nextIdx] в 1 вместо 0. Надеюсь, это поможет.

    private int[] getNumbers(int arbitraryN) {
        int[] vals = new int[30];
        int nextIdx = 0;
        int nextNumber=0;
        Random r = new Random();
        if (arbitraryN > 270 || arbitraryN < 30)
            throw new Exception("Not a Valid number");
        while (vals.Sum() < arbitraryN)
        {
            nextNumber = r.Next(1, 9);
            nextIdx = r.Next(29);
            vals[nextIdx] = nextNumber;
            if (vals.Sum() > arbitraryN)
            {
                vals[nextIdx] = 0;
                vals[nextIdx] = 270 - vals.Sum();
                break;
            }
        }
        return vals;
    }

Ответ 10

Нет никакого guarrentee, чтобы 30 случайных чисел из 1-9 добавляли к любому конкретному N.

Что вы можете найти, это список чисел, которые будут содержать до N и ограничены от 1 до 9, но число не будет равно 30. Я считаю, что минимальное количество нужных вам чисел равно 23, будучи (22 * 9) + 2. Максимум, конечно, будет 200 (200 * 1). Таким образом, длина списка находится где-то внутри [23,200]. Таким образом, вероятность того, что случайный список может быть длиной 30, является довольно низкой. Если все длины списков доступны (я думаю, что они), ваши шансы в долгосрочной перспективе составляют около 0,5%.

Ответ 11

Я думаю, что это самый простой способ сделать это, поэтому у него может не хватить усложнения, но он доставит вас туда.

        String output = "";
        int sum = 0;
        int result = 200;       //enter the "end number"

        Random r = new Random();

        while (sum != result) {
            int add;
            if ((result - sum) > 10)
            {
                add = r.Next(1, 10);
            }
            else
            {
                add = r.Next(result - sum + 1);
            }

            sum += add;
            output += add.ToString() + " + ";
        }

        output = output.Remove(output.Length - 2);

        Console.WriteLine(output);

Надеюсь, что это поможет!

Ответ 12

Если вам нужен несмещенный алгоритм, наивная реализация - это что-то вроде:

while (true) {
  numbers = [];
  total = 0;
  for (i = 0; i < COUNT; ++i) {
    next = rand(BOUNDS);
    total += next;
    numbers.push(next);
  }

  if (total == TARGET) {
    return numbers;
  }
}

Это не завершающий и медленный, но он не предвзятый. Если вы хотите непредвзятый алгоритм, я не уверен, что предлагаемые алгоритмы объективны.

Ответ 13

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

Ответ 14

Я думал, что попробую подход к разделению и завоеванию. Кажется, это работает очень хорошо. Я уверен, что результаты не являются действительно случайными из-за ограничений элементов алгоритма, но он приближается. По сути, я разбил список пополам и целевую сумму пополам и рекрутировал, пока не получаю списки из 3 элементов или меньше. Затем я использую повторную итерацию случайных чисел до тех пор, пока эти меньшие суммы не будут достигнуты. Здесь код с пробелом под ним.

using System;
using System.Collections.Generic;

namespace AddUpClient {
    class Program {

        static void Main() {
            AddUpWorker worker = new AddUpWorker();
            int MinDigit = 1;
            int MaxDigit = 9;
            int ItemsToSum = 30;
            int TargetSum = 150;

            try {
                //Attempt to get a list of pseudo-random list of integers that add up to the target sum
                IList<int> Results = worker.AddUp(MinDigit, MaxDigit, ItemsToSum, TargetSum);

                EvaluateResults(TargetSum, Results);
                Console.ReadLine();
            }
            catch (Exception E) {
                Console.Out.WriteLine("Error: {0}", E.Message);
                return;
            }

        }

        private static void EvaluateResults(int TargetSum, IList<int> Results)
        {
            Console.Out.WriteLine("Results have {0} items.", Results.Count);

            int Sum = 0;
            foreach (int Result in Results) {
                Sum += Result;
                Console.Out.WriteLine("Result: {0} Running total: {1}", Result, Sum);
            }
            Console.Out.WriteLine();
            Console.Out.WriteLine("Result = {0}", (Sum == TargetSum ? "SUCCESS" : "FAIL"));
        }
    }

    internal class AddUpWorker {

        Random RGenerator = new Random();

        public IList<int> AddUp(int MinDigit, int MaxDigit, int ItemsToSum, int TargetSum) {

            Console.Out.WriteLine("AddUp called to sum {0} items to get {1}", ItemsToSum, TargetSum);
            if (ItemsToSum > 3) {

                int LeftItemsToSum = ItemsToSum/2;
                int RightItemsToSum = ItemsToSum - LeftItemsToSum;

                int LeftTargetSum = TargetSum/2;
                int RightTargetSum = TargetSum - LeftTargetSum;


                IList<int> LeftList = AddUp(MinDigit, MaxDigit, LeftItemsToSum, LeftTargetSum);
                IList<int> RightList = AddUp(MinDigit, MaxDigit, RightItemsToSum, RightTargetSum);

                List<int> Results = new List<int>();
                Results.AddRange(LeftList);
                Results.AddRange(RightList);
                return Results;
            }

            // 3 or less

            int MinSumWeCanAchieve = ItemsToSum*MinDigit;
            int MaxSumWeCanAchieve = ItemsToSum*MaxDigit;


            if (TargetSum < MinSumWeCanAchieve)
                throw new ApplicationException("We added up too fast");

            if (TargetSum > MaxSumWeCanAchieve)
                throw new ApplicationException("We added up too slow");

            //Now we know we can achieve the result -- but it may not be too efficient...

            int[] TrialNumbers = new int[ItemsToSum];
            int MaxIteration = 100000;
            int IterationPrintInterval = 1000;
            int TrialSum;
            bool PrintIteration;

            for (int Iteration = 1; Iteration <= MaxIteration; ++Iteration) {

                PrintIteration = ((Iteration % IterationPrintInterval) == 0);

                if (PrintIteration)
                    Console.Out.WriteLine("Iteration {0} attempting to sum {1} numbers to {2}",
                        Iteration, ItemsToSum, TargetSum);

                TrialSum = 0;
                for (int j=0; j < ItemsToSum; ++j) {
                    TrialNumbers[j] = RGenerator.Next(MinDigit, MaxDigit + 1);
                    TrialSum += TrialNumbers[j];
                }
                if (PrintIteration)
                    ShowArray(string.Format("Iteration: {0}", Iteration), TrialNumbers);

                if (TrialSum == TargetSum) {    //Yay
                    ShowArray(string.Format("Success in {0} iterations: ", Iteration), TrialNumbers);
                    return new List<int>(TrialNumbers);
                }
                //try again....
            }

            throw new ApplicationException(string.Format("Maximum of {0} trials exceeded", MaxIteration));
        }

        private void ShowArray(string Prefix, int[] numbers)
        {
            for (int i = 0; i < numbers.Length; ++i) {
                if (i == 0)
                    Console.Write("{0} {1}", Prefix, numbers[i]);
                else
                    Console.Write(", {0}", numbers[i]);
            }
            Console.WriteLine();
        }
    }
}

AddUp called to sum 30 items to get 150
AddUp called to sum 15 items to get 75
AddUp called to sum 7 items to get 37
AddUp called to sum 3 items to get 18
Success in 10 iterations:  7, 2, 9
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 12 iterations:  5, 4
AddUp called to sum 2 items to get 10
Success in 2 iterations:  1, 9
AddUp called to sum 8 items to get 38
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 11 iterations:  4, 5
AddUp called to sum 2 items to get 10
Success in 6 iterations:  8, 2
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 3 iterations:  8, 1
AddUp called to sum 2 items to get 10
Success in 1 iterations:  4, 6
AddUp called to sum 15 items to get 75
AddUp called to sum 7 items to get 37
AddUp called to sum 3 items to get 18
Success in 3 iterations:  4, 6, 8
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 17 iterations:  3, 6
AddUp called to sum 2 items to get 10
Success in 24 iterations:  1, 9
AddUp called to sum 8 items to get 38
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 3 iterations:  2, 7
AddUp called to sum 2 items to get 10
Success in 3 iterations:  1, 9
AddUp called to sum 4 items to get 19
AddUp called to sum 2 items to get 9
Success in 4 iterations:  5, 4
AddUp called to sum 2 items to get 10
Success in 2 iterations:  9, 1
Results have 30 items.
Result: 7 Running total: 7
Result: 2 Running total: 9
Result: 9 Running total: 18
Result: 5 Running total: 23
Result: 4 Running total: 27
Result: 1 Running total: 28
Result: 9 Running total: 37
Result: 4 Running total: 41
Result: 5 Running total: 46
Result: 8 Running total: 54
Result: 2 Running total: 56
Result: 8 Running total: 64
Result: 1 Running total: 65
Result: 4 Running total: 69
Result: 6 Running total: 75
Result: 4 Running total: 79
Result: 6 Running total: 85
Result: 8 Running total: 93
Result: 3 Running total: 96
Result: 6 Running total: 102
Result: 1 Running total: 103
Result: 9 Running total: 112
Result: 2 Running total: 114
Result: 7 Running total: 121
Result: 1 Running total: 122
Result: 9 Running total: 131
Result: 5 Running total: 136
Result: 4 Running total: 140
Result: 9 Running total: 149
Result: 1 Running total: 150

Result = SUCCESS

Ответ 15

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

Основываясь на идеях в этом посте, я придумал два возможных решения.

Первый метод:

  • Выведите наименьшее целочисленное значение, которое можно повторить, чтобы добавить число рядом с желаемым итогом. По сути, просто делите целое число.
  • Инициализировать массив со всеми значениями, равными числу, найденному на шаге 1.
  • Если есть остаток (обычно будет), произвольно добавьте его к элементам в массиве и уменьшите остаток до тех пор, пока остаток не станет 0. В этот момент у нас есть массив, который будет равен желаемому итогу, но он будет быть очень неслучайным.
  • Для ряда итераций произвольно добавьте и вычтите из двух местоположений в массиве. Пример: добавьте 1 в позицию 0 и вычтите 1 из позиции 4. При этом выполните проверки границ (все числа должны быть не менее 0, а все числа не должны превышать верхнюю границу).

Второй метод намного проще, но приводит к менее случайному распределению:

  • Инициализировать массив из 0 требуемой длины.
  • Выберите случайный индекс в массиве и добавьте 1. Если значение этого индекса превысит верхнюю границу, проигнорируйте его и выберите другой индекс.
  • Повторите шаг 2 количество раз, указанное желаемым итогом.

Вот код:

public static int[] getRandomsWithTotalA(int desiredTotal, int desiredNumbers, int upperBound)
{
    Random r = new Random();

    // Is this even a possible feat?
    if (desiredNumbers * upperBound < desiredTotal) throw new ArgumentException("This is not possible!", "desiredTotal");

    // Start by figuring out the closest number we can get to by repeating the initial number.
    int lowestRepeating = desiredTotal / desiredNumbers;

    // Determine the remainder
    int lowestRepeatingRemainder = desiredTotal % desiredNumbers;

    // Initialize and populate an array of numbers with the lowest repeater.
    int[] results = Enumerable.Repeat(lowestRepeating, desiredNumbers).ToArray();

    // We will perform (n*desiredTotal) shuffles.
    int shuffles = (desiredTotal * desiredNumbers);

    while (shuffles > 0)
    {
        int a = r.Next(desiredNumbers);
        int b= r.Next(desiredNumbers);
        if (a==b) continue; // do nothing if they're equal - try again.

        // Test bounds.
        if (results[a]+1>upperBound) continue;
        if (results[b]-1<0) continue;

        // Add one to the first item.
        results[a]++;

        // Do we still have a remainder left? If so, add one but don't subtract from
        // somewhere else.
        if (lowestRepeatingRemainder>0)
        {
            lowestRepeatingRemainder--;
            continue;
        }
        // Otherwise subtract from another place.
        results[b]--;
        // decrement shuffles
        shuffles--;
    }

    return results;
}

public static int[] getRandomsWithTotalB(int desiredTotal, int desiredNumbers, int upperBound)
{
    Random r = new Random();

    // Is this even a possible feat?
    if (desiredNumbers * upperBound < desiredTotal) throw new ArgumentException("This is not possible!", "desiredTotal");

    // Initialize and populate an array of numbers with the lowest repeater.
    int[] results = new int[desiredNumbers];

    while (desiredTotal > 0)
    {
        int a = r.Next(desiredNumbers);

        // Test bounds.
        if (results[a] + 1 > upperBound) continue;

        // Add one to the first item.
        results[a]++;

        desiredTotal--;
    }

    return results;
}

Пример прогона:

static void Main(string[] args)
{
    foreach (int i in getRandomsWithTotalA(200, 30, 9))
    {
        Console.Write("{0}, ", i);
    }
    Console.WriteLine("\n");
    foreach (int i in getRandomsWithTotalB(200, 30, 9))
    {
        Console.Write("{0}, ", i);
    }
}

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

6, 8, 5, 7, 6, 9, 9, 8, 5, 4, 4, 6, 7, 7, 8, 4, 9, 6, 6, 5, 8, 9, 9, 6, 6, 8, 7, 4, 7, 7, 

Эти методы, понятно, не так распределены, как хотелось бы. Это имело бы смысл, в частности, со вторым методом; если у вас есть случайный источник, который по-настоящему равномерно распределен, тогда ваш выбор элементов для увеличения должен иметь равную вероятность для всех возможных значений. Первый из них потенциально может быть немного лучше, но он по-прежнему страдает тем, что случайный источник также идеально распределен поровну.

Мне кажется, что возможно улучшить, по крайней мере, первый метод, введя некоторую форму смещения в выбор индекса или, возможно, рандомизацию того, сколько мы добавляем и вычитаем (не всегда 1), или рандомизацию того, мы фактически делаем сложение/вычитание или нет. Простое изменение количества итераций, похоже, меняет распределение, но через некоторое время кажется, что мы начинаем отдавать предпочтение внешним границам. (Возможно, невозможно получить по-настоящему равномерное распределение!)

В любом случае, вы идете... Хорошее место, чтобы начать хотя бы.

Ответ 16

public static List<int> getNumbers(int n)
    {
        Random random = new Random(DateTime.Now.Millisecond);
        List<int> obtainedNumbers = new List<int>(); 
        do
        {
            obtainedNumbers.Add(random.Next(1, 9));
        }
        while (n - obtainedNumbers.Sum() > 0);
        return obtainedNumbers;
    }

Код JaredPar мне нравится, но он медленный, он как бы бросает монету и надеется получить значение n.Nice куски кодов