如何均匀分配项目,没有随机数

时间:2017-06-21 19:30:59

标签: c#

我遇到需要在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'。正如评论中所提到的那样,我正在努力实现“看起来”更均匀的发行版。

5 个答案:

答案 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);