按重量随机排序列表

时间:2019-04-03 12:22:23

标签: c# sorting random statistics probability

这个想法是我有一个物品清单,每个物品都附有重量。现在,我想将列表的顺序随机化,但我也想考虑权重,以“偏向”随机化过程。

有多种方法,但是我特别感兴趣的是一种特定的方法以及为什么它产生的分布比我预期的要大。该代码可能很好,但是我想了解为什么它会执行它的工作?

我知道其他产生预期结果的算法。

一个方法是基本上创建一个范围,每个范围具有特定项目重量的长度,然后从所产生的整个范围中随机选择一个点。这样一遍又一遍地创建项目,直到没有项目/没有范围可供选择为止。它产生了超过一百万次尝试的预期比率。

还有另一种算法,不需要生成范围,但是希望初始列表是随机顺序的,然后通过减法并针对x <= 0进行检查,还需要逐项生成一个随机但有偏差的项目列表。它产生了超过一百万次尝试的预期比率。

我目前要集中精力的方法是为每个项目生成一个订购值,然后一次性订购整个列表。我编写的代码无法产生超过一百万次尝试的预期比率。

控制台应用程序的C#代码

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            var myList = new List<Item>
            {
                new Item { Name = "A70", Weight = 70},
                new Item { Name = "B20", Weight = 20},
                new Item { Name = "C10", Weight = 10},
            };

            var stats = new Dictionary<string, int>();
            myList.ForEach(x => stats.Add(x.Name, 0));

            var rnd = new Random();
            for (var i = 0; i < 1000000; ++i)
            {
                var newList = GetSorted(myList, rnd);
                ++stats[newList.First().Name];
            }

            var sum = stats.ToList().Sum(x => x.Value);
            stats.ToList().ForEach(x => Console.WriteLine($"{x.Key}: {((float)x.Value / sum * 100):0.00}%"));

            Console.ReadLine();
        }

        private static IEnumerable<Item> GetSorted(IEnumerable<Item> list, Random rnd)
        {
            return list
                .Select(x => new
                {
                    Order = x.Weight * rnd.NextDouble(),
                    Item = x
                })
                .OrderByDescending(x => x.Order)
                .Select(x => x.Item);
        }
    }

    class Item
    {
        public string Name { get; set; }
        public int Weight { get; set; }
    }
}

通过此代码,我希望每个项目在列表的第一位置的概率与每个项目的权重非常相似。而不是70-20-10%的比率,我得到大约85-13-2%的比率。看起来好像有些非线性正在起作用,但我现在还不了解。

这里的问题是要了解此给定代码的工作方式。我知道并且有不同的方法可以起作用并产生预期的结果。

谢谢!

2 个答案:

答案 0 :(得分:2)

这是一个解释。为简单起见,让我们考虑一个更简单的情况:

var myList = new List<Item>
{
    new Item { Name = "A20", Weight = 20},
    new Item { Name = "B10", Weight = 10},
};

我们正在通过将权重乘以随机数来确定排序顺序。如果我们将A20的权重乘以大于0.5的任何数字,则无论B10的随机数是多少,都将首先进行排序。如果我们将A20的权重乘以0.5以下的任何数字,则它与B10的机会相等,即等于第一个。因此,分布将是75%/ 25%,而不是最初直观的67%/ 33%。

要修复算法,必须使用权重的平方根。

.Select(x => new
{
    Order = Math.Sqrt(x.Weight) * rnd.NextDouble(),
    Item = x
})

更新:权重平方不是一个好的解决方法。

答案 1 :(得分:0)

我固定了代码才能正常工作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication107
{
    class Program
    {
        static void Main(string[] args)
        {
             var myList = new List<Item>
            {
                new Item { Name = "A70", Weight = 70},
                new Item { Name = "B20", Weight = 90},
                new Item { Name = "C10", Weight = 100},
            };

            var stats = new Dictionary<string, int>();
            myList.ForEach(x => stats.Add(x.Name, 0));

            var rnd = new Random();
            for (var i = 0; i < 1000000; ++i)
            {
                var newList = GetSorted(myList, rnd);
                ++stats[newList.Name];
            }

            var sum = stats.ToList().Sum(x => x.Value);
            stats.ToList().ForEach(x => Console.WriteLine("{0}: '{1}", x.Key, ((float)x.Value / sum * 100)));

            Console.ReadLine();

        }
        private static Item GetSorted(List<Item> list, Random rnd)
        {
            Item results = null;
            int value = rnd.Next(0,100);
            for(int i = 0; i < list.Count(); i++)
            {
                if (value < list[i].Weight)
                {
                    results = list[i];
                    break;
                }
            }
            return results;
        }
    }

    class Item
    {
        public string Name { get; set; }
        public int Weight { get; set; }
    }
}