随机扩散搜索(Stochastic Diffusion Search,SDS)算法由 J.Bishop 于1989年提出,并由 Bishop 和 S.Nasuto 积极开发。与其他群体算法相比,该算法的一个显著特征是其深刻的数学合理性。SDS最初是为离散优化而开发的。2011年,提出了对其进行全局连续优化的修改。
餐厅游戏
一群代表正在一个陌生的城市参加一个长会议,每天晚上,他们都面临着选择餐馆吃饭的问题。这座城市有许多餐馆,提供各种各样的菜肴。小组的目标是找到最好的一个,让每个代表都能享受一顿饭。然而,对所有可能的餐厅和菜肴组合进行详尽的搜索会花费太多时间。为了解决这个问题,代表们采用随机扩散搜索。
每位代表都充当一名代理人,对城市中最好的餐厅的选择进行猜测。每天晚上,代表们通过参观餐厅并从餐厅的菜肴中随机选择一种来测试他们的假设。第二天早上,在早餐时,每一位对前一晚的晚餐不满意的代表都会请他的一位同事分享他们对晚餐的印象。如果同事的印象是正面的,代表也会选择那家餐厅。否则,代表将从该城市的可用餐厅列表中随机选择另一个位置。
因此,得益于这一策略,大量代表迅速形成,聚集在城市中最好的餐厅周围。
这个游戏有几个有趣的特点。在完全没有外部控制和管理的情况下,一群代表进行沟通,以解决无法单独快速解决的问题。如果当前餐厅的服务或菜单显著下降或业务关闭,则代表有效地转到下一家最好的餐厅。主要要求是餐厅、菜单和个人菜肴的可比性。每个代理自己决定他们的体验是否良好。
在评估城市所有地点的所有菜肴之前,代表们将在一家高质量的餐厅度过许多晚上。
批评者指出,代表们可能有不同的食物偏好,因此一名代表可能会找到一家他们喜欢所有菜肴的餐厅,但这可能无法满足其他人的需求。如果只有一名或少数代表永久留在这样的餐厅,其余代表将继续照常行事,最终大多数代表仍会聚集在最好的餐厅。然而,在极端情况下,所有代理人可能会发现自己独自用餐。即使只有一家优秀的餐厅能让大多数代表满意,这家餐厅将永远找不到,因为所有代表都对他们目前的选择感到满意,不会寻找新的位置。
我们的实现对搜索策略的逻辑进行了微小的更改,这要归功于即使没有经验更好的代表,代表也会继续搜索餐厅,因此对餐厅的搜索不会停止,这与规范版本不同,在规范版本中,改变当前对餐厅的看法与前一个观点相比很重要。如果大多数代表的意见没有好转,那么他们将继续去同一家餐厅,这意味着陷入局部的极值。
金矿游戏
一群由经验丰富的矿工组成的朋友了解在山脉的山丘上开采黄金的可能性。然而,他们没有关于最富有的地方到底在哪里的信息。在他们的地图上,山脉被划分为几个单独的山丘,每个山丘都包含一组需要开采的地层。随着时间的推移,发现黄金的概率与其财富成正比。
为了最大限度地增加他们的集体财富,矿工们应该确定这座山上有最丰富的金矿层,以便最大数量的矿工能够在那里开采。但是,这些信息无法提前获得。为了解决这个问题,矿工们决定使用简单的随机扩散搜索。
采矿过程开始时,每个矿工被随机分配一座小山进行采矿(自定义小山假设)。每天,每个矿工都会随机选择他们山上的一个地层进行开采。
每天结束时,矿工感到高兴的概率与他或她发现的黄金量(适应度函数的值)成正比。
晚上,工作结束后,矿工们聚在一起。一个不开心的矿工随机选择另一个矿工与之交谈。如果被选中的矿工满意,他会很高兴地告诉他的同事他正在开采的山的名字。因此,两位矿工都支持山丘假说。如果被选中的矿工不高兴,他什么也不说,而原来的矿工再次被迫选择一个新的假设——随机确定他第二天要开采的山。
在SDS(自组织动态系统)的上下文中,代理充当矿工。活跃的代理人是“快乐的矿工”,而不活跃的代理人则是“不快乐的矿工“。每个代理人的假说代表了矿工的“山丘假说”。这一过程与SDS同构,允许矿工自然地自我组织,并迅速聚集在黄金浓度高的山脊上。
矿工的幸福感可以用概率来衡量,也可以用绝对布尔值来表示,假设每个矿工在每天结束时都是快乐或不快乐的。如果黄金被建模为一种随着时间的推移而减少的有限资源,那么搜索就会变得非常自适应,矿工会转移到黄金最多的地方。
尽管“快乐”一词是主观的,就像饮食习惯一样,但在这种情况下,它是在客观意义上使用的。所有矿工都使用相同的过程:当他们聚在一起可能分享他们正在开采的山丘的信息时,他们一天中发现的黄金数量决定了他们在一天结束时宣布自己“幸运”的可能性。
我们不会把矿工分为“快乐的”和“不快乐的”。与餐厅游戏概念的情况一样,这允许增加代理商搜索新的未经探索的地方的活动。
为了使算法形式化,我们将使用“候选者”的概念,它相当于餐馆游戏中的代表和金矿游戏中的矿工。候选人是一名搜索代理。为了更简单地理解餐馆或山丘的本质,我们可以想象一个有两个坐标的空间,尽管实际上可以使用无限数量的坐标来解决多维问题。在图1中,名称C1、C2、C3显示了存储餐厅编号的候选者(搜索字段中的相应空格)。在餐厅信息的扩散交换过程中,如果对话者的适应度函数值较高,候选人会从随机选择的会议参与者那里借用餐厅编号。每个优化参数(搜索空间坐标)的范围除以算法的外部参数中指定的餐厅数量。例如,如果在算法参数中指定了100家餐厅的数量,这意味着每个坐标的范围将被划分为100个部分。
每家餐厅的菜单上都有一份菜肴清单。每个菜单菜都是一个餐厅内搜索空间的特定坐标。该算法实现了一种方案,即候选人只品尝一道随机选择的餐厅菜肴。
现在是时候看看SDS(随机扩散搜索)代码,从算法的核心和灵魂开始了——代理,也就是候选,它可以用S_candidate结构来描述。它包含以下字段:
1. raddr: 包含餐厅地址的数组。每个数组元素表示一个餐厅的地址。
2. raddrPrev: 包含以前餐馆地址的数组。每个数组元素表示一个餐厅的地址。
3. c: 包含坐标(菜肴)的数组。数组的每个元素表示一个菜肴的坐标。
4. cPrev: 包含先前坐标(菜肴)的数组。数组的每个元素表示一个菜肴的坐标。
5. f: 代理当前状态的适应度函数值。
6. fPrev: 代理先前状态的适应度函数值。
S_Candidate 结构具有 Init 方法,该方法初始化所有“coords”大小的数组(坐标的数量-要优化的参数),并将f和fPrev的初始值设置为-DBL_MAX,因为在第一次迭代时没有也没有人可以比较候选者的经验。
//——————————————————————————————————————————————————————————————————————————————
struct S_Candidate
{
void Init (int coords)
{
ArrayResize (c, coords);
ArrayResize (cPrev, coords);
ArrayResize (raddr, coords);
ArrayResize (raddrPrev, coords);
f = -DBL_MAX;
fPrev = -DBL_MAX;
}
int raddr []; //restaurant address
int raddrPrev []; //previous restaurant address
double c []; //coordinates (dishes)
double cPrev []; //previous coordinates (dishes)
double f; //fitness
double fPrev; //previous fitness
};
//——————————————————————————————————————————————————————————————————————————————
声明包含以下内容的SDS算法类:
类字段:
- cB: 包含最佳坐标的数组
- fB: 最佳坐标的适应度函数值
- cands: 用于查找最佳坐标的候选数组
- rangeMax: 包含每个坐标的最大值的数组
- rangeMin: 包含每个坐标的最小值的数组
- rangeStep: 包含每个坐标的搜索步骤的数组
类方法:
- Init: 算法参数的初始化,如坐标数量、群体大小、餐厅数量和选择餐厅的概率
- Moving: 执行算法步骤,将候选者移动到新坐标
- Revision: 执行修订步骤,更新最佳坐标和适应度函数
- SeInDiSp: 用给定步骤计算给定范围内新坐标的方法
- RNDfromCI: 在给定区间内生成随机数的方法
其他类字段:
- coords: 坐标数
- populationSize: 群体大小
- restNumb: 餐厅数量
- probabRest: 选择餐馆的概率
- restSpace: 餐厅空间
- revision: 指示需要修订的标志
//——————————————————————————————————————————————————————————————————————————————
class C_AO_SDS
{
//----------------------------------------------------------------------------
public: double cB []; //best coordinates
public: double fB; //FF of the best coordinates
public: S_Candidate cands []; //candidates
public: double rangeMax []; //maximum search range
public: double rangeMin []; //manimum search range
public: double rangeStep []; //step search
public: void Init (const int coordinatesNumberP, //coordinates number
const int populationSizeP, //population size
const int restaurantsNumberP, //restaurants number
const double probabRestP); //probability restaurant choosing
public: void Moving ();
public: void Revision ();
//----------------------------------------------------------------------------
private: int coords; //coordinates number
private: int populationSize; //population size
private: int restNumb; //restaurants number
private: double probabRest; //probability restaurant choosing
private: double restSpace []; //restaurants space
private: bool revision;
private: double SeInDiSp (double In, double InMin, double InMax, double Step);
private: double RNDfromCI (double min, double max);
};
//——————————————————————————————————————————————————————————————————————————————
SDS算法的初始化方法首先为一些变量和数组设置初始值。
该方法的输入参数为:
- coordinatesNumberP: 坐标数(搜索空间维度)
- populationSizeP: 群体大小(候选人数量)
- restaurantsNumberP: 餐馆数量
- probabRestP: 选择餐馆的概率
首先,使用MathSrand函数重置随机数生成器,并将当前值传递给它(以微秒为单位)。然后,fB 和 revision 变量分别初始化为初始值——DBL_MAX和“false”。
接下来,坐标NumberP和populationSizeP输入的值被分配给坐标和populationSize变量。
restNumb和probabRest变量是用restaurantsNumberP和probabRestP值初始化的。
“coords”大小的restSpace数组是使用ArrayResize函数创建的。
然后使用ArrayResize函数创建populationSize大小的“cands”数组。在循环中,通过使用“coords”参数调用Init方法来初始化“cands”数组的每个元素。
“coords”大小的rangeMax、rangeMin、rangeStep和cB数组也使用ArrayResize函数创建。
//——————————————————————————————————————————————————————————————————————————————
void C_AO_SDS::Init (const int coordinatesNumberP, //coordinates number
const int populationSizeP, //population size
const int restaurantsNumberP, //restaurants number
const double probabRestP) //probability restaurant choosing
{
MathSrand ((int)GetMicrosecondCount ()); // reset of the generator
fB = -DBL_MAX;
revision = false;
coords = coordinatesNumberP;
populationSize = populationSizeP;
restNumb = restaurantsNumberP;
probabRest = probabRestP;
ArrayResize (restSpace, coords);
ArrayResize (cands, populationSize);
for (int i = 0; i < populationSize; i++) cands [i].Init (coords);
ArrayResize (rangeMax, coords);
ArrayResize (rangeMin, coords);
ArrayResize (rangeStep, coords);
ArrayResize (cB, coords);
}