【问题标题】:How to optimize this suboptimal Set-Cover solution?如何优化这个次优的 Set-Cover 解决方案?
【发布时间】:2010-10-09 21:12:41
【问题描述】:

我编写了这个程序来测试“解决”集合覆盖问题需要多长时间。

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

namespace SetCover
{
    class Program
    {
        const int maxNumItems = 10000;
        const int numSets = 5000;
        const int maxItemsPerSet = 300;

        static void Main(string[] args)
        {
            var rand = new Random();
            var sets = new List<HashSet<int>>(numSets);
            var cover = new List<HashSet<int>>(numSets);
            var universe = new HashSet<int>();
            HashSet<int> remaining;
            var watch = new Stopwatch();


            Console.Write("Generating sets...");
            for (int i = 0; i < numSets; ++i)
            {
                int numItemsInSet = rand.Next(1, maxItemsPerSet);
                sets.Add(new HashSet<int>());

                for (int j = 0; j < numItemsInSet; ++j)
                {
                    sets[i].Add(rand.Next(maxNumItems));
                }
            }
            Console.WriteLine("Done!");

            Console.Write("Computing universe...");
            foreach (var set in sets)
                foreach (var item in set)
                    universe.Add(item);
            Console.WriteLine("Found {0} items.", universe.Count);

            watch.Start();

            //Console.Write("Removing subsets...");
            //int numSetsRemoved = sets.RemoveAll(subset => sets.Any(superset => subset != superset && subset.IsSubsetOf(superset)));
            //Console.WriteLine("Removed {0} subsets.", numSetsRemoved);


            //Console.Write("Sorting sets...");
            //sets = sets.OrderByDescending(s => s.Count).ToList();
            //Console.WriteLine("{0} elements in largest set.", sets[0].Count);


            Console.WriteLine("Computing cover...");
            remaining = universe.ToHashSet();
            while (remaining.Any())
            {
                Console.Write("  Finding set {0}...", cover.Count + 1);
                var nextSet = sets.MaxBy(s => s.Intersect(remaining).Count());
                remaining.ExceptWith(nextSet);
                cover.Add(nextSet);
                Console.WriteLine("{0} elements remaining.", remaining.Count);
            }
            Console.WriteLine("{0} sets in cover.", cover.Count);

            watch.Stop();

            Console.WriteLine("Computed cover in {0} seconds.", watch.Elapsed.TotalSeconds);

            Console.ReadLine();
        }
    }

    public static class Extensions
    {
        public static HashSet<TValue> Clone<TValue>(this HashSet<TValue> set)
        {
            var tmp = new TValue[set.Count];
            set.CopyTo(tmp, 0);
            return new HashSet<TValue>(tmp);
        }

        public static HashSet<TSource> ToHashSet<TSource>(this IEnumerable<TSource> source)
        {
            return new HashSet<TSource>(source);
        }
    }
}

这只是一个贪婪的次优解决方案,但它仍然需要 147 秒才能运行。然而,我认为,这个解决方案应该非常接近到最佳状态,所以它应该足以满足我的目的。不过,我怎样才能加快速度呢?

我注释掉了几行,因为它们弊大于利。 编辑:计算宇宙实际上不应该是时间的一部分......这是可以预先知道的。

【问题讨论】:

  • 如果您想要严格的时间测量,最好删除 WriteLine() 语句。
  • @Henk:不太关心“严肃”的测量。具体数字并不重要,只是它变得更低;)

标签: c# algorithm optimization np-complete set-cover


【解决方案1】:

我没有深入探讨您的代码/算法的细节,但我会用一些理论来为您提供建议。正如 henk 评论的那样,为了执行“良好”的基准测试,您必须删除所有不需要的代码并在发布模式下运行您的程序,并从命令行进行全面优化。

然后,请记住您正在运行托管代码:C#(和 Java)是为互操作性而设计的,而不是为了性能而设计的,尽管它们仍然是很好的平台。如果您需要性能,您应该尝试在 C++ 中重新实现您的代码,或者,如果您愿意,可以尝试将 Mono 与 AOT(提前编译器)一起使用:它会大大提高性能

 mono --aot=full YourProgram.exe

现在详细了解基准和最优性:您是否将自己的结果与其他人进行了比较?您是否在同一硬件上运行了其他集合覆盖算法,或者您能否将您的硬件与运行相同算法的其他硬件进行比较?

而且...您的解决方案与最优解的距离有多近?你能给[你自己]一个估计吗?关键在于 LINQ,我讨厌它,因为您为了代码的简单性而失去了对代码的控制。 LINQ 的复杂性是什么?如果每个 LINQ 是 O(n),你的算法是 O(n^3) 但我可能建议你替换

remaining.Any()

remaining.Count > 0

获得一定程度的复杂性。

我的只是建议,希望对你有所帮助

【讨论】:

  • remaining.Any() 仅使用 1250 个元素运行 15 次...我不认为这是一个瓶颈,我怀疑它是 O(1) 无论如何,不​​是吗?你的建议很好,但不知道那个单声道的东西。我不知道 set-cover 的任何其他 C# 实现,或者我会使用它们。将它移植到 C++ 或其他东西不是问题,但我需要首先正确理解算法背后的理论。我所说的“最佳”是指“产生最少数量的集合”,而不是在效率方面。我不知道最佳 sol'n 是什么,因为它是 NP 完全的,并且需要很长时间才能解决。
  • 我很确定最昂贵的线路是sets.MaxBy(s =&gt; s.Intersect(remaining).Count()),因为它必须计算 2 组的交集大约 5000 次。我不确定Intersect 方法的成本到底有多高……我认为至少是O(n log n)
  • 是的,我阅读了 Any() 的代码,实际上它是 O(1)。我还在 Wikipedia 上发现贪心算法通常工作得很好,是多项式的,所以我现在将重点放在运行时环境:)
  • 总有一天 :) 我脑海中浮现出一百万个想法……时不时地,我喜欢解决我认为没有实际完成项目的最大障碍。 . 我希望如果我事先解决所有棘手的问题,它会一帆风顺......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 2017-07-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多