我遇到需要在M个插槽中均匀分配N个项目的情况。每个项目都有自己的分配%。出于讨论的目的,有三个项目(a,b,c),各自的百分比(50,25,25)均匀分布在20个槽中。因此10 X a,5 X b& 5 X c需要分发。结果如下:
import { SharedService } from './shared,service';
import { SharedService } from '././shared,service';
import { SharedService } from 'src/app/shared.service';
我正在努力的部分是插槽数量,项目数量和百分比都可以变化,当然百分比总是高达100%。我写的代码产生了以下输出,它总是被重新加权以支持具有最高百分比的项目。任何想法都会很棒。
1. a
2. a
3. c
4. b
5. a
6. a
7. c
8. b
9. a
10. a
11. c
12. b
13. a
14. a
15. c
16. b
17. a
18. a
19. c
20. b
修改 这是我的代码目前的样子。如前所述,在后加权分布中得出结果。对于一些小环境,我试图在各个程序中均匀分配商业广告。因此,每次运行相同的输入都必须产生完全相同的输出。这就是排除使用随机数的原因。
1. a
2. b
3. c
4. a
5. b
6. c
7. a
8. b
9. c
10. a
11. c
12. b
13. a
14. b
15. c
16. a
17. a
18. a
19. a
20. a
编辑2 试着在这里澄清我的要求。目前,因为项目'a'将被分配10次,这是所有三个项目中最高的,所以在分发结束时,项目16-20全部仅被分配了'a'。正如评论中所提到的那样,我正在努力实现“看起来”更均匀的发行版。
答案 0 :(得分:3)
查看此问题的一种方法是作为多维线条绘制问题。所以我使用Bresenham的线算法来创建分布:
public static IEnumerable<T> GetDistribution<T>( IEnumerable<Tuple<T, int>> itemCounts )
{
var groupCounts = itemCounts.GroupBy( pair => pair.Item1 )
.Select( g => new { Item = g.Key, Count = g.Sum( pair => pair.Item2 ) } )
.OrderByDescending( g => g.Count )
.ToList();
int maxCount = groupCounts[0].Count;
var errorValues = new int[groupCounts.Count];
for( int i = 1; i < errorValues.Length; ++i )
{
var item = groupCounts[i];
errorValues[i] = 2 * groupCounts[i].Count - maxCount;
}
for( int i = 0; i < maxCount; ++i )
{
yield return groupCounts[0].Item;
for( int j = 1; j < errorValues.Length; ++j )
{
if( errorValues[j] > 0 )
{
yield return groupCounts[j].Item;
errorValues[j] -= 2 * maxCount;
}
errorValues[j] += 2 * groupCounts[j].Count;
}
}
}
输入是您想要的每个项目的实际数量。这有几个优点。首先,它可以使用整数运算,这可以避免任何舍入问题。如果您要求10个项目并且想要均匀分布3个项目(这基本上只是再次舍入问题),它也消除了任何歧义。
答案 1 :(得分:1)
这是一个没有随机数的,可以提供所需的输出。
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
// name, percentage
Dictionary<string, double> distribution = new Dictionary<string,double>();
// name, amount if one more were to be distributed
Dictionary<string, int> dishedOut = new Dictionary<string, int>();
//Initialize
int numToGive = 20;
distribution.Add("a", 0.50);
distribution.Add("b", 0.25);
distribution.Add("c", 0.25);
foreach (string name in distribution.Keys)
dishedOut.Add(name, 1);
for (int i = 0; i < numToGive; i++)
{
//find the type with the lowest weighted distribution
string nextUp = null;
double lowestRatio = double.MaxValue;
foreach (string name in distribution.Keys)
if (dishedOut[name] / distribution[name] < lowestRatio)
{
lowestRatio = dishedOut[name] / distribution[name];
nextUp = name;
}
//distribute it
dishedOut[nextUp] += 1;
Console.WriteLine(nextUp);
}
Console.ReadLine();
}
}
答案 2 :(得分:0)
//AABC AABC…
int totalA = 10
int totalB = 5
int totalC = 5
int totalItems = 20 //A+B+C
double frequencyA = totalA / totalItems; //0.5
double frequencyB = totalB / totalItems; //0.25
double frequencyC = totalC / totalItems; //0.25
double filledA = frequencyA;
double filledB = frequencyB;
double filledC = frequencyC;
string output = String.Empty;
while(output.Length < totalItems)
{
filledA += frequencyA;
filledB += frequencyB;
filledC += frequencyC;
if(filledA >= 1)
{
filledA -= 1;
output += "A";
if(output.Length == totalItems){break;}
}
if(filledB >= 1)
{
filledB -= 1
output += "B";
if(output.Length == totalItems){break;}
}
if(filledC >= 1)
{
filledC -= 1
output += "C";
if(output.Length == totalItems){break;}
}
}
答案 3 :(得分:0)
这个答案大多是被盗的,并且可以从here
轻松改编以供您使用我的想法是,您可以以最简单的方式分发您的商品,而无需按顺序排列,然后随机播放列表。
public static void ShuffleTheSameWay<T>(this IList<T> list)
{
Random rng = new Random(0);
int n = list.Count;
while (n > 1) {
n--;
int k = rng.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
小提琴here
答案 4 :(得分:0)
使用固定种子代替真正的随机数生成器,以便程序在每次运行时都具有相同的输出(对于相同的输入)。在下面的代码中,&#39; 0&#39;是种子,意思是随机的&#39;每次运行程序时生成的数字总是相同的。
Random r = new Random(0);