[英]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.