簡體   English   中英

使用 C# 在 # 個隨機大小的部分中拆分值(每個部分有一個最大值)

[英]Split value in # randomly sized parts using C# (with a max value per part)

我正在嘗試按照標題所暗示的那樣去做。 取一個像 100 這樣的值和像 5 這樣的零件編號。然后將 100 分成 5 個部分,加起來為 100。每個部分都是隨機的。 所以結果應該是 20、25、5、40、10。它會返回一個列表/數組。 這是我目前使用的代碼,這要歸功於 10 多年前的一篇文章。

        List<int> a = new List<int>();
        a = Enumerable.Repeat(0, numOfStats - 1)        // Seq with (n-1) elements...
                          .Select(x => Random.Range(1, points))  // ...mapped to random values
                          .Concat(new[] { 0, points })
                          .OrderBy(x => x)
                          .ToArray()
                          .ToList();

        return a.Skip(1).Select((x, i) => x - a[i]).ToList();

numStats 是分割數, points 是要分割的總值。

唯一的問題是我需要確保每個部分不超過一定數量。 因此,例如,每個部分最多 30 個。 有人知道我如何編輯它以確保零件上有夾子嗎?

放棄嘗試在一行中完成(並進行防御性編程,有很多邊緣情況)

這是一些工作代碼

如果您需要良好的隨機分布,您可能需要使“需要更多空間”和“充值”位更智能

特別注意,當maxPart很大時,返回的列表通常會下降(因為Random.Next()的最大參數會隨着算法的進行而減少)。 要解決此問題,請在最后進行洗牌,就在return之前。 洗牌值得學習,這里太多了。 這是一個好的開始關於 Shuffle

static List<int> SplitValue(int value, int nParts, int maxPart)
{
  if (maxPart < value / nParts) throw new Exception("Not possible");
  var rng = new Random();
  var lst = new List<int>();
  var total = 0;
  //  Initial random allocation
  for (var i = 0; i < nParts; i++)
  {
    var part = rng.Next(Math.Min(maxPart + 1, value - total)); // upper bound is exclusive
    lst.Add(part);
    total += part;
    //  Need more room
    if (total == value && i + 1 < nParts)
      for (var j = i; j >= 0; j--)
      {
        if (lst[i] > 0)
        {
          lst[i] -= 1;
          total--;
        }
      }
  }
  //  Top-up
  for (var i = 0; i < nParts && total < value; i++)
  {
    var topup = Math.Min(maxPart - lst[i], value - total);
    lst[i] += topup;
    total += topup;
  }
  if (total != 100) throw new Exception("Failed");
  return lst;
}

//Console.WriteLine(string.Join(',', SplitValue(100,5,10)));
Console.WriteLine(string.Join(',', SplitValue(100,5,20)));
Console.WriteLine(string.Join(',', SplitValue(100,5,30)));
Console.WriteLine(string.Join(',', SplitValue(100,5,70)));
Console.WriteLine(string.Join(',', SplitValue(100,5,70)));
Console.WriteLine(string.Join(',', SplitValue(100,5,150)));

我並沒有聲稱這是沒有錯誤的,您需要進行測試(並且好奇地想看看提供了哪些其他想法)

樣本輸出

20,20,20,20,20
30,24,19,13,14
66,26,8,0,0
46,27,5,21,1
26,42,18,11,3

我認為do - while循環可以/應該通過更多的思考來刪除:

public List<int> SplitValue(int value, int numParts, int maxPartValue)
{
    Random rnd = new Random();
    int remainingValue = value;
    int part;

    if (value / numParts > maxPartValue)
        throw new ArgumentException("Not possible");

    if (maxPartValue > value - numParts)
        throw new ArgumentException("Not possible");

    List<int> Parts = new List<int>();

    for (int loop = 1; loop < numParts; loop++)
    {
        do
        {
            part = rnd.Next(1, Math.Min(remainingValue / (numParts - loop), maxPartValue) + 1);

            if ((remainingValue - part) < (maxPartValue) * (numParts - loop))
                break;

        } while (true);

        Parts.Add(part);

        remainingValue -= part;
    }
    
    Parts.Add(value - Parts.Sum()); 
    
    return Parts;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM