递归地执行多个自回避行走
Executing multiple self-avoiding walks, recursively
我有一个 3D 简单立方晶格,我在我的代码中称之为 Grid
,周期性边界条件大小为 20x20x20(数字是任意的)。我想做的是种植多条聚合度为N的聚合物链(有N个节点的图),不重叠,自我回避。
目前,我可以递归地种植一种聚合物。这是我的代码
const std::vector <int> ex{1,0,0}, nex{-1,0,0}, ey{0,1,0}, ney{0,-1,0}, ez{0,0,1}, nez{0,0,-1}; // unit directions
const std::vector <std::vector <int>> drns = {ex, nex, ey, ney, ez, nez}; // vector of unit directions
void Grid::plant_polymer(int DoP, std::vector <std::vector <int>>* loc_list){
// loc_list is the list of places the polymer has been
// for this function, I provide a starting point
if (DoP == 0){
Polymer p (loc_list);
this->polymer_chains.push_back(p); // polymer_chains is the attribute which holds all polymer chains in the grid
return; // once the degree of polymerization hits zero, you are done
};
// until then
// increment final vector in loc_list in a unit direction
std::vector <int> next(3,0);
for (auto v: drns){
next = add_vectors(&((*loc_list).at((*loc_list).size()-1)), &v);
impose_pbc(&next, this->x_len, this->y_len, this->z_len);
if (this->occupied[next]==0){ // occupied is a map which takes in a location, and spits out if it is occupied (1) or not (0)
// occupied is an attribute of the class Grid
dop--; // decrease dop now that a monomer unit has been added
(*loc_list).push_back(next); // add monomer to list
this->occupied[next] == 1;
return plant_polymer(DoP, loc_list);
}
}
std::cout << "no solution found for the self-avoiding random walk...";
return;
这不是一个通用的解决方案。我正在为聚合物提供种子,而且,我只种植一种聚合物。我想让它能够在不指定种子的情况下种植多种聚合物。每次我想添加聚合物时,是否可以递归地寻找起始位置,然后构建聚合物,同时确保它不与系统中已有的其他聚合物重叠?如果您有任何建议,我们将不胜感激。
自回避行走至少从 1960 年代就开始研究了,并且有大量关于它们的文献。还好你遇到的问题属于最简单的(walks长度固定在一个比较小的值)。
1
您应该注意的第一件事是您的问题过于宽泛,无法找到唯一的答案。也就是说,如果你在系统中种植许多聚合物,你将获得的结果取决于聚合物种植和生长的动态。主要有两种情况。要么你种下一些种子,然后开始从它们中生长出聚合物,要么你在“其他地方”生长每一种聚合物,然后尝试在系统中的随机位置,一个接一个地种植它们,保持自我回避的条件。这两种方法将导致聚合物的统计分布不同,除了更详细地指定系统动力学之外,您无能为力。
我相信第二种方法更容易一些,因为它可以让您免于决定如果某些聚合物不能增长到所需长度(重新启动模拟?)该怎么办,所以让我们只关注它。
一般算法可能如下所示:
- 将
maximum_number_of_attempts
设置为相当大的值,但不要太大,比如一百万
- 将
required_number_of_polymers
设置为所需的值
- 将
number_of_attempts
设为0
- 将
number_of_planted_polymers
设为0
- 同时
number_of_attempts
< maximum_number_of_attempts
和 number_of_planted_polymers
< required_number_of_polymers
- 增加
number_of_attempts
1
- 生成下一个随机聚合物
- 在系统中选择一个随机位置(格点)
- 检查是否可以在没有交叉点的位置种植聚合物
- 当且仅当是,
- 接受聚合物(将其添加到聚合物列表;更新已占用晶格节点列表)
- 增加
number_of_planted_polymers
1
为了加快速度,您可以只从未占用的站点中选择初始位置(例如,在 while
循环中)。另一个想法是尝试使用聚合物,在第一次电镀失败时,在不同位置多次(但不要太多,你需要试验)。
2
现在下一步:如何生成自我回避随机游走。你的代码几乎没问题,除了一些误解。
在函数 void Grid::plant_polymer
中有一个严重错误:它总是在 space 中以 完全相同的顺序 搜索可能的聚合物形状。换句话说,它是确定性的。对于 Monte Carlo 方法,这听起来像是一场灾难。您可能会做的一件事是随机调整方向。
auto directions = drns;
std::shuffle(begin(directions), end(directions), random_generator); // c++17
for (const auto & v: directions)
{
...
为此,您需要一个已经定义和初始化的随机数生成器,例如
std::random_device rd;
std::mt19937 random_generator(rd());
更早的地方,在程序中只有一次。
如果您不使用 C++17,请使用 std::random_shuffle
而不是 std::shuffle
,但请注意,前者已被贬低,请参阅 https://en.cppreference.com/w/cpp/algorithm/random_shuffle 了解原因的讨论.
附带说明一下,在询问与特定软件相关的问题时,请尝试提供 Minimal, reproducible example。仅根据阅读代码来回答问题几乎总是更耗时,而且答案往往更加粗略和不准确。
我有一个 3D 简单立方晶格,我在我的代码中称之为 Grid
,周期性边界条件大小为 20x20x20(数字是任意的)。我想做的是种植多条聚合度为N的聚合物链(有N个节点的图),不重叠,自我回避。
目前,我可以递归地种植一种聚合物。这是我的代码
const std::vector <int> ex{1,0,0}, nex{-1,0,0}, ey{0,1,0}, ney{0,-1,0}, ez{0,0,1}, nez{0,0,-1}; // unit directions
const std::vector <std::vector <int>> drns = {ex, nex, ey, ney, ez, nez}; // vector of unit directions
void Grid::plant_polymer(int DoP, std::vector <std::vector <int>>* loc_list){
// loc_list is the list of places the polymer has been
// for this function, I provide a starting point
if (DoP == 0){
Polymer p (loc_list);
this->polymer_chains.push_back(p); // polymer_chains is the attribute which holds all polymer chains in the grid
return; // once the degree of polymerization hits zero, you are done
};
// until then
// increment final vector in loc_list in a unit direction
std::vector <int> next(3,0);
for (auto v: drns){
next = add_vectors(&((*loc_list).at((*loc_list).size()-1)), &v);
impose_pbc(&next, this->x_len, this->y_len, this->z_len);
if (this->occupied[next]==0){ // occupied is a map which takes in a location, and spits out if it is occupied (1) or not (0)
// occupied is an attribute of the class Grid
dop--; // decrease dop now that a monomer unit has been added
(*loc_list).push_back(next); // add monomer to list
this->occupied[next] == 1;
return plant_polymer(DoP, loc_list);
}
}
std::cout << "no solution found for the self-avoiding random walk...";
return;
这不是一个通用的解决方案。我正在为聚合物提供种子,而且,我只种植一种聚合物。我想让它能够在不指定种子的情况下种植多种聚合物。每次我想添加聚合物时,是否可以递归地寻找起始位置,然后构建聚合物,同时确保它不与系统中已有的其他聚合物重叠?如果您有任何建议,我们将不胜感激。
自回避行走至少从 1960 年代就开始研究了,并且有大量关于它们的文献。还好你遇到的问题属于最简单的(walks长度固定在一个比较小的值)。
1
您应该注意的第一件事是您的问题过于宽泛,无法找到唯一的答案。也就是说,如果你在系统中种植许多聚合物,你将获得的结果取决于聚合物种植和生长的动态。主要有两种情况。要么你种下一些种子,然后开始从它们中生长出聚合物,要么你在“其他地方”生长每一种聚合物,然后尝试在系统中的随机位置,一个接一个地种植它们,保持自我回避的条件。这两种方法将导致聚合物的统计分布不同,除了更详细地指定系统动力学之外,您无能为力。
我相信第二种方法更容易一些,因为它可以让您免于决定如果某些聚合物不能增长到所需长度(重新启动模拟?)该怎么办,所以让我们只关注它。
一般算法可能如下所示:
- 将
maximum_number_of_attempts
设置为相当大的值,但不要太大,比如一百万 - 将
required_number_of_polymers
设置为所需的值 - 将
number_of_attempts
设为0 - 将
number_of_planted_polymers
设为0 - 同时
number_of_attempts
<maximum_number_of_attempts
和number_of_planted_polymers
<required_number_of_polymers
- 增加
number_of_attempts
1 - 生成下一个随机聚合物
- 在系统中选择一个随机位置(格点)
- 检查是否可以在没有交叉点的位置种植聚合物
- 当且仅当是,
- 接受聚合物(将其添加到聚合物列表;更新已占用晶格节点列表)
- 增加
number_of_planted_polymers
1
- 增加
为了加快速度,您可以只从未占用的站点中选择初始位置(例如,在 while
循环中)。另一个想法是尝试使用聚合物,在第一次电镀失败时,在不同位置多次(但不要太多,你需要试验)。
2
现在下一步:如何生成自我回避随机游走。你的代码几乎没问题,除了一些误解。
在函数 void Grid::plant_polymer
中有一个严重错误:它总是在 space 中以 完全相同的顺序 搜索可能的聚合物形状。换句话说,它是确定性的。对于 Monte Carlo 方法,这听起来像是一场灾难。您可能会做的一件事是随机调整方向。
auto directions = drns;
std::shuffle(begin(directions), end(directions), random_generator); // c++17
for (const auto & v: directions)
{
...
为此,您需要一个已经定义和初始化的随机数生成器,例如
std::random_device rd;
std::mt19937 random_generator(rd());
更早的地方,在程序中只有一次。
如果您不使用 C++17,请使用 std::random_shuffle
而不是 std::shuffle
,但请注意,前者已被贬低,请参阅 https://en.cppreference.com/w/cpp/algorithm/random_shuffle 了解原因的讨论.
附带说明一下,在询问与特定软件相关的问题时,请尝试提供 Minimal, reproducible example。仅根据阅读代码来回答问题几乎总是更耗时,而且答案往往更加粗略和不准确。