Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在给定用户列表中的分布_C#_Algorithm_C# 4.0_Distribution - Fatal编程技术网

C# 在给定用户列表中的分布

C# 在给定用户列表中的分布,c#,algorithm,c#-4.0,distribution,C#,Algorithm,C# 4.0,Distribution,我有一个要求,即我有一个实体列表和可以分配该实体的用户 E1可以由U1或U2分配 E2必须由U5分配 E3可以由U2或U3或U4分配 我有这样的50K个实体,每个实体可能有1个或多个用户。对于1个用户,其clear和entity将仅分配给该用户。如果有多个用户,可以将其分配给其中的任何一个用户 我们希望将其分发给每个用户,以便每个用户获得相等数量的实体。存在最小可能/不可避免的扭曲分布,而且每个用户可能已经拥有一些实体:U1已经有2K,U2已经有3K实体,因此分布也应该考虑这一事实 编辑1 我们

我有一个要求,即我有一个实体列表和可以分配该实体的用户

E1可以由U1或U2分配

E2必须由U5分配

E3可以由U2或U3或U4分配

我有这样的50K个实体,每个实体可能有1个或多个用户。对于1个用户,其clear和entity将仅分配给该用户。如果有多个用户,可以将其分配给其中的任何一个用户

我们希望将其分发给每个用户,以便每个用户获得相等数量的实体。存在最小可能/不可避免的扭曲分布,而且每个用户可能已经拥有一些实体:U1已经有2K,U2已经有3K实体,因此分布也应该考虑这一事实

编辑1

我们已经尝试了一种解决方案,即按顺序进行,并根据当时分配给用户的分配一次分配一个实体,但这会产生扭曲的结果,因为我们得到的用户在较早时分配较少,但在较晚时分配较多,反之亦然

E1至E25“必须由U1和U2中的任何一个处理

E26至E50“必须由U2和U3中的任何一个处理

如果我们按顺序进行,最后:U1得到12(从E1-E25),U2得到19(从E1-E25得到13,从E26-E50得到6),U3得到19(从E26-E50)。 所以总共分配了50个。好的但看到了扭曲的结果吗

EDIT2


为什么每个实体有不同的用户?要分发多个产品。有些用户处理多个产品,有些用户处理单个产品,但所有用户都需要负载平衡。

我可能误解了您的问题您的问题,但从我对您描述的理解来看,问题似乎很简单

关于创建新实体 论现有实体的重新配置 如果要“重新分配”已分配的实体:

Take the number of entities you want to reallocate and divide it by the number of users and allocate them accordingly

您好,您可以维护一个基于实体数排序的树形图吗。每次在树映射(0)处将实体分配给用户时,都会增加实体数

