Dingo Optimization Algorithm Modification (DOAm)
Contents
Introduction
Let's ask ourselves: are there population-based metaheuristic algorithms that, with a limited number of iterations — 10,000, which is our case — are capable of delivering a 100% results? Its power should be pretty overwhelming. In fact, sometimes I doubt the feasibility of discovering such a gem in the optimization trade.
Similar algorithms for such a purpose may exist, and today we will get acquainted with one of the unique optimization methods, or rather, a modified version of the algorithm I have already described in the previous article of the Dingo Optimization Algorithm, which brings us closer to solving such a difficult task.
Implementation of the algorithm
While working on the Dingo Optimization Algorithm, I became interested in the principle of modeling dingo hunting, which has several strategies. We got acquainted with this algorithm and analyzed in detail the principles of its operation. The algorithm's test yielded average results; it did not make it into the ranking table, as it was still 20% short of the entry threshold. It would seem that we can put it on the shelf and move on. However, I decided to try experimenting with the formulas, and this is what I ended up with:
Group attack formula (Equation 2), original version:
x⃗ᵢ(t+1) = β₁ · [Σₖ₌₁ⁿᵃ φ⃗ₖ(t)]/nₐ - x⃗*(t)
now looks like this:
a[agentIdx].c[c] = cB[c] + beta1 * sumAttack;
As you can see, the sign has been changed - instead of subtracting the best solution, we use addition. Also, the method for calculating sumAttack is more complicated - it is divided by (na * coords), instead of just "na".
The survival threshold in the original version is survival[r] <= 0.3, let's change it to survival[r] <= 0.01. We are sharply lowering the threshold, which means that we will only apply survival strategies to the "weakest" agents.
The original scavenging formula (Equation 4):
x⃗ᵢ(t+1) = ½ · e^β₂ · |x⃗ᵣ₁(t) - (-1)^σ · x⃗ᵢ(t)|
New version:
a[agentIdx].c[c] = (MathExp(beta2) * a[r1].c[c] - sign * a[agentIdx].c[c]) / 2.0;
Now the module (absolute value) is removed allowing for negative coordinates. In addition, the modified algorithm averages the positional difference across all coordinates simultaneously rather than coordinate by coordinate.
Why might these changes improve the algorithm's performance? Change of sign in group attack can create a movement "towards a better solution, taking into account the positions of others," rather than movement away from the best solution, as in the authors’ version. Lowering the survival threshold allows most agents to use basic hunting strategies, using survival only in extreme cases. The absence of a module in the scavenging formula allows for freer movement in the search space.

