0

我正在做一个模拟退火算法来优化给定的学生和项目分配。

这是来自 Wikipedia 的与语言无关的伪代码:

s ← s0; e ← E(s)                                // Initial state, energy.
sbest ← s; ebest ← e                            // Initial "best" solution
k ← 0                                           // Energy evaluation count.
while k < kmax and e > emax                     // While time left & not good enough:
  snew ← neighbour(s)                           // Pick some neighbour.
  enew ← E(snew)                                // Compute its energy.
  if enew < ebest then                          // Is this a new best?
    sbest ← snew; ebest ← enew                  // Save 'new neighbour' to 'best found'.
  if P(e, enew, temp(k/kmax)) > random() then   // Should we move to it?
    s ← snew; e ← enew                          // Yes, change state.
  k ← k + 1                                     // One more evaluation done
return sbest                                    // Return the best solution found.

以下是该技术的改编。我的主管说这个想法在理论上是好的。

首先,我从整组随机分配中提取一些分配(即整个学生词典及其分配的项目,包括项目的排名),将其复制并传递给我的函数。我们称之为分配aOld(它是一个字典)。aOld有一个与之相关的权重,称为wOld。权重如下所述。

该函数执行以下操作:

  • 让这个分配,aOld成为best_node
  • 从所有学生中,选择随机数量的学生并粘贴在列表中
  • 剥离(DEALLOCATE)他们的项目++反映项目的变化(allocated参数现在False)和讲师(如果他们的一个或多个项目不再分配,则释放空位)
  • 随机化该列表
  • 再次尝试分配(REALLOCATE)该列表项目中的每个人
  • 计算权重(将排名相加,排名 1 = 1,排名 2 = 2...并且没有项目排名 = 101)
  • 对于这个新的分配aNew,如果权重wNew小于wOld我一开始选择的分配权重,那么这就是best_node(由上面的Simulated Annealing算法定义)。将算法应用于aNew并继续。
  • 如果wOld < wNew,则再次应用该算法aOld并继续。

分配/数据点表示为“节点”,使得node = (weight, allocation_dict, projects_dict, lecturers_dict)

现在,我只能执行这个算法一次,但我需要尝试一个数字 N(kmax在 Wikipedia 片段中表示)并确保我总是随身携带,previousnodebest_node.

为了不修改我原来的字典(我可能想重置),我做了字典的浅拷贝。从我在文档中阅读的内容来看,它似乎只复制引用,并且由于我的字典包含对象,因此更改复制的字典最终会更改对象。所以我尝试使用copy.deepcopy()。这些字典引用了已经用 SQLA 映射的对象。


Questions:

我已经为所面临的问题提供了一些解决方案,但由于我使用 Python 的超绿色能力,它们对我来说听起来都相当神秘。

  1. Deepcopy 不能很好地与 SQLA 配合使用。有人告诉我,ORM 对象上的深拷贝可能存在阻止它按预期工作的问题。显然,我最好“构建复制构造函数,即 def copy(self): return FooBar(....)”。有人可以解释一下这是什么意思吗?

  2. 我检查并发现deepcopy存在问题,因为 SQLAlchemy 在您的对象上放置了额外的信息,即_sa_instance_state属性,我不希望在副本中但对于对象来说是必需的。有人告诉我:“有一些方法可以手动清除旧的_sa_instance_state并在对象上放置一个新的,但最直接的方法是创建一个新对象__init__()并设置重要的属性,而不是做一个完整的深拷贝。” 这到底是什么意思呢?我是否创建了一个新的、未映射的类,类似于旧的映射类?

  3. 另一种解决方案是我必须“__deepcopy__()在您的对象上实现并确保设置了新的 _sa_instance_state,sqlalchemy.orm.attributes 中有一些函数可以帮助解决这个问题。” 再一次,这超出了我的范围,所以有人可以解释一下这是什么意思吗?

  4. 一个更普遍的问题:鉴于上述信息,是否有任何关于如何维护信息/状态的建议best_node(必须始终通过我的 while 循环持续存在)和previous_node,如果我的实际对象(由字典引用,因此节点) 是否由于发生解除分配/重新分配而发生变化?也就是说,不使用副本?

4

2 回答 2

2

我有另一个可能的解决方案:使用事务。这可能仍然不是最好的解决方案,但实施它应该更快。

首先像这样创建您的会话:

# transactional session
Session = sessionmaker(transactional=True)
sess = Session()

这样,它将是事务性的。事务的工作方式是sess.commit()使您的更改永久化,同时sess.rollback()将它们还原。

在模拟退火的情况下,您希望在找到新的最佳解决方案时提交。在以后的任何时候,您都可以调用 rollback() 将状态恢复到该位置。

于 2010-06-07T05:49:29.963 回答
0

你不想像那样复制 sqlalchemy 对象。您可以实现自己的方法来轻松制作副本,但这可能不是您想要的。您不希望数据库中的学生和项目的副本是吗?所以不要复制这些数据。

所以你有一个字典来保存你的分配。在此过程中,您永远不应修改 SQLAlchemy 对象。所有可以修改的信息都应该存储在这些字典中。如果您需要修改对象以考虑到这一点,请在最后复制数据。

于 2010-06-06T07:24:41.920 回答