我分两步做了这件事

  • 将所有实体仅分配给一个用户
  • 按顺序分配其余实体
  • 平衡用户之间的实体
  • 通过分发任何差异来解决剩余问题
  • 原始代码

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    
        public class Entity
        {
            public string Type { get; set; }
        }
    
        public class User
        {
            public User()
            {
                EntitiesCount = new Dictionary<string, int>();
            }
            public string userId { get; set; }
            public Dictionary<string, int> EntitiesCount { get; set; }
            public int TotalEntities { get; set; }
            public bool Ignored { get; set; }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
    
                for (var myLoop = 0; myLoop < 100; myLoop++)
                {
                    //Create users
                    var userList = new List<User> {
                        new User { userId = "U0" } ,
                        new User { userId = "U1" } ,
                        new User { userId = "U2" } ,
                        new User { userId = "U3" } ,
                        new User { userId = "U4" } 
                    };
                    userList = userList.OrderBy(u => u.userId).ToList();
                    //Assign Users to Entities
                    var entityUsers = new Dictionary<string, List<User>>() { 
                        { "E0", 
                          new List<User> { 
                              userList[0] ,
                              userList[1] 
                          } 
                        } ,
                        { "E1", 
                          new List<User> { 
                              userList[4]
                          } 
                        } ,
                        { "E2", 
                          new List<User> { 
                              userList[1],
                              userList[2],
                              userList[3] 
                          } 
                        } 
                    };
    
    
                    //var entityUsers = new Dictionary<string, List<User>>() { 
                    //        { "E0", 
                    //          new List<User> { 
                    //              userList[0] ,
                    //              userList[1] 
                    //          } 
                    //        } ,
                    //        { "E1", 
                    //          new List<User> { 
                    //              userList[1],
                    //              userList[2],
                    //          } 
                    //        } ,
                    //    };
    
                    //Load Entities, you can change the number of entities generated her
                    var entities = GenerateEntities(entityUsers.Count(), 50000);
    
                    //Group the Entities by their type and display total number
                    var lookupEntities = entities.ToLookup(e => e.Type);
                    foreach (var lookupEntity in lookupEntities)
                    {
                        Console.WriteLine(lookupEntity.Key + " has " + lookupEntity.Count());
                    }
    
    
                    // Users are ignored if there is a one to one mapping
                    var ignoreUsers = 0;
    
                    //Entities are ignored if they are only handled by one user
                    var ignoreEntities = 0;
    
                    foreach (var entityUser in entityUsers)
                    {
                        foreach (var user in entityUser.Value)
                        {
                            user.EntitiesCount.Add(entityUser.Key, 0);
                        }
                    }
    
                    //Assign entities where only one user available
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count == 1))
                    {
    
                        Console.WriteLine("Assigning all " + entityUser.Key + " to " + entityUser.Value[0].userId + " - " + lookupEntities[entityUser.Key].Count());
    
                        entityUser.Value[0].TotalEntities += lookupEntities[entityUser.Key].Count();
                        entityUser.Value[0].EntitiesCount[entityUser.Key] = lookupEntities[entityUser.Key].Count();
    
                        //Ignore these entities because they cannot changed
                        ignoreEntities += entityUser.Value[0].TotalEntities;
    
                        if (entityUsers.Count(e => e.Value.Contains(entityUser.Value[0])) == 1)
                        {
                            //The user is only assigned to this one entity so ignore user in balancing
                            ignoreUsers++;
                            entityUser.Value[0].Ignored = true;
                        }
                    }
    
    
    
                    //Assign entities where more than one user available
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count > 1))
                    {
                        var numberOfEntities = lookupEntities[entityUser.Key].Count();
                        for (var i = 0; i < numberOfEntities; i++)
                        {
                            var user = entityUser.Value.OrderBy(u => u.TotalEntities).First();
                            if (!user.EntitiesCount.ContainsKey(entityUser.Key))
                                user.EntitiesCount.Add(entityUser.Key, 0);
                            user.EntitiesCount[entityUser.Key]++;
                            user.TotalEntities++;
                        }
    
                    }
    
                    var averagePerUser = 0;
                    var busyUsers = userList.Count(a => a.TotalEntities != 0);
    
                    //Check to see if there is only one users assigned to each entity
                    if (busyUsers != ignoreUsers)
                    {
                        //Calculate the expected average per user 
    
                        var totalEntities = entities.Count;
                        averagePerUser = (totalEntities - ignoreEntities) / (busyUsers - ignoreUsers);
    
                        Console.WriteLine();
                        Console.WriteLine("Total Entities: " + totalEntities);
                        Console.WriteLine("Average Entities: " + averagePerUser);
                        Console.WriteLine();
    
                        OutputAllocation(userList, averagePerUser);
                        var orderedUserList = userList.OrderByDescending(u => u.TotalEntities).ToList();
                        //Loop through the users and compare to the remaining users
                        for (var i = 0; i < orderedUserList.Count - 1; i++)
                        {
                            for (var j = i + 1; j < userList.Count; j++)
                            {
                                BalanceUsers(userList[i], userList[j], entityUsers, averagePerUser);
                            }
                        }
    
                        ////Loop through the list in reverse order ?
                        //for (var i = userList.Count - 1; i >= 0; i--)
                        //{
                        //    for (var j = i - 1; j >= 0; j--)
                        //    {
                        //        BalanceUsers(userList[i], userList[j], entityUsers, averagePerUser);
                        //    }
                        //}
                    }
    
                    OutputAllocation(userList, averagePerUser);
    
                    Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                    Console.WriteLine();
    
                    //Even out remaining difference across entity Type
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count > 1))
                    {
                        if (entityUser.Value.Any(u => (u.TotalEntities - averagePerUser > 0)))
                        {
                            var users = entityUser.Value.Where(u => (u.TotalEntities - averagePerUser != 0) && u.EntitiesCount[entityUser.Key] > 0);
    
                            var difference = 0;
                            foreach (var user in users)
                            {
                                difference += user.TotalEntities - averagePerUser;
                                user.TotalEntities -= difference;
                                user.EntitiesCount[entityUser.Key] -= difference;
                            }
    
                            List<User> fixUsers = null;
                            if (difference < 0)
                            {
                                fixUsers = entityUser.Value.Where(u => (u.EntitiesCount[entityUser.Key] > 0)).ToList();
                            }
                            else
                            {
                                fixUsers = entityUser.Value;
                            }
                            var change = difference / fixUsers.Count();
                            var userCount = fixUsers.Count();
                            foreach (var fixUser in fixUsers)
                            {
                                fixUser.TotalEntities += change;
                                fixUser.EntitiesCount[entityUser.Key] += change;
                                difference -= change;
                                userCount--;
                                //Correct change so that nothing gets lost
                                if (userCount != 0)
                                    change = difference / userCount;
                                else
                                    change = difference;
    
                            }
                        }
                    }
    
                    OutputAllocation(userList, averagePerUser);
    
                    Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                    Console.WriteLine();
    
                    foreach (var lookupEntity in lookupEntities)
                    {
                        Console.Write(lookupEntity.Key + " - " + lookupEntity.Count());
                        Console.Write(" Allocation: ");
                        foreach (User user in entityUsers[lookupEntity.Key])
                        {
                            Debug.Assert(user.EntitiesCount[lookupEntity.Key] >= 0);
                            Console.Write(user.userId + " = " + user.EntitiesCount[lookupEntity.Key] + "; ");
                        }
                        Console.WriteLine();
                    }
    
                }
                Console.ReadLine();
            }
    
            private static void OutputAllocation(List<User> userList, int averagePerUser)
            {
                //Display allocation after initial assignment
                foreach (var user in userList)
                {
                    var difference = user.TotalEntities - averagePerUser;
                    if (user.Ignored)
                        Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities);
                    else
                        Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities + " difference " + difference);
                }
                Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                Console.WriteLine();
            }
    
    
            /// <summary>
            /// Compares two users and balances them out
            /// </summary>
            private static void BalanceUsers(User firstUser, User secondUser, Dictionary<string, List<User>> entityUsers, int averagePerWorker)
            {
                //Get the difference betweent the current users and the average worker
                var firstUserDiff = firstUser.TotalEntities - averagePerWorker;
                var secondUserDiff = secondUser.TotalEntities - averagePerWorker;
    
    
                //Get all the entities which the two users share
                var sharedEntityTypes = entityUsers.Where(x => x.Value.Contains(firstUser) && x.Value.Contains(secondUser)).Select(e => e.Key);
    
                foreach (var entityType in sharedEntityTypes)
                {
                    var difference = firstUserDiff;
    
                    if (firstUser.EntitiesCount.Count() > secondUser.EntitiesCount.Count())
                    {
                        difference = -1 * secondUserDiff;
                    }
                    else if (firstUser.EntitiesCount.Count() == secondUser.EntitiesCount.Count())
                    {
                        difference = firstUserDiff - secondUserDiff;
                    }
                    else
                    {
                        difference = firstUserDiff;
                    }
    
                    difference = firstUserDiff;
    
                    var maxAllowed = 0;
                    if (difference > 0)
                    {
                        maxAllowed = firstUser.EntitiesCount[entityType] > difference ? difference : firstUser.EntitiesCount[entityType];
                    }
                    else
                    {
                        maxAllowed = secondUser.EntitiesCount[entityType] > Math.Abs(difference) ? difference : -1 * secondUser.EntitiesCount[entityType];
                    }
    
                    firstUser.EntitiesCount[entityType] -= maxAllowed;
                    firstUser.TotalEntities -= maxAllowed;
    
                    secondUser.EntitiesCount[entityType] += maxAllowed;
                    secondUser.TotalEntities += maxAllowed;
    
                    firstUserDiff = firstUser.TotalEntities - averagePerWorker;
                    secondUserDiff = secondUser.TotalEntities - averagePerWorker;
                }
            }
    
    
    
            private static List<Entity> GenerateEntities(int maxEntityTypes, int totalEntities)
            {
                var entityTypes = new List<string>();
                for (var i = 0; i < maxEntityTypes; i++)
                {
                    entityTypes.Add("E" + i);
                }
                var entities = new List<Entity>();
                Random random = new Random();
                for (var i = 0; i < totalEntities; i++)
                {
                    //Randomly allocate user
                    entities.Add(new Entity { Type = entityTypes[random.Next(maxEntityTypes)] });
    
                    //Used to get even distribution
                    //entities.Add(new Entity { Type = entityTypes[i%maxEntityTypes] });
    
                    //Used to get specific ratio
                    //var type = "";
                    //switch (i % 3)
                    //{
                    //    case 0:
                    //        type = "E0";
                    //        break;
                    //    case 1:
                    //    case 2:
                    //        type = "E1";
                    //        break;
                    //}
    
                    //entities.Add(new Entity { Type = type });
                }
                return entities;
    
    
            }
        }
    
    使用系统;
    使用System.Collections.Generic;
    使用系统诊断;
    使用System.Linq;
    公共类实体
    {
    公共字符串类型{get;set;}
    }
    公共类用户
    {
    公共用户()
    {
    EntitiesCount=新字典();
    }
    公共字符串用户标识{get;set;}
    公共字典实体计数{get;set;}
    公共整数TotalEntities{get;set;}
    公共布尔忽略{get;set;}
    }
    班级计划
    {
    静态void Main(字符串[]参数)
    {
    对于(var myLoop=0;myLoop<100;myLoop++)
    {
    //创建用户
    var userList=新列表{
    新用户{userId=“U0”},
    新用户{userId=“U1”},
    新用户{userId=“U2”},
    新用户{userId=“U3”},
    新用户{userId=“U4”}
    };
    userList=userList.OrderBy(u=>u.userId.ToList();
    //将用户分配给实体
    var entityUsers=new Dictionary(){
    {“E0”,
    新列表{
    用户列表[0],
    用户列表[1]
    } 
    } ,
    {“E1”,
    新列表{
    用户列表[4]
    } 
    } ,
    {“E2”,
    新列表{
    用户列表[1],
    用户列表[2],
    用户列表[3]
    } 
    } 
    };
    //var entityUsers=new Dictionary(){
    //{“E0”,
    //新列表{
    //用户列表[0],
    //用户列表[1]
    //          } 
    //        } ,
    //{“E1”,
    //新列表{
    //用户列表[1],
    //用户列表[2],
    //          } 
    //        } ,
    //    };
    //加载实体时,可以更改生成的实体数
    var entities=GenerateEntities(entityUsers.Count(),50000);
    //按实体类型对其进行分组并显示总数
    var lookupEntities=entities.ToLookup(e=>e.Type);
    foreach(lookupEntities中的变量lookupEntity)
    {
    Console.WriteLine(lookupEntity.Key+“具有”+lookupEntity.Count());
    }
    //如果存在一对一映射,则忽略用户
    var ignoreUsers=0;
    //如果实体仅由一个用户处理,则将忽略它们
    var ignoreEntities=0;
    foreach(entityUsers中的var entityUser)
    {
    foreach(entityUser.Value中的var user)
    {
    user.EntitiesCount.Add(entityUser.Key,0);
    }
    }
    //在只有一个用户可用的情况下分配实体
    foreach(entityUsers.Where中的var entityUser(a=>a.Value.Count==1))
    {
    Console.WriteLine(“将所有“+entityUser.Key+”分配给“+entityUser.Value[0].userId+”-“+lookupEntities[entityUser.Key].Count());
    实体用户。
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    
        public class Entity
        {
            public string Type { get; set; }
        }
    
        public class User
        {
            public User()
            {
                EntitiesCount = new Dictionary<string, int>();
            }
            public string userId { get; set; }
            public Dictionary<string, int> EntitiesCount { get; set; }
            public int TotalEntities { get; set; }
            public bool Ignored { get; set; }
    
        }
    
        class Program
        {
            static void Main(string[] args)
            {
    
                for (var myLoop = 0; myLoop < 100; myLoop++)
                {
                    //Create users
                    var userList = new List<User> {
                        new User { userId = "U0" } ,
                        new User { userId = "U1" } ,
                        new User { userId = "U2" } ,
                        new User { userId = "U3" } ,
                        new User { userId = "U4" } 
                    };
                    userList = userList.OrderBy(u => u.userId).ToList();
                    //Assign Users to Entities
                    var entityUsers = new Dictionary<string, List<User>>() { 
                        { "E0", 
                          new List<User> { 
                              userList[0] ,
                              userList[1] 
                          } 
                        } ,
                        { "E1", 
                          new List<User> { 
                              userList[4]
                          } 
                        } ,
                        { "E2", 
                          new List<User> { 
                              userList[1],
                              userList[2],
                              userList[3] 
                          } 
                        } 
                    };
    
    
                    //var entityUsers = new Dictionary<string, List<User>>() { 
                    //        { "E0", 
                    //          new List<User> { 
                    //              userList[0] ,
                    //              userList[1] 
                    //          } 
                    //        } ,
                    //        { "E1", 
                    //          new List<User> { 
                    //              userList[1],
                    //              userList[2],
                    //          } 
                    //        } ,
                    //    };
    
                    //Load Entities, you can change the number of entities generated her
                    var entities = GenerateEntities(entityUsers.Count(), 50000);
    
                    //Group the Entities by their type and display total number
                    var lookupEntities = entities.ToLookup(e => e.Type);
                    foreach (var lookupEntity in lookupEntities)
                    {
                        Console.WriteLine(lookupEntity.Key + " has " + lookupEntity.Count());
                    }
    
    
                    // Users are ignored if there is a one to one mapping
                    var ignoreUsers = 0;
    
                    //Entities are ignored if they are only handled by one user
                    var ignoreEntities = 0;
    
                    foreach (var entityUser in entityUsers)
                    {
                        foreach (var user in entityUser.Value)
                        {
                            user.EntitiesCount.Add(entityUser.Key, 0);
                        }
                    }
    
                    //Assign entities where only one user available
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count == 1))
                    {
    
                        Console.WriteLine("Assigning all " + entityUser.Key + " to " + entityUser.Value[0].userId + " - " + lookupEntities[entityUser.Key].Count());
    
                        entityUser.Value[0].TotalEntities += lookupEntities[entityUser.Key].Count();
                        entityUser.Value[0].EntitiesCount[entityUser.Key] = lookupEntities[entityUser.Key].Count();
    
                        //Ignore these entities because they cannot changed
                        ignoreEntities += entityUser.Value[0].TotalEntities;
    
                        if (entityUsers.Count(e => e.Value.Contains(entityUser.Value[0])) == 1)
                        {
                            //The user is only assigned to this one entity so ignore user in balancing
                            ignoreUsers++;
                            entityUser.Value[0].Ignored = true;
                        }
                    }
    
    
    
                    //Assign entities where more than one user available
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count > 1))
                    {
                        var numberOfEntities = lookupEntities[entityUser.Key].Count();
                        for (var i = 0; i < numberOfEntities; i++)
                        {
                            var user = entityUser.Value.OrderBy(u => u.TotalEntities).First();
                            if (!user.EntitiesCount.ContainsKey(entityUser.Key))
                                user.EntitiesCount.Add(entityUser.Key, 0);
                            user.EntitiesCount[entityUser.Key]++;
                            user.TotalEntities++;
                        }
    
                    }
    
                    var averagePerUser = 0;
                    var busyUsers = userList.Count(a => a.TotalEntities != 0);
    
                    //Check to see if there is only one users assigned to each entity
                    if (busyUsers != ignoreUsers)
                    {
                        //Calculate the expected average per user 
    
                        var totalEntities = entities.Count;
                        averagePerUser = (totalEntities - ignoreEntities) / (busyUsers - ignoreUsers);
    
                        Console.WriteLine();
                        Console.WriteLine("Total Entities: " + totalEntities);
                        Console.WriteLine("Average Entities: " + averagePerUser);
                        Console.WriteLine();
    
                        OutputAllocation(userList, averagePerUser);
                        var orderedUserList = userList.OrderByDescending(u => u.TotalEntities).ToList();
                        //Loop through the users and compare to the remaining users
                        for (var i = 0; i < orderedUserList.Count - 1; i++)
                        {
                            for (var j = i + 1; j < userList.Count; j++)
                            {
                                BalanceUsers(userList[i], userList[j], entityUsers, averagePerUser);
                            }
                        }
    
                        ////Loop through the list in reverse order ?
                        //for (var i = userList.Count - 1; i >= 0; i--)
                        //{
                        //    for (var j = i - 1; j >= 0; j--)
                        //    {
                        //        BalanceUsers(userList[i], userList[j], entityUsers, averagePerUser);
                        //    }
                        //}
                    }
    
                    OutputAllocation(userList, averagePerUser);
    
                    Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                    Console.WriteLine();
    
                    //Even out remaining difference across entity Type
                    foreach (var entityUser in entityUsers.Where(a => a.Value.Count > 1))
                    {
                        if (entityUser.Value.Any(u => (u.TotalEntities - averagePerUser > 0)))
                        {
                            var users = entityUser.Value.Where(u => (u.TotalEntities - averagePerUser != 0) && u.EntitiesCount[entityUser.Key] > 0);
    
                            var difference = 0;
                            foreach (var user in users)
                            {
                                difference += user.TotalEntities - averagePerUser;
                                user.TotalEntities -= difference;
                                user.EntitiesCount[entityUser.Key] -= difference;
                            }
    
                            List<User> fixUsers = null;
                            if (difference < 0)
                            {
                                fixUsers = entityUser.Value.Where(u => (u.EntitiesCount[entityUser.Key] > 0)).ToList();
                            }
                            else
                            {
                                fixUsers = entityUser.Value;
                            }
                            var change = difference / fixUsers.Count();
                            var userCount = fixUsers.Count();
                            foreach (var fixUser in fixUsers)
                            {
                                fixUser.TotalEntities += change;
                                fixUser.EntitiesCount[entityUser.Key] += change;
                                difference -= change;
                                userCount--;
                                //Correct change so that nothing gets lost
                                if (userCount != 0)
                                    change = difference / userCount;
                                else
                                    change = difference;
    
                            }
                        }
                    }
    
                    OutputAllocation(userList, averagePerUser);
    
                    Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                    Console.WriteLine();
    
                    foreach (var lookupEntity in lookupEntities)
                    {
                        Console.Write(lookupEntity.Key + " - " + lookupEntity.Count());
                        Console.Write(" Allocation: ");
                        foreach (User user in entityUsers[lookupEntity.Key])
                        {
                            Debug.Assert(user.EntitiesCount[lookupEntity.Key] >= 0);
                            Console.Write(user.userId + " = " + user.EntitiesCount[lookupEntity.Key] + "; ");
                        }
                        Console.WriteLine();
                    }
    
                }
                Console.ReadLine();
            }
    
            private static void OutputAllocation(List<User> userList, int averagePerUser)
            {
                //Display allocation after initial assignment
                foreach (var user in userList)
                {
                    var difference = user.TotalEntities - averagePerUser;
                    if (user.Ignored)
                        Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities);
                    else
                        Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities + " difference " + difference);
                }
                Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
                Console.WriteLine();
            }
    
    
            /// <summary>
            /// Compares two users and balances them out
            /// </summary>
            private static void BalanceUsers(User firstUser, User secondUser, Dictionary<string, List<User>> entityUsers, int averagePerWorker)
            {
                //Get the difference betweent the current users and the average worker
                var firstUserDiff = firstUser.TotalEntities - averagePerWorker;
                var secondUserDiff = secondUser.TotalEntities - averagePerWorker;
    
    
                //Get all the entities which the two users share
                var sharedEntityTypes = entityUsers.Where(x => x.Value.Contains(firstUser) && x.Value.Contains(secondUser)).Select(e => e.Key);
    
                foreach (var entityType in sharedEntityTypes)
                {
                    var difference = firstUserDiff;
    
                    if (firstUser.EntitiesCount.Count() > secondUser.EntitiesCount.Count())
                    {
                        difference = -1 * secondUserDiff;
                    }
                    else if (firstUser.EntitiesCount.Count() == secondUser.EntitiesCount.Count())
                    {
                        difference = firstUserDiff - secondUserDiff;
                    }
                    else
                    {
                        difference = firstUserDiff;
                    }
    
                    difference = firstUserDiff;
    
                    var maxAllowed = 0;
                    if (difference > 0)
                    {
                        maxAllowed = firstUser.EntitiesCount[entityType] > difference ? difference : firstUser.EntitiesCount[entityType];
                    }
                    else
                    {
                        maxAllowed = secondUser.EntitiesCount[entityType] > Math.Abs(difference) ? difference : -1 * secondUser.EntitiesCount[entityType];
                    }
    
                    firstUser.EntitiesCount[entityType] -= maxAllowed;
                    firstUser.TotalEntities -= maxAllowed;
    
                    secondUser.EntitiesCount[entityType] += maxAllowed;
                    secondUser.TotalEntities += maxAllowed;
    
                    firstUserDiff = firstUser.TotalEntities - averagePerWorker;
                    secondUserDiff = secondUser.TotalEntities - averagePerWorker;
                }
            }
    
    
    
            private static List<Entity> GenerateEntities(int maxEntityTypes, int totalEntities)
            {
                var entityTypes = new List<string>();
                for (var i = 0; i < maxEntityTypes; i++)
                {
                    entityTypes.Add("E" + i);
                }
                var entities = new List<Entity>();
                Random random = new Random();
                for (var i = 0; i < totalEntities; i++)
                {
                    //Randomly allocate user
                    entities.Add(new Entity { Type = entityTypes[random.Next(maxEntityTypes)] });
    
                    //Used to get even distribution
                    //entities.Add(new Entity { Type = entityTypes[i%maxEntityTypes] });
    
                    //Used to get specific ratio
                    //var type = "";
                    //switch (i % 3)
                    //{
                    //    case 0:
                    //        type = "E0";
                    //        break;
                    //    case 1:
                    //    case 2:
                    //        type = "E1";
                    //        break;
                    //}
    
                    //entities.Add(new Entity { Type = type });
                }
                return entities;
    
    
            }
        }
    
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    
    
    public class Entity
    {
        public Entity()
        {
            _users = new List<User>();
        }
        public string Type { get; set; }
    
        public List<User> _users;
        public List<User> Users
        {
            get
            {
                //You can add your rules for users here 
                return _users;
            }
        }
        public User AssignedUser { get; set; }
    
    }
    
    public class User
    {
        public User()
        {
            Entities = new Dictionary<string, Entity>();
        }
        public string userId { get; set; }
        public Dictionary<string, Entity> Entities { get; set; }
        public int TotalEntities { get; set; }
        public bool Ignored { get; set; }
    
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            //Load Entities, you can change the number of entities generated here
            int numEntityToGenerate = 50001;
            //Create users
            var userList = new List<User> {
                                new User { userId = "U0" } ,
                                new User { userId = "U1" } ,
                                new User { userId = "U2" } ,
                                new User { userId = "U3" } ,
                                new User { userId = "U4" } 
                            };
            userList = userList.OrderBy(u => u.userId).ToList();
    
            var entities = GenerateEntities(userList, numEntityToGenerate);
    
            foreach (var entity in entities)
            {
                foreach (var user in entity.Users)
                {
                    user.Entities.Add(entity.Type, null);
                }
            }
    
            foreach (var entity in entities)
            {
                Console.Write(".");
                var user = entity.Users.OrderBy(u => u.TotalEntities).First();
                if (!user.Entities.ContainsKey(entity.Type))
                    user.Entities.Add(entity.Type, null);
                user.Entities[entity.Type] = entity;
                user.TotalEntities++;
                entity.AssignedUser = user;
            }
    
            var averagePerUser = 0;
            var busyUsers = userList.Count(a => a.TotalEntities != 0);
    
    
            //Calculate the expected average per user 
            var totalEntities = entities.Count;
            averagePerUser = (totalEntities) / (busyUsers);
    
            Console.WriteLine();
            Console.WriteLine("Total Entities: " + totalEntities);
            Console.WriteLine("Average Entities: " + averagePerUser);
            Console.WriteLine("Busy Users: " + busyUsers);
            Console.WriteLine();
    
            List<int> oldDifference = null;
            List<int> newDifference = null;
    
            do
            {
                oldDifference = GetDifferenceList(userList, averagePerUser);
                OutputAllocation(userList, averagePerUser);
                var orderedUserList = userList.OrderByDescending(u => u.TotalEntities).ToList();
                //Loop through the users and compare to the remaining users
                for (var i = 0; i < orderedUserList.Count - 1; i++)
                {
                    for (var j = i + 1; j < userList.Count; j++)
                    {
                        BalanceUsers(userList[i], userList[j], entities, averagePerUser);
                    }
                }
                newDifference = GetDifferenceList(userList, averagePerUser);
            } while (!Enumerable.SequenceEqual(oldDifference.OrderBy(t => t), newDifference.OrderBy(t => t)));
    
    
            Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
            Console.WriteLine();
    
            OutputAllocation(userList, averagePerUser);
    
            Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
            Console.WriteLine();
    
    
            //Check data quality
            foreach (var entity in entities)
            {
                //Check to see if assigned user is valid
                Debug.Assert(entity.Users.Contains(entity.AssignedUser));
            }
            Console.ReadLine();
        }
    
        private static List<int> GetDifferenceList(List<User> userList, int averagePerUser)
        {
            var differences = new List<int>();
    
            foreach (var user in userList)
            {
                var difference = user.TotalEntities - averagePerUser;
                if (user.TotalEntities != 0)
                    differences.Add(difference);
            }
            return differences;
    
        }
    
        private static void OutputAllocation(List<User> userList, int averagePerUser)
        {
            //Display allocation after initial assignment
            foreach (var user in userList)
            {
                var difference = user.TotalEntities - averagePerUser;
                if (user.TotalEntities == 0)
                    Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities);
                else
                    Console.WriteLine("Assignment  " + user.userId + " has " + user.TotalEntities + " difference " + difference);
            }
            Console.WriteLine("Total assigned: " + userList.Sum(u => u.TotalEntities));
            Console.WriteLine();
        }
    
    
        /// <summary>
        /// Compares two users and balances them out
        /// </summary>
        private static void BalanceUsers(User firstUser, User secondUser, List<Entity> entities, int averagePerWorker)
        {
            //Get the difference betweent the current users and the average worker
            var firstUserDiff = firstUser.TotalEntities - averagePerWorker;
            var secondUserDiff = secondUser.TotalEntities - averagePerWorker;
    
    
            if ((firstUserDiff != 0 && secondUserDiff != 0) && Math.Abs(firstUserDiff - secondUserDiff) > 1)
            {
                //Get all the entities which the two users share
                var sharedEntity = entities.Where(x => x.Users.Contains(firstUser) && x.Users.Contains(secondUser));
    
                foreach (var entity in sharedEntity)
                {
                    //Find out the direction the change needs to occur
                    if (firstUserDiff >= secondUserDiff)
                    {
                        //Removing from firstUser so find out if it has the entity
                        if (firstUser.Entities[entity.Type] != null)
                        {
                            firstUser.Entities[entity.Type] = null;
                            firstUser.TotalEntities--;
    
                            secondUser.Entities[entity.Type] = entity;
                            secondUser.TotalEntities++;
    
                            entity.AssignedUser = secondUser;
                        }
                    }
                    else
                    {
                        //Removing from secondUser so find out if it has the entity
                        if (secondUser.Entities[entity.Type] != null)
                        {
                            firstUser.Entities[entity.Type] = entity;
                            firstUser.TotalEntities++;
    
                            secondUser.Entities[entity.Type] = null;
                            secondUser.TotalEntities--;
    
                            entity.AssignedUser = firstUser;
                        }
                    }
    
                    firstUserDiff = firstUser.TotalEntities - averagePerWorker;
                    secondUserDiff = secondUser.TotalEntities - averagePerWorker;
    
                    //Check to see if the two users have been balanced or if the difference is only one
                    //IF that is the case break the for loop
                    if ((firstUserDiff != 0 && secondUserDiff != 0) && (firstUserDiff == secondUserDiff) && Math.Abs(firstUserDiff - secondUserDiff) <= 1)
                        break;
                }
            }
        }
    
    
        /// <summary>
        /// Generate a list of entities randomly adding a list of potential users to each entity
        /// </summary>
        /// <param name="userList">list of available users</param>
        /// <param name="totalEntities">Total number of entities required</param>
        /// <returns>A list of entities</returns>
        private static List<Entity> GenerateEntities(List<User> userList, int totalEntities)
        {
            var entities = new List<Entity>();
            Random random = new Random();
            for (var i = 0; i < totalEntities; i++)
            {
                var entity = new Entity { Type = "E" + (i + 1).ToString() };
                entities.Add(entity);
    
                //This code will either an entity to the last user or to a random list of users excluding the last one
                if (random.Next(12) == 0)
                {
                    var user = userList[userList.Count() - 1];
                    entity.Users.Add(user);
                }
                else
                {
                    var numOfUsers = random.Next(2);
    
                    for (var j = 0; j <= numOfUsers; j++)
                    {
                        var user = userList[random.Next(userList.Count() - 1)];
                        if (!entity.Users.Contains(user))
                            entity.Users.Add(user);
                    }
                }
    
                //if (i <= totalEntities / 2 )
                //{
                //    entity.Users.Add(userList[0]);
                //    entity.Users.Add(userList[1]);
                //}
                //else
                //{
                //    entity.Users.Add(userList[2]);
                //    entity.Users.Add(userList[1]);
                //}
    
            }
            return entities;
    
    
        }
    }