Split value in 24 randomly sized parts using C#

前端 未结 11 1073
忘了有多久
忘了有多久 2021-02-03 12:01

I have a value, say 20010. I want to randomly divide this value over 24 hours. So basically split the value into a 24 slot big array where all slots are randomly big.

Wh

相关标签:
11条回答
  • 2021-02-03 12:17

    Assuming that you don't want to have much (any) control over the distribution of sizes, here's an approach that would work (pseudo-code).

    1. Create a list of 24 random values, generated however you like, at whatever scale
    2. Find the sum of this list
    3. Create your final list by scaling the 24 random values against your total

    Notes

    • If you use floating point arithmetic, you may be off by one or two. To avoid this, don't use scaling to complete the last value, instead fill it in with the total remaining.
    • If you do need tighter control over the distribution, use a different method to generate your initial array, but the rest doesn't need to change.
    0 讨论(0)
  • 2021-02-03 12:17

    Here's another solution that I think would work very well for this. Each time the method is called, it will return another set of randomly distributed values.

    public static IEnumerable<int> Split(int n, int m)
    {
        Random r = new Random();
        int i = 0;
    
        var dict = Enumerable.Range(1, m - 1)
            .Select(x => new { Key = r.NextDouble(), Value = x })
            .OrderBy(x => x.Key)
            .Take(n - 2)
            .Select(x => x.Value)
            .Union(new[] { 0, m })
            .OrderBy(x => x)
            .ToDictionary(x => i++);
    
        return dict.Skip(1).Select(x => x.Value - dict[x.Key - 1]);
    }
    
    0 讨论(0)
  • 2021-02-03 12:17

    I tried the solutions from David and dahlbyk but had no luck. So here is what I came up with after reading the answer from mjv:

    public static class IntExtensions
    {
        public static IEnumerable<int> Split(this int number, int parts)
        {
            var slots = Enumerable.Repeat(0, parts).ToList();
            var random = new Random();
    
            while (number > 0)
            {
                var slot = random.Next(0, parts);
                slots[slot]++;
                number--;
            }
            return slots;
        }
    }
    
    0 讨论(0)
  • 2021-02-03 12:27

    Another option would be to generate a random number between 0 and the target number. Then, add each "piece" to a list. Choose the largest "piece" and cut it in two, using another random number. Select the largest from the list (now with three pieces), and continue until you have the desired number of pieces.

    List<int> list = new List<int>();
    list.Add(2010);
    Random random = new Random();
    while (list.Count() < 24)
    {
        var largest = list.Max();
        var newPiece = random.Next(largest - 1);
        list.Remove(largest);
        list.Add(newPiece);
        list.Add(largest - newPiece);
    }
    
    0 讨论(0)
  • 2021-02-03 12:28
    class Numeric
      def n_rands(n)
        raw = (1..n).map { |x| rand } 
        raw.map { |x| x * to_f / raw.sum.to_f }.map { |x| x.to_i }.tap do |scaled|
          scaled[-1] = self - scaled[0..-2].sum
        end
      end
    end
    
    puts 1000.n_rands(10).inspect # [22, 70, 180, 192, 4, 121, 102, 179, 118, 12]
    
    0 讨论(0)
  • 2021-02-03 12:34

    This is fun. Inspired by David, here's an implementation of mjv's solution using only LINQ-provided operators. Since David's Dictionary key is just an index, we can use an array instead for the Pairwise functionality:

    var r = new Random();
    var a = Enumerable.Repeat(null, n - 1)        // Seq with (n-1) elements...
                      .Select(x => r.Next(1, m))  // ...mapped to random values
                      .Concat(new [] { 0, m })
                      .OrderBy(x => x)
                      .ToArray();
    
    return a.Skip(1).Select((x,i) => x - a[i]);
    
    0 讨论(0)
提交回复
热议问题