Figure 1. DOAm_dingo strategy modifications
The illustration above shows the key differences between the modified version and the original one described by the algorithm authors: in a group attack, the movement is carried out towards a leader instead of moving from it, the survival threshold is lowered down to 0.01, now scavenging is done without a module in the formula and averaging is performed by (na × coords) instead of just (na). Modifications are highlighted in red for clarity.
Now let's move on to describing the algorithm code and see what we have got. The C_AO_DOAm_dingo class is a specialized version of the C_AO base class. It is an optimization algorithm based on modifying the behavior of dingoes, so the Dingo Optimization Algorithm M class inherits from C_AO, which implies that it has the basic functionality. The following default values are set for the key parameters:
- popSize (population size, i.e. number of dingoes): 50.
- P (probability of choosing between hunting and scavenging): 0.5.
- Q (probability of choosing between group attack and chase): 0.7.
- Init — initial launch of the algorithm, including the initialization of agents (dingoes) and their parameters, using the provided ranges and change steps.
- Moving — the main phase of the algorithm, where agents move and interact in accordance with the rules of the algorithm.
- Revision — revising or evaluating the state of the algorithm after a certain stage or epoch.
- P — probability that a dingo will hunt or scavenge.
- Q — probability that a dingo will participate in a group attack or chase.
- beta1, beta2 - used to generate random vectors or steps in the optimization with given ranges of values;
- naIni, naEnd — define the minimum and maximum number of dingoes participating in the attack, which can vary;
- survival — array that stores the "survival" or fitness indicators of each dingo.
- UpdateSurvivalRates — update the survival rates of agents;
- GroupAttack — implement group attack behavior;
- Persecution — implement chase behavior;
- Scavenger — implement the scavenging behavior;
- SurvivalProcedure — procedure related to the survival of agents;
- GetAttackVector — get a vector that determines the attack direction;
- CalculateAttackSum — calculate the total attack "effect".
//———————————————————————————————————————————————————————————————————— class C_AO_DOAm_dingo : public C_AO { public: //---------------------------------------------------------- ~C_AO_DOAm_dingo () { } C_AO_DOAm_dingo () { ao_name = "DOAm"; ao_desc = "Dingo Optimization Algorithm M"; ao_link = "https://www.mql5.com/en/articles/19187"; popSize = 50; // population size (number of dingoes) P = 0.5; // Hunting or Scavenger rate Q = 0.7; // Group attack or chase ArrayResize (params, 3); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "P"; params [1].val = P; params [2].name = "Q"; params [2].val = Q; } void SetParams () { popSize = (int)params [0].val; P = params [1].val; Q = params [2].val; } bool Init (const double &rangeMinP [], // minimum values const double &rangeMaxP [], // maximum values const double &rangeStepP [], // step change const int epochsP = 0); // number of epochs void Moving (); void Revision (); //------------------------------------------------------------------ double P; // hunting or scavenging probability double Q; // group attack or chase probability private: //--------------------------------------------------------- double beta1; // -2 < beta1 < 2 double beta2; // -1 < beta2 < 1 int naIni; // minimum number of attacking dingoes int naEnd; // maximum number of attacking dingoes double survival []; // array of survival indicators // Auxiliary methods void UpdateSurvivalRates (); void GroupAttack (int agentIdx, int na); void Persecution (int agentIdx); void Scavenger (int agentIdx); void SurvivalProcedure (int agentIdx); void GetAttackVector (int na, int &attackVector []); double CalculateAttackSum (int agentIdx, int na); }; //————————————————————————————————————————————————————————————————————
This method is designed to initialize the Dingo Optimization Algorithm M algorithm. It prepares the algorithm for operation by setting the initial conditions for optimization, determining how dingoes will form attack groups, and setting the initial state of their ‘survival’ state.
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_DOAm_dingo::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ // Initialize algorithm parameters naIni = 2; // minimum number of attackers naEnd = popSize / naIni; // maximum number of attackers ArrayResize (survival, popSize); ArrayInitialize (survival, 1.0); return true; } //————————————————————————————————————————————————————————————————————
The Moving method is the main function responsible for executing one step (or iteration) of the algorithm, it simulates the behavior of a pack of dingoes while they search for the optimal solution. The method dynamically controls the behavior of the dingo population, allowing them to explore the solution space using different strategies based on their "survival" and probabilistic decisions. A more detailed description of the main methods was given in the previous article. Here we will look more closely at the methods specific to this implementation.
//———————————————————————————————————————————————————————————————————— //--- Main step of the algorithm void C_AO_DOAm_dingo::Moving () { // Starting initialization of the population if (!revision) { for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; return; } //------------------------------------------------------------------ // Update survival rates UpdateSurvivalRates (); // Update beta parameters for the current iteration //beta1 = -2.0 + 4.0 * u.RNDprobab (); //beta2 = -1.0 + 2.0 * u.RNDprobab (); beta1 = u.RNDfromCI (-2.0, 2.0); beta2 = u.RNDfromCI (-1.0, 1.0); // Main loop for all dingoes for (int r = 0; r < popSize; r++) { // Check the survival rate (Section 2.2.4) //if (survival[r] <= 0.3) if (survival [r] <= 0.01) { // Strategy 4: Survival (Eq. 6) SurvivalProcedure (r); } else { // Choose between hunting and scavenging if (u.RNDprobab () < P) // If hunting { if (u.RNDprobab () < Q) // If group attack { // Strategy 1: Group attack (Eq. 2) int na = naIni + (int)((naEnd - naIni) * u.RNDprobab ()); GroupAttack (r, na); } else // Chase { // Strategy 2: Chase (Eq. 3) Persecution (r); } } else // Scavenging { // Strategy 3: Scavenging (Eq. 4) Scavenger (r); } } } } //————————————————————————————————————————————————————————————————————
The GroupAttack method implements "Strategy 1: Group attack". This strategy is used when a dingo decides to attack prey as part of a group. First, the auxiliary method CalculateAttackSum is called. It takes the current dingo's index (agentIdx) and the number of dingoes participating in the attack (na), and calculates the total strength or direction of the attack by summing the contributions from all participants. The result of this complex operation is stored in the sumAttack variable.
Next, the method updates the position (coordinates) of the current dingo. The method iterates over each 'c' coordinate of the search space and calculates a new value for the dingo's position. The updated formula is as follows:
New coordinate = Best solution coordinate + beta1 * total attack vector
where:
- cB [c] represents the c-th coordinate of the current best solution found in the optimization,
- beta1 — coefficient that regulates the intensity or direction of the group attack impact,
- sumAttack — result obtained in the previous step.
The resulting new coordinate value is then "fitted" within the acceptable limits. The u.SeInDiSp method ensures that the new value remains within the specified minimum (rangeMin) and maximum (rangeMax) boundaries, while taking into account the step (rangeStep) of change.
Ultimately, the GroupAttack method models a coordinated search for an optimal solution by a group of dingoes. It aggregates information from multiple individuals via sumAttack, takes into account the best solution found (cB) and uses a random coefficient beta1 to determine a new, potentially more advantageous, position for the dingo in the search space.
//———————————————————————————————————————————————————————————————————— //--- Strategy 1: Group attack (Eq. 2) void C_AO_DOAm_dingo::GroupAttack (int agentIdx, int na) { // Calculate the total attack vector double sumAttack = CalculateAttackSum (agentIdx, na); // Apply the group attack formula (Eq. 2) for (int c = 0; c < coords; c++) { // v = beta1 * sumatory - theBestVct a [agentIdx].c [c] = cB [c] + beta1 * sumAttack; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
The CalculateAttackSum method is designed to calculate the total attack vector used in "Strategy 1: Group Attack". It determines how a particular dingo (agentIdx) will interact with other dingoes participating in a group attack (na).
First, the method creates a temporary list (attackVector) that will contain the indices of the dingoes directly involved in the attack. The size of this list is determined by the "na" parameter (number of attackers). Then the GetAttackVector helper method is called. This method, based on the current state of the population, selects "na" dingoes that will participate in the attack and writes their indices into the attackVector. A 'sumatory' variable is initialized to zero. Next comes the main loop, iterates over all dingoes involved in the attack: for each attacking dingo, whose index (attackerIdx) is taken from the attackVector.
Inside this loop there is another loop that goes through all coordinates (c) of the search space (loop: from "0" to "coords - 1"). At each coordinate, the difference between the position of the attacking dingo and the position of the dingo, for which the total attack is calculated, is defined. This difference is then divided by the total number of possible attack participants (na) and the total number of coordinates (coords), and added to 'sumatory'.
Finally, the method returns the 'sumatory' value. This value represents the average position difference between the attacking dingoes and the target dingo, adjusted for the number of participants and dimensions. This value will then be used to update the target dingo's position as part of a group attack.
Thus, CalculateAttackSum collects information about the current positions of dingoes participating in a joint attack and outputs a complex value that reflects their total displacement relative to the attacked dingo. This value then directs the movement of the attacked dingo.
//———————————————————————————————————————————————————————————————————— //--- Calculate the sum for a group attack double C_AO_DOAm_dingo::CalculateAttackSum (int agentIdx, int na) { // Create a vector of attacking dingoes int attackVector []; ArrayResize (attackVector, na); GetAttackVector (na, attackVector); // Calculate the average difference in positions double sumatory = 0.0; for (int j = 0; j < na; j++) { int attackerIdx = attackVector [j]; // Calculate the average over all coordinates for (int c = 0; c < coords; c++) { sumatory += (a [attackerIdx].c [c] - a [agentIdx].c [c]) / (na * coords); } } return sumatory; } //————————————————————————————————————————————————————————————————————
The GetAttackVector method is responsible for generating a list of dingoes that will directly participate in the group attack. It creates a set of random, unique indices, each corresponding to one dingo from the entire population. The method first sets the size of the attackVector output array to the given number "na" (the number of attackers). The 'c' counter is also initialized to zero. It will keep track of how many dingoes have already been selected for attack.
The method enters a loop that will continue until "na" dingoes are selected. In each iteration of the loop, a random index (idx) of dingo is generated from the entire available population (from "0" to "popSize - 1"). Before adding the generated index to the attackers' list, a check is performed. The method iterates through the already selected indices (from "0" to "c - 1") of the attackVector array. If the randomly generated index (idx) matches any of the previously chosen ones (i.e. attackVector[i] == idx), the "found" flag is set to 'true'. If the flag remains 'false' (that is, the given index has not yet been selected), then this new, unique index (idx) is added to the attackVector array under the current index of 'c'. After this, the counter 'c' is increased by one.
Essentially, GetAttackVector is responsible for randomly selecting "na" unique dingoes from the entire population so that they can form an attack group. This process ensures that each dingo in the group is different from the others, preventing duplication and providing variety in the attack group.
//———————————————————————————————————————————————————————————————————— //--- Form the attacking dingo vector void C_AO_DOAm_dingo::GetAttackVector (int na, int &attackVector []) { int c = 0; ArrayResize (attackVector, na); while (c < na) { int idx = u.RNDintInRange (0, popSize - 1); // Check that the index is not repeated bool found = false; for (int i = 0; i < c; i++) { if (attackVector [i] == idx) { found = true; break; } } if (!found) { attackVector [c] = idx; c++; } } } //————————————————————————————————————————————————————————————————————
The Persecution method implements "Strategy 2: Chase". The goal of this method is to update the position of one specific dingo (agentIdx) so that it starts chasing. The method first selects a random dingo from the entire population, the index is stored in the r1 variable.
Next comes a cycle that goes through all the dimensions (c) of the space, in which the dingoes are located. For each dimension, the following formula is used to calculate the new position of the dingo (agentIdx): the product of three values is added to the base value (cB [c]), which represents the position of the best dingo: beta1 - a coefficient controlling the degree of chase influence on MathExp, (beta2) - an exponential function of the coefficient beta2 and the difference between the position of a selected random dingo (a [r1].c [c]), and the current position of the attacking dingo (a [agentIdx].c [c]) in the given dimension.
The new position is immediately adjusted using the normalization auxiliary function u.SeInDiSp, which ensures that the new position remains within the acceptable range from rangeMin[c] to rangeMax[c] for each dimension, taking into account the rangeStep[c] step.
Ultimately, the Persecution method causes the specified dingo (agentIdx) to start moving towards a randomly selected other dingo (r1), while taking into account the base best position and using the beta1 and beta2 parameters to adjust the chase strength and dynamics. In this case, the new position always remains within the strict boundaries of the search space.
//———————————————————————————————————————————————————————————————————— //--- Strategy 2: Chase (Eq. 3) void C_AO_DOAm_dingo::Persecution (int agentIdx) { // Select a random dingo int r1 = u.RNDintInRange (0, popSize - 1); // Apply the chase formula (Eq. 3) for (int c = 0; c < coords; c++) { // v = theBestVct + beta1 * exp(beta2) * (Positions[r1] - Positions[r]) a [agentIdx].c [c] = cB [c] + beta1 * MathExp (beta2) * (a [r1].c [c] - a [agentIdx].c [c]); a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
The Scavenger method implements "Strategy 3: Scavenging". The goal of this method is to update the position of one particular dingo (agentIdx) so that it moves to a new position that is some kind of "compromise" or "intermediate" value between its current position and the position of a randomly selected other dingo.
The method first selects a random dingo from the entire population and stores its index in the r1 variable. This dingo will act as a kind of reference point. A random binary value (sign) is also generated. This value will be either "1.0" or "-1.0". This determines whether the difference subtraction will be "forward" or "reverse".
Next comes the cycle, it goes through all the dimensions of the space the dingoes are located in. For each measurement, the new dingo position (agentIdx) is calculated as follows: the position of a randomly selected dingo (a[r1].c[c]) is taken and multiplied by the exponential of the coefficient "beta2", the current position of the attacking dingo is subtracted from this value (or added, depending on sign), and the resulting value is divided by 2.0.
This effectively means that the new position will be somewhere between the dingo's current position and a new "attractive" position, depending on beta2 and the chosen dingo. As in other methods, after calculating the new position, it is immediately adjusted using the u.SeInDiSp auxiliary function. This function ensures that the new position stays within the allowed range (from rangeMin[c] to rangeMax[c]) for each dimension.
Thus, the Scavenger method updates the dingo's position by moving it to a point that is the average between its current position and a position determined using an exponential transformation of the other dingo's position and a random sign. This mimics the behavior of an animal searching for prey that may be "along the way" or "somewhere between" known points.
//———————————————————————————————————————————————————————————————————— //--- Strategy 3: Scavenging (Eq. 4) void C_AO_DOAm_dingo::Scavenger (int agentIdx) { // Select a random dingo int r1 = u.RNDintInRange (0, popSize - 1); double sign = u.RNDbool() ? 1.0 : -1.0; // Apply the scavenging formula (Eq. 4) for (int c = 0; c < coords; c++) { // v = (exp(beta2) * Positions[r1] - (-1)^binary * Positions[r]) / 2 a [agentIdx].c [c] = (MathExp (beta2) * a [r1].c [c] - sign * a [agentIdx].c [c]) / 2.0; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
The SurvivalProcedure method implements "Strategy 4: Survival procedure". The main goal of this method is to update the position of one specific dingo (agentIdx) using information from two other dingoes to find a new, more advantageous position. The method first selects two different random dingoes from the entire population. Their indices are stored in r1 and r2 variables. It is guaranteed that r1 and r2 are not the same.
A random binary value (sign) is also generated, which can be either "1.0" or "-1.0". It determines whether the position of the second dingo will be subtracted with the direct or inverted sign. Next, a loop follows through all the dimensions. For each dimension, a new dingo position is calculated as follows: half the difference between the position of the first random dingo (a[r1].c[c]) and the position of the second random dingo (a[r2] is added to the best position found (cB[c]). c[c]), while the sign of the position of the second dingo can be inverted depending on "sign". After calculating the new position, it should be adjusted using the SeInDiSp auxiliary function.
In general, the SurvivalProcedure method demonstrates a strategy where a dingo updates its position by calculating it as a variation from the best position found, adding to it half the difference between the positions of two other randomly selected dingoes. The random sign adds an element of unpredictability to the choice of direction for this difference, and the position limitation ensures that the dingo stays in its working area. This can be interpreted as an attempt to find a "mean" between two other potential "promising" points, while taking into account the best known position.
//———————————————————————————————————————————————————————————————————— //--- Strategy 4: Survival procedure (Eq. 6) void C_AO_DOAm_dingo::SurvivalProcedure (int agentIdx) { // Select two different random dingoes int r1, r2; do { r1 = u.RNDintInRange (0, popSize - 1); r2 = u.RNDintInRange (0, popSize - 1); } while (r1 == r2); double sign = u.RNDbool() ? 1.0 : -1.0; // Apply the survival formula (Eq. 6) for (int c = 0; c < coords; c++) { // v = theBestVct + (Positions[r1] - (-1)^binary * Positions[r2]) / 2 a [agentIdx].c [c] = cB [c] + (a [r1].c [c] - sign * a [r2].c [c]) / 2.0; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
The UpdateSurvivalRates method is responsible for calculating and updating the "survival rates" for each dingo in the population. These indicators determine the likelihood that a dingo will help determine agent behavior in the next iteration.
First, the method initializes two variables: minFitness and maxFitness. It then cycles through all dingoes in the population. For each dingo, its current fitness value (a[i].f) is compared with the current minFitness and maxFitness. If the current fitness is less than minFitness, minFitness is updated. If the current fitness is greater than maxFitness, maxFitness is updated. As a result of this cycle, minFitness will contain the smallest fitness value in the entire population, and maxFitness will contain the largest.
If the difference between maxFitness and minFitness is very small (practically zero, meaning that all dingoes have the same fitness), then all dingoes are assigned a survival value of "0.5". This prevents division by zero and establishes equal chances for everyone.
If the fitness of the dingoes differs, the method is repeated in a loop across all dingoes. For each dingo, its survival rate (survival [i]) is calculated. The formula for this indicator is as follows: (maxFitness - a [i].f) / (maxFitness - minFitness). This formula normalizes the dingo's fitness value to the range of all fitness values.
The dingo with the lowest fitness, close to minFitness, will have a survival rate close to 1.0 (since maxFitness - a[i].f will be large). A dingo with the highest fitness, close to maxFitness, will have a survival rate close to 0.0 (since maxFitness - a[i].f will be close to zero).
Ultimately, the UpdateSurvivalRates method transforms absolute dingo fitness values into relative "survival rates", where higher fitness values correspond to lower survival rates, and vice versa. This can be interpreted as a strategy where those who "suffer" (have low fitness) have a greater chance of "surviving" or changing, while those who are most "successful" (with high fitness) have less incentive to change.
//———————————————————————————————————————————————————————————————————— //--- Update survival rates void C_AO_DOAm_dingo::UpdateSurvivalRates () { // Find the minimum and maximum 'fitness' double minFitness = DBL_MAX; double maxFitness = -DBL_MAX; for (int i = 0; i < popSize; i++) { if (a [i].f < minFitness) minFitness = a [i].f; if (a [i].f > maxFitness) maxFitness = a [i].f; } // Calculate survival rates if (MathAbs (maxFitness - minFitness) < DBL_EPSILON) { ArrayInitialize (survival, 0.5); } else { for (int i = 0; i < popSize; i++) { // survival_rate = (max - fit) / (max - min) survival [i] = (maxFitness - a [i].f) / (maxFitness - minFitness); } } } //————————————————————————————————————————————————————————————————————
The Revision method focuses on finding the best dingo in the current population after sorting and, if this dingo outperforms previously known best results, updates the global best solution scores. This is a standard procedure for optimization algorithms to store and track the best solution found throughout the search.
//———————————————————————————————————————————————————————————————————— //--- Update the best and worst solutions void C_AO_DOAm_dingo::Revision () { // Sort the population to find the best and worst static S_AO_Agent aT []; ArrayResize (aT, popSize); u.Sorting (a, aT, popSize); // Update the global best solution if (a [0].f > fB) { fB = a [0].f; ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); } } //————————————————————————————————————————————————————————————————————
Test results
Results from 50 runs, instead of the usual 10 (to smooth out unusually strong but random outcomes)
DOA|Dingo Optimization Algorithm|50.0|0.5|0.7|
=============================
5 Hilly's; Func runs: 10000; result: 0.4796838225601811
25 Hilly's; Func runs: 10000; result: 0.4536754883347326
500 Hilly's; Func runs: 10000; result: 0.4636906699500103
=============================
5 Forest's; Func runs: 10000; result: 0.9414536230352631
25 Forest's; Func runs: 10000; result: 0.8790978288567921
500 Forest's; Func runs: 10000; result: 0.9145376343407303
=============================
5 Megacity's; Func runs: 10000; result: 0.7861538461538461
25 Megacity's; Func runs: 10000; result: 0.8606153846153848
500 Megacity's; Func runs: 10000; result: 0.8480461538461537
=============================
All score: 6.62695 (73.63%)
The visualization shows a complex changing pattern in the search area, associated with the use of different hunting strategies (the characteristic diagonal patterns inherited from its predecessor are still visible). However, despite the high overall scores, there is a very large spread in the results across all test functions.

