【问题标题】:Timetable permutation generation and memory optimisation时间表排列生成和内存优化
【发布时间】:2018-07-19 06:09:27
【问题描述】:

我正在创建一个程序来优化我的大学为学生的时间表提供的课程。

我将数据解析为以下结构/类:

public struct Subject
{  
    public int Classes {...}
    public int Hours { ... }
    public string Name { get; set; }
    public string Code { get; set; }
    public string Base_Code;
    public Campus Campus { get; set; }
    public Semester Semester;  
}

public struct Timeslot
{
    public string ClassType; //code for type: i.e. L for Lecture, W for Workshop
    public string Description;
    public DayOfWeek Day;
    public DateTime Start, Finish;
    public List<Tuple<string, string>> weekSpans;
    public string Location;
}

public struct Class
{
    public Subject subject { get; set; }
    public Timeslot time { get; set; }
}

在我本学期的 n 个科目中,我总共有 m 个必修课要参加(讲座 1、2、3,研讨会等)。我将这些存储在“类”类型列表的列表中。

例如:

第 1 讲(主题 1)

  • 周一上午 9 点上课
  • 周一下午 1 点上课

研讨会 1(主题 2)

  • 周一上午 9 点上课
  • 周二下午 2 点上课

我目前正在使用 Eric Lippert 的笛卡尔积方法来生成一个包含所有 n 类组合的IEnumerable&lt;IEnumerable&lt;Class&gt;&gt;。

static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) 
{ 
  // base case: 
  IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() }; 
  foreach(var sequence in sequences) 
  { 
    var s = sequence; // don't close over the loop variable 
    // recursive case: use SelectMany to build the new product out of the old one 
    result = 
      from seq in result 
      from item in s 
      select seq.Concat(new[] {item}); 
    } 
  return result; 
}

但是,当我需要从每个 IEnumerable 收集必要的信息以确定哪个时间表符合设定的标准(较短的天数、大部分休息日等)时,就会出现我的问题。尝试遍历所有内容并将它们存储在我的 TimeTable 容器类中确实对存储初始排列所需的内存之上的内存造成负担。

public class TimeTable
{
    public IEnumerable<Class> _classes;
    public DateTime MinStart { get; private set; }
    public DateTime MaxFinish { get; private set; }
    public int DaysOff { get; private set; }
    public TimeTable(IEnumerable<Class> Classes) { ... }
}

事实上,我的内存猛增到 2GB,然后崩溃。我曾想过通过丢弃包含冲突的时间表来优化排列过程,我知道该怎么做,但我无法弄清楚我可以将代码放入 CartesianProduct 算法的哪个位置,这样它就不会保存在内存中。我还考虑为每个班级分配一个 ID,然后计算排列,但这并没有优化按用户偏好对时间表进行排序的需要。

这是我的第一个个人项目,我必须处理组合学和内存优化,因此非常感谢任何有关如何优化此优化过程的建议和帮助(哈哈!)。

【问题讨论】:

  • 这些类型都不应该是结构。它们是可变的,对于结构来说太大了,并且在逻辑上不代表单个值。它们都应该是类。
  • 您只是无法将所有这些都存储在内存中。您需要弄清楚如何在任何时候只在内存中拥有恒定数量的排列时找到您想要的信息。排列增长太快,你无法做任何其他事情。
  • 除了最小的调度问题外,我们不想生成所有可能的排列。你明白为什么会这样。通常这些问题会受到混合整数规划、约束规划或某种形式的(元)启发式的攻击。

标签: c# optimization memory-management cartesian-product


【解决方案1】:

您不需要将所有元素组合存储在一个大的超级集合中,以便对其进行迭代。我曾经写过一个java solution,应该很容易翻译成c#,但可能不完全适合,但主要思想,我可以在这里布局。

你有一些必修课要结合,假设a1,a2,a3; b1, b2; c1、c2、c3、c4。

学生必须选择a之一,b之一,c之一。

可能的组合数量为a.size * b.size * c.size = 3*2*4 = 24。

因此,您无需生成组合并对其进行迭代,而是使用 int i 在数字 0 到 23 上循环或迭代,然后选择一个课程组合,如果您已经构建了组合,它将位于索引 i 处。

它是如何完成的?好吧,假设数字 i 是 17。i % (c.size) = 17%4= 1。这将是 c, c2 的第二个 (0, 1, 2, 3) 元素。

现在我们将 i' (i % c.size) 除以 c.size 得到 17/4=4。 4 % b.size(2) = 0 所以对于 b 我们得到 index=0 即 b1。

i' 除以 b.size,即 i"=2。在索引 2 (% (3 = a.size)) 处,我们找到 a3。对于最后一个元素,我们不需要模数,而是另一方面,它不会造成伤害并允许统一的流程。

尝试用少量数字来说服自己,所有组合都以系统的顺序准确可靠地发生一次。

如果两门课程发生冲突,就选下一门。

【讨论】:

  • 我看到了它背后的逻辑,但这是否让我能够对没有冲突的排列进行排序?
  • 由于没有状态来保存元素的集合,不直接:每个组合只是生成的JIT,但可以过滤生成并将结果存储在自己的集合中。像在 CartesianIteratorTest 中一样迭代,for (List &lt;Object&gt; lo: ci) if filter (lo) myList.add (lo); 和 filter.lo 可以是类似 (notOverlapping (lo(0).time , lo(1).time)) 的谓词,假设 lo(0) 和 lo (1) 是诅咒并且curses.time 是间隔,您可以为其定义一个方法(notOverlapping (t1, t2)。
  • 由于要比较的元素多于2个,所以比较的不是2个而是每对元素。在伪代码中:for a
  • 我认为这种方法有效。我面临的一个问题是,当 List 中只有 1 个项目时,比如一周内只有 1 次的讲座。如果这是在 List> 的顶部,那么它将导致执行 x % 1,每次都产生 0。我必须按列表计数降序排序才能正常工作。
  • @Trontor:我猜这可能是一个实现问题。我在stackoverflow.com/a/10083452/312172 的参考实现,如果我将其中一个列表更改为仅包含一个元素,那么它应该可以正常工作。但是你的课程不能互相冲突,而且每门课程都会与这整周的课程在时间重叠上发生冲突——这是问题所在吗?如果可以将主题延长到下学期,也许您需要在每个列表中选择一门空课程?
猜你喜欢
  • 1970-01-01
  • 2018-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多