首页 > 解决方案 > 按权重随机排序列表

问题描述

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

有多种方法,但我对某种方法特别感兴趣,以及为什么它产生的分布与我预期的不同。代码可能很好,但我想了解,为什么它会这样做?

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

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

还有另一种算法,不需要生成范围,但期望初始列表是随机顺序,然后通过减法和检查 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% 的比率。看起来好像有某种非线性在起作用,但我现在不明白。

这里的问题是理解这个给定的代码是如何工作的。我知道并且有不同的方法可以工作并产生预期的结果。

谢谢!

标签: c#sortingrandomstatisticsprobability

解决方案


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

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
})

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


推荐阅读