DOAm_dingo on the Hilly test function

DOAm_dingo on the Forest test function

DOAm_dingo on the Megacity test function
Based on the test results, the DOAm_dingo algorithm ranks first in our ranking of population optimization methods.
| # | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | DOAm_dingo | dingo_optimization_algorithm_M | 0.47968 | 0.45367 | 0.46369 | 1.39704 | 0.94145 | 0.87909 | 0.91454 | 2.73508 | 0.78615 | 0.86061 | 0.84805 | 2.49481 | 6.627 | 73.63 |
| 1 | ANS | across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | code lock algorithm (joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | animal migration ptimization M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O) evolution strategies | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | comet tail algorithm (joo) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | time evolution travel algorithm (joo) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | stochastic diffusion search M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | billiards optimization algorithm M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | archery algorithm M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | evolution of social groups (joo) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | simulated isotropic annealing (joo) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | EOm | extremal_optimization_M | 0.76166 | 0.77242 | 0.31747 | 1.85155 | 0.99999 | 0.76751 | 0.23527 | 2.00277 | 0.74769 | 0.53969 | 0.14249 | 1.42987 | 5.284 | 58.71 |
| 13 | BBO | biogeography based optimization | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 14 | ACS | artificial cooperative search | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 15 | DA | dialectical algorithm | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 16 | BHAm | black hole algorithm M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 17 | ASO | anarchy society optimization | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 18 | RFO | royal flush optimization (joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 19 | AOSm | atomic orbital search M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 20 | TSEA | turtle shell evolution algorithm (joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 21 | BSA | backtracking_search_algorithm | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| 22 | DE | differential evolution | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 23 | SRA | successful restaurateur algorithm (joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 24 | CRO | chemical reaction optimization | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 25 | BIO | blood inheritance optimization (joo) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 26 | DOA | dream_optimization_algorithm | 0.85556 | 0.70085 | 0.37280 | 1.92921 | 0.73421 | 0.48905 | 0.24147 | 1.46473 | 0.77231 | 0.47354 | 0.18561 | 1.43146 | 4.825 | 53.62 |
| 27 | BSA | bird swarm algorithm | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 28 | DEA | dolphin_echolocation_algorithm | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 29 | HS | harmony search | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 30 | SSG | saplings sowing and growing | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 31 | BCOm | bacterial chemotaxis optimization M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 32 | ABO | african buffalo optimization | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 33 | (PO)ES | (PO) evolution strategies | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 34 | FBA | fractal-based Algorithm | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 35 | TSm | tabu search M | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 36 | BSO | brain storm optimization | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 37 | WOAm | wale optimization algorithm M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 38 | AEFA | artificial electric field algorithm | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 39 | AEO | artificial ecosystem-based optimization algorithm | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 40 | CAm | camel algorithm M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 41 | ACOm | ant colony optimization M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 42 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 43 | DA_duelist | duelist_algorithm | 0.92782 | 0.53778 | 0.27792 | 1.74352 | 0.86957 | 0.47536 | 0.18193 | 1.52686 | 0.62153 | 0.33569 | 0.11715 | 1.07437 | 4.345 | 48.28 |
| 44 | BFO-GA | bacterial foraging optimization - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| RW | random walk | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
Summary
The considered modification of the DOA_dingo algorithm stands out for its high search capabilities. However, it should be noted that these impressive results are average values obtained in a series of tests. This means that to ensure sufficient exploration of the search space, the user will have to perform the optimization multiple times.
On the other hand, the algorithm shows a rather modest convergence rate on smooth functions. Nevertheless, DOA_dingo is a very useful and promising method. I would recommend using it for the initial stage of searching, with subsequent transition to more accurate algorithms. It is also extremely important to pre-normalize all parameters to the same range of values.

Figure 2. Color-coded comparison of algorithms across the corresponding tests

Figure 3. Histogram of algorithm testing results (scale from 0 to 100, the higher the better, where 100 is the maximum possible theoretical result, in the archive there is a script for calculating the rating table)
DOAm_dingo pros and cons:
Pros:
- Fast.
- Very high average results.
Disadvantages:
- High variability in results.
- Tendency to get stuck.
The article is accompanied by an archive with the current versions of the algorithm codes. The author of the article is not responsible for the absolute accuracy in the description of canonical algorithms. Changes have been made to many of them to improve search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include | Parent class of population optimization algorithms |
| 2 | #C_AO_enum.mqh | Include | Enumeration of population optimization algorithms |
| 3 | TestFunctions.mqh | Include | Library of test functions |
| 4 | TestStandFunctions.mqh | Include | Test stand function library |
| 5 | Utilities.mqh | Include | Library of auxiliary functions |
| 6 | CalculationTestResults.mqh | Include | Script for calculating results for the comparison table |
| 7 | Testing AOs.mq5 | Script | The unified test stand for all population optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population optimization algorithms without visualization |
| 9 | Test_AO_DOAm_dingo.mq5 | Script | DOAm_dingo test stand |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/19187
Warning: All rights to these materials are reserved by MetaQuotes Ltd. Copying or reprinting of these materials in whole or in part is prohibited.
This article was written by a user of the site and reflects their personal views. MetaQuotes Ltd is not responsible for the accuracy of the information presented, nor for any consequences resulting from the use of the solutions, strategies or recommendations described.
Defining your Edge (Part 1): Using a Discrete Fourier Transform and a Spiking Neural Network in a Trading Robot
Building a Correlation-Aware Portfolio Risk Monitor in MQL5
Comparing Trade Return Distributions with Mann-Whitney U in MQL5
Price Action Analysis Toolkit Development (Part 75): Building a Modular Multi-Symbol Trading Panel in MQL5
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
An article entitled ‘Modification of the Dingo Optimisation Algorithm — Dingo Optimisation Algorithm M (DOAm)’ has been published:
Author: Andrey Dik