【问题标题】:Sorting List randomly by weights按权重随机排序列表
【发布时间】:2019-08-24 23:30:03
【问题描述】:

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

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

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

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

还有另一种算法,不需要生成范围,但期望初始列表是随机顺序,然后通过减法和检查 x

我目前想要关注的方法是为每个项目生成一个排序值,然后一次性订购整个列表。我编写的代码不会产生超过一百万次尝试的预期比率。

控制台应用程序的 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% 的比率。看起来好像有某种非线性在起作用,但我现在不明白。

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

谢谢!

【问题讨论】:

  • 您每次都得到相同的随机数。您需要使用 Next() 方法: var newList = GetSorted(myList, rnd.Next());
  • 并非如此。我没有传递随机值,而是传递随机对象并在 GetSorted() 方法中调用 rnd.NextDouble() 。如果它保持不变,我将获得 100% 的最高权重项目,而其他所有项目的 0%。
  • 您遇到了重叠。所以你的范围是 1) 0 到 70 2) 0 到 20 3) 0 到 10 这 3 个概率类似于 1) 1/3 (0 到 10) + 1/2 (11 到 20) + (20 到 70)二)1/3(0 到 10)+1/2(10 到 20)三)1/3(0 到 10)
  • 谢谢!我明白其中的逻辑。虽然结果并不能完全证实这一点。在当前情况下,0-10 范围的发生概率为 10/70,然后第三个元素发生的概率为 1/3。 10/70/3 = ~4.8%。我得到了大约一半......
  • 如果你只看最后一种情况,它是 10/3 ~ 2。如果需要,我可以计算出确切的错误结果。我已经参加了足够多的大学课程来做到这一点。

标签: c# sorting random statistics probability


【解决方案1】:

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

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

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

【讨论】:

  • 感谢您的解释。它确实提供了一些见解!尽管给定的解决方案并没有真正解决它,但比率仍然不正确。例如,90/10 的概率始终变为 83/17。
  • @GigAHerZ 你是对的!我更新了我的答案以反映我的修复不正确。
【解决方案2】:

我修复了工作代码:

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

【讨论】:

  • 这不是解决办法。这是我在原始帖子中描述的不同算法。我已经知道它可以工作并产生正确的结果。找到一个可行的解决方案从来都不是问题...... 编辑:更糟糕的是,它甚至不产生项目列表,而只是选择一个项目。完全偏离主题的答案。
  • 这正是你的算法:一种是基本上创建一个范围,每个范围都有特定项目重量的长度,然后从产生的整个范围中随机选择一个点。您的代码只取了第一项,而不是数组。
  • 请阅读原文。我写过:“这里的问题是要了解,这个给定的代码是如何工作的。我知道并且有不同的方法可以工作并产生预期的结果。”您的代码在算法上与给定的代码完全不同......
  • 我的代码生成范围,每个范围都有特定项目重量的长度。我只是使用范围的上限。范围 1:0 到 69,范围 2:70 到 89,范围 3:90 到 99。这三个范围是 70、20、10,它们是权重。
猜你喜欢
  • 2018-10-12
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 1970-01-01
  • 2014-07-21
相关资源
最近更新 更多