Crow Search Algorithm (CSA)
Introduction
In this article, we will examine the Crow Search Algorithm (CSA), whose idea seemed very promising to me. It is a swarm-based metaheuristic method proposed for global optimization. CSA simulates the behavior of crows as they hide and search for food caches. The algorithm is simple to implement, has few parameters, and has given rise to numerous modifications and hybrid versions. We will examine the original version.
The CSA algorithm was proposed by Ali Askarzadeh and was published in 2016 in Computers & Structures (Elsevier).
Implementation of the algorithm
Imagine an early morning in a park. A crow is perched on a branch — black, glossy, with eyes that reflect a thought. It is not just a bird — it is a strategist. And these aren't just any birds — they are true analysts of the natural world, the smartest of all birds. The intelligent behavior of these birds inspired the creation of the Crow Search Algorithm (CSA), because they are capable of doing what a good algorithm should be able to do: search, remember, adapt, and not be fooled.
Skill 1: Remembering caches. A crow finds a piece of bread and hides it under some leaves. It does not just drop it; it remembers the place. A day later, the crow returns and knows exactly where to look. In the algorithm, this means that each “crow” (agent) stores its best position as a cache. It is its own personal treasure, and it never forgets where it is.
Skill 2: Following others. Another crow is watching. It is sitting on a lamppost, watching where the first crow hid its food. Then, once the first crow flies away, it comes down and checks. In CSA, an agent selects another agent and tries to “follow” it to its cache — after all, if that agent has found a good solution, why not check it?
Skill 3: Deception. However, the first crow is not that naive. It knows it may be being followed. So it makes a feint — it pretends to hide the food in a bush, but actually hides it behind a rock. The algorithm works similarly: if an agent “notices that it is being followed,” it leads the pursuer to a random location. This helps the algorithm avoid getting stuck on a single solution — as if the crow were saying, “Did you think I’d lead you to a place with lots of tasty treats?” “Ha, fooled you!”
Skill 4: Adaptation. Crows do not follow a set pattern. Today they hide their food in the grass; tomorrow — in a crack in the fence. They try things, learn, and adjust their strategy. In CSA, movement and behavior parameters can change. The algorithm does not stand still — it adapts, just like a crow searching for the best way to survive.
Ultimately, a crow is not just a bird, but a metaphor for intelligence, and the Crow Search Algorithm is not a dry formula. It is an attempt to bring into the world of numbers what crows do every day: “Seek out the best, defend what’s theirs, learn from others, and be cunning enough not to be fooled.”

Figure 1. CSA algorithm
The illustration above shows the main scenarios and rules for how the algorithm works. After examining the strategies of the CSA optimization method in detail, we can draw up detailed pseudocode for the algorithm.
- Set the parameters:
- N = number of crows (population size)
- max_iter = maximum number of iterations
- fl = flight length
- AP = awareness probability
- d = dimensionality of the problem
- Initialize the population:
- For each crow i from 1 to N:
- Position[i] = a random position in the search space
- Calculate the fitness of Position[i]
- Memory[i] = Position[i] (initially, memory is set to the current position)
- Memory_fitness[i] = fitness of position[i]
- For each crow i from 1 to N:
- Repeat for each iteration t from 1 to max_iter:
- For each crow i from 1 to N: a. Choose a target to follow:
- Randomly select a crow j (where j ≠ i)
- r = a random number between 0 and 1
- For each dimension k:
- ri = a random number between 0 and 1
- New_position[i][k] = Position[i][k] + ri × fl × (Memory[j][k] - Position[i][k])
- New_position[i] = a random position in the search space
- Ensure that New_position[i] is within the bounds
- If it is outside the bounds, move it back to the nearest bound
- Calculate the fitness of the new positions:
- For each i crow:
- Fitness_new[i] = calculate_fitness(New_position[i])
- For each i crow:
- Update memory:
- For each i crow:
- IF Fitness_new[i] is better than Fitness_memory[i]:
- Memory[i] = New_position[i]
- Memory_fitness[i] = Fitness_new[i]
- OTHERWISE:
- Memory does not change
- IF Fitness_new[i] is better than Fitness_memory[i]:
- For each i crow:
- Update current positions:
- For each i crow:
- Position[i] = New_position[i]
- For each i crow:
- For each crow i from 1 to N: a. Choose a target to follow:
- Find the best solution:
- Best_Solution = the position stored in memory with the best fitness among all crows
- Return Best_solution
Let's move on to the most important part — writing code. The S_CrowMemory structure is designed to store information about a crow's "memory":
- Position dimensionality — how many coordinates make up one "position" in this memory.
- Position coordinate values — an array in which each number represents a specific coordinate value in this position.
- Fitness value — indicates how "good" or "fit" a given position is.
The Init method is used to initialize this structure. When it is called, the number of dimensions is passed as an argument. This method allocates the necessary storage space for the position coordinates based on the provided number of dimensions, and sets the initial fitness value to a very small number (the most negative possible value) so that any actual fitness value obtained will be greater.
//———————————————————————————————————————————————————————————————————— // Structure for storing crow memory struct S_CrowMemory { double position[]; // position in memory double fitness; // fitness of the position in memory void Init(int dimensions) { ArrayResize(position, dimensions); fitness = -DBL_MAX; } }; //————————————————————————————————————————————————————————————————————
The C_AO_CrowSearchAlgorithm class is an implementation of a search algorithm based on the behavior of crows. It inherits from the base C_AO class. When an object of the class is created, the algorithm's main parameters are initialized in addition to setting the name and description:
- popSize — the number of agents (crows) in the population,
- flightLength — how far a crow moves during flight,
- awarenessProbability — the probability that a crow is "aware" of its surroundings and the actions of other crows.
- SetParams () — a method intended to update the algorithm's internal parameters from an external source (from the "params" array).
- Init () — the main initialization point for the entire algorithm. It accepts the allowed value ranges for the search parameters (minimum, maximum, step) and the total number of epochs (iterations).
- Moving () — the main movement and for updating the positions of the crows in the search space at each iteration of the algorithm.
- Revision () — checks and adjusts states, and evaluates the quality of the solutions found after each iteration.
- InitializeMemory () — initializes memory for all crows in the population,
- SelectRandomCrow () — selects a random crow from the population, excluding the specified crow,
- UpdateMemory () — updates the memory entry for a specific crow.
- flightLength — flight length, algorithm parameter
- awarenessProbability — awareness probability, algorithm parameter
//———————————————————————————————————————————————————————————————————— class C_AO_CrowSearchAlgorithm : public C_AO { public: //---------------------------------------------------------- ~C_AO_CrowSearchAlgorithm () { } C_AO_CrowSearchAlgorithm () { ao_name = "CSA"; ao_desc = "Crow Search Algorithm"; ao_link = "https://www.mql5.com/ru/articles/19669"; popSize = 20; // population size (number of crows) flightLength = 2.0; // flight length (fl) awarenessProbability = 0.1; // awareness probability (AP) ArrayResize (params, 3); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "flightLength"; params [1].val = flightLength; params [2].name = "awarenessProbability"; params [2].val = awarenessProbability; } void SetParams () { popSize = (int)params [0].val; flightLength = params [1].val; awarenessProbability = params [2].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP); void Moving (); void Revision (); private: void InitializeMemory (); int SelectRandomCrow (int excludeCrow); void UpdateMemory (int crowIndex); //------------------------------------------------------------------ public: double flightLength; // flight length (fl) double awarenessProbability; // awareness probability (AP) private: //--------------------------------------------------------- S_CrowMemory crowMemory[]; // crow memory (array of structures) }; //————————————————————————————————————————————————————————————————————
The Init method serves as the starting point for running the crow search algorithm. Its main task is to prepare all the necessary structures and verify that the inputs are correct.
Standard initialization. First, the StandardInit method is called, which performs the general setup required for any optimization algorithm: it sets the search ranges, the step size, the number of epochs, and determines the number of "dimensions". If this standard initialization fails, the Init method immediately returns "false".
Initializing the crow memory. Memory is allocated for the crowMemory array of structures, whose size is equal to popSize (the number of crows in the population). Next, a separate Init method is called for each crow in the array. This method, in turn, configures the crow's memory structure by specifying how many dimensions (parameters) it should track (this value is taken from 'coords', which is set by StandardInit), and sets the initial fitness value.
Checking and adjusting parameters. The flightLength parameter is checked. If it is less than or equal to zero, it is assigned the default value (2.0). The awarenessProbability parameter is checked. If it is less than zero, it is set to 0.0. If it is greater than one, it is set to 1.0. These checks ensure that the probabilistic parameters remain within the valid range.
Finally, if all initialization steps succeed, the method returns 'true', and the algorithm is ready to run.//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_CrowSearchAlgorithm::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ // Initializing an array of memory structures ArrayResize (crowMemory, popSize); for (int i = 0; i < popSize; i++) { crowMemory[i].Init(coords); } // Check parameter validity if (flightLength <= 0.0) flightLength = 2.0; if (awarenessProbability < 0.0) awarenessProbability = 0.0; if (awarenessProbability > 1.0) awarenessProbability = 1.0; return true; } //————————————————————————————————————————————————————————————————————
The InitializeMemory method is responsible for initially populating each crow's "memory" with information about its current position and its quality (fitness).
Iterating over all crows. The method iterates through each crow in the population, using a loop from zero to popSize (the total number of crows).
Copying the position. For each i crow , the current position of i crow is copied; "a [i].c" contains the current coordinates of i crow (as an array), and 'coords' is the number of dimensions (parameters) in the search space. These coordinates are then stored in crowMemory [i].position – in the memory of i crow.
Storing fitness. Along with the position, the fitness (quality) value of the crow's current position, a [i].f, is stored in crowMemory [i].fitness.Thus, after this method is executed, each crow will store in its memory structure (crowMemory) information about the position it occupied at the start of the algorithm, along with the corresponding fitness value. This serves as the starting point for the subsequent optimization, in which the crows will use this "primary memory" to make decisions about their movements.
//———————————————————————————————————————————————————————————————————— //--- Initializing crows' memory void C_AO_CrowSearchAlgorithm::InitializeMemory () { // Initialize memory with the current positions for (int i = 0; i < popSize; i++) { ArrayCopy (crowMemory[i].position, a[i].c, 0, 0, coords); crowMemory[i].fitness = a[i].f; } } //————————————————————————————————————————————————————————————————————
The SelectRandomCrow method is designed to select a random crow from the entire population, while ensuring that the selected crow is not the one we want to exclude (specified in the excludeCrow parameter).
Checking the population size. If there is only one crow in the population (popSize <= 1), then the only available crow is the one with index "0", so it is returned.
Generating a random index. The method enters a "do-while" loop. Inside the loop, a random number (selectedCrow) is generated, which represents the index of a potentially selected crow. The u.RNDfromCI function is used for generation; it returns a random number in the range from 0.0 to popSize (excluding the upper bound itself, which is why a small offset is added). The resulting floating-point number is converted to an integer. Additional checks are performed to ensure that the resulting index falls within the valid bounds of the population array. If the index falls outside these bounds, it is adjusted.
Exclusion condition. The "do-while" loop continues to generate random indices until the generated selectedCrow is different from excludeCrow. In other words, if the crow we want to exclude happens to be selected, the process is repeated.
Returning the selected crow. As soon as a random index is generated that does not match excludeCrow, that index is returned as the result of the method.
//———————————————————————————————————————————————————————————————————— //--- Select a random crow (excluding the specified one) int C_AO_CrowSearchAlgorithm::SelectRandomCrow (int excludeCrow) { if (popSize <= 1) return 0; int selectedCrow; do { selectedCrow = (int)MathFloor(u.RNDfromCI(0.0, popSize - 0.001)); if (selectedCrow >= popSize) selectedCrow = popSize - 1; if (selectedCrow < 0) selectedCrow = 0; } while (selectedCrow == excludeCrow); return selectedCrow; } //————————————————————————————————————————————————————————————————————
The Moving method is the main operating mechanism of the Crow Search Algorithm. It is responsible for moving and updating the crows' positions during the search. The method consists of two phases: initialization and the main iteration loop.
Phase 1: Population initialization (on first run). If the algorithm is run for the first time (determined by the "revision" flag), all crows are initialized. For each crow in the population, its coordinates (a [i].c) are assigned random values. These values are generated within the specified ranges (rangeMin, rangeMax) and with the specified step size (rangeStep), which ensures that the initial positions of the crows are feasible. After initialization, the "revision" flag is set to 'true' so that the next invocation of the method will proceed to the main loop phase.
Phase 2: Main iteration loop (after initialization). This loop is executed on each iteration of the algorithm and includes the following steps for each crow:
- Saving the previous position. Before generating a new position, the current position (a [i].c) and fitness value (a [i].f) of the current crow are stored in the corresponding "previous" variables (a [i].cP and a [i].fP, respectively). This is needed so that, if the new position turns out to be worse, the algorithm can revert to the previous one,
- Selecting an "opponent". Another crow (j) is randomly selected from the population. This crow becomes the one that the current crow follows. It is important that the selected crow cannot be the same one we are currently processing (SelectRandomCrow(i) ensures this),
- Determining the movement scenario. A random number (r_j) between 0 and 1 is generated and compared with the awarenessProbability parameter (awareness probability). This parameter determines how the crow will move:
-
Scenario 1: One crow follows another (Formula 1)
- If r_j is greater than or equal to awarenessProbability, this means that i crow will attempt to move closer to the best position based on the position of the selected j crow.
- The new position is calculated using the following formula: new position = current position + random step * flight length * (crow position (j) - current position).
- A random step (r_i) is another random number that introduces variability.
- The flight length, "flightLength," is a parameter that controls how far a crow can move.
- Crow position (j) - current_position is a vector directed from the current position to the position of the crow selected to be followed.
- After the new position is calculated, it is also checked for feasibility within the specified ranges (rangeMin, rangeMax, rangeStep).
-
Scenario 2: Completely Random Movement (Formula 2)
- If r_j is less than awarenessProbability, this means that j crow "notices" it is being followed and takes decisive action.
- In this case, i crow ignores the position of j crow and moves to a completely new random position within the entire search space.
- This new random position is also generated taking into account the permissible ranges and steps.
//———————————————————————————————————————————————————————————————————— //--- Main step of the algorithm (formulas 1 and 2 from the document) void C_AO_CrowSearchAlgorithm::Moving () { // Initial population initialization if (!revision) { // Step 1: Initialize the crow population randomly 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; } //------------------------------------------------------------------ // Main iteration loop // Step 5: Generate a new position for each crow for (int i = 0; i < popSize; i++) { // Store the current positions in cP (previous coordinates) ArrayCopy (a[i].cP, a[i].c, 0, 0, coords); a[i].fP = a[i].f; // Randomly select crow j to follow (excluding itself) int j = SelectRandomCrow(i); // Generate a random number to determine awareness double r_j = u.RNDfromCI(0.0, 1.0); // Apply the position update formulas according to the scenarios if (r_j >= awarenessProbability) { // Scenario 1: Crow j is unaware of being followed // Formula (1): P_i^(t+1) = P_i^t + r_i * fl_j * (M_j^t - P_i^t) for (int c = 0; c < coords; c++) { double r_i = u.RNDfromCI(0.0, 1.0); a[i].c[c] = a[i].c[c] + r_i * flightLength * (crowMemory[j].position[c] - a[i].c[c]); // Step 6: Feasibility check for the new position a[i].c[c] = u.SeInDiSp (a[i].c[c], rangeMin[c], rangeMax[c], rangeStep[c]); } } else { // Scenario 2: Crow j is aware of being followed // Formula (2): Movement to a random position for (int c = 0; c < coords; c++) { a[i].c[c] = u.RNDfromCI (rangeMin[c], rangeMax[c]); a[i].c[c] = u.SeInDiSp (a[i].c[c], rangeMin[c], rangeMax[c], rangeStep[c]); } } } } //————————————————————————————————————————————————————————————————————
The UpdateMemory method is responsible for updating the "memory" of a specific crow. The crow's memory stores its best-found position and the corresponding fitness value.
Comparison of the current fitness with the stored fitness value. The method receives the index of the crow, crowIndex, whose memory needs to be updated. It compares the fitness value of this crow's current position, a [crowIndex].f, with the fitness value previously stored in its memory.
Memory update (if the new position is better). If the current fitness is better than the previously stored fitness, the memory is updated: the crow’s current position is copied into its memory, and the current fitness value is copied into memory as the new best value.
Thus, this method ensures that each crow’s memory always retains information about the best point it has reached so far. If a crow finds a new position that yields a higher fitness value, it "remembers" that new position and its fitness. Otherwise, the memory remains unchanged.
//———————————————————————————————————————————————————————————————————— //--- Update the crow's memory void C_AO_CrowSearchAlgorithm::UpdateMemory (int crowIndex) { // Formula (3): Update the memory if the new position is better if (a[crowIndex].f > crowMemory[crowIndex].fitness) { ArrayCopy (crowMemory[crowIndex].position, a[crowIndex].c, 0, 0, coords); crowMemory[crowIndex].fitness = a[crowIndex].f; } } //————————————————————————————————————————————————————————————————————
The Revision method is responsible for evaluating and updating the results after each main crow movement cycle.
Initial memory initialization. When the algorithm is first run (when the fitness values in the crows' memory are set to the minimum possible value, indicating that they are not yet valid), a separate method is called to initialize the memory. This ensures that each crow has a properly populated "baseline" for comparison.
Updating each crow's individual memory. The method iterates through all crows in the population and calls the UpdateMemory method for each crow; this method compares the crow's current position with its best position stored in memory. If the new position is better, this crow's memory is updated.
Updating the global best solutions. The method again iterates through all crows, this time to find the best solutions among the current positions of all crows. If the current position of any crow (a[i].f) turns out to be better than the known global best value (fB), that value and the corresponding position are saved as the new global best solution (fB and cB).
Additional memory check. After evaluating the current positions, another check is performed, this time on each crow’s individual memory. If the fitness value of any stored best (maximum) position (crowMemory [i].fitness) turns out to be better than the current global best (fB), then the global best solution is updated. This is important because the UpdateMemory method could update the crow's memory, but the crow could then move away from that best position during the current iteration.Thus, the Revision method serves as a point for gathering information after each step of the algorithm. It keeps both each crow's best individual achievements and the overall best and worst solutions found by the entire population so far up to date.
//———————————————————————————————————————————————————————————————————— //--- Update and check results void C_AO_CrowSearchAlgorithm::Revision () { // If this is the first iteration after initialization if (crowMemory[0].fitness == -DBL_MAX) { InitializeMemory(); } // Step 8: Updating each crow's memory for (int i = 0; i < popSize; i++) { UpdateMemory(i); } // Update the global best solution for (int i = 0; i < popSize; i++) { if (a[i].f > fB) { fB = a[i].f; ArrayCopy (cB, a[i].c, 0, 0, WHOLE_ARRAY); } } // We also check memory for better solutions for (int i = 0; i < popSize; i++) { if (crowMemory[i].fitness > fB) { fB = crowMemory[i].fitness; ArrayCopy (cB, crowMemory[i].position, 0, 0, WHOLE_ARRAY); } } } //————————————————————————————————————————————————————————————————————
Test results
As the results show, performance is average and weaker on the discrete Megacity function.
=============================
5 Hilly's; Func runs: 10000; result: 0.7780377334241331
25 Hilly's; Func runs: 10000; result: 0.5107983411957056
500 Hilly's; Func runs: 10000; result: 0.2671707154561419
=============================
5 Forest's; Func runs: 10000; result: 0.9221928451900311
25 Forest's; Func runs: 10000; result: 0.48104687904934185
500 Forest's; Func runs: 10000; result: 0.16367212698152436
=============================
5 Megacity's; Func runs: 10000; result: 0.4846153846153848
25 Megacity's; Func runs: 10000; result: 0.34215384615384614
500 Megacity's; Func runs: 10000; result: 0.11487692307692406
=============================
All score: 4.06456 (45.16%)
The visualization shows significant scatter for low-dimensional functions, and the long horizontal green lines indicate that the algorithm is getting stuck in local extrema. There is also some scatter for medium-dimensional problems; the algorithm has the greatest difficulty with high-dimensional cases, indicated by the red lines.

CSA_crow on the Hilly test function

CSA_crow on the Forest test function

CSA_crow on the Megacity test function
After a series of test runs, the CSA_crow algorithm is shown in the ranking table for reference.
| No. | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1,000 p (500 F) | ||||||||
| 1 | DOAdingom | 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 |
| 2 | 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 |
| 3 | 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 |
| 4 | 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 |
| 5 | (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 |
| 6 | 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 |
| 7 | 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 |
| 8 | 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 |
| 9 | 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 |
| 10 | 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 |
| 11 | 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 |
| 12 | 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 |
| 13 | 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 |
| 14 | 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 |
| 15 | 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 |
| 16 | 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 |
| 17 | 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 |
| 18 | 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 |
| 19 | 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 |
| 20 | 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 |
| 21 | 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 |
| 22 | 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 |
| 23 | 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 |
| 24 | 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 |
| 25 | CRO | chemical reaction optimisation | 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 |
| 26 | 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 |
| 27 | 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 |
| 28 | 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 |
| 29 | 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 |
| 30 | 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 |
| 31 | 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 |
| 32 | 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 |
| 33 | 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 |
| 34 | (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 |
| 35 | 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 |
| 36 | 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 |
| 37 | 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 |
| 38 | 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 |
| 39 | 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 |
| 40 | 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 |
| 41 | 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 |
| 42 | 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 |
| 43 | 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 |
| 44 | 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 |
| 45 | 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 |
| CSA_crow | crow_search_algorithm | 0.77804 | 0.51080 | 0.26717 | 1.55601 | 0.92219 | 0.48105 | 0.16367 | 1.56691 | 0.48461 | 0.34215 | 0.11487 | 0.94163 | 4.065 | 45.16 | |
| 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 | |
Conclusions
After a series of tests, the algorithm did not make it into the ranking table of population-based optimization algorithms; it fell just short, but it is a very promising method. Elegant simplicity — the algorithm is truly complete and self-contained. It's hard to add anything without compromising the basic concept. The problem is that simplicity, which is an advantage for ease of understanding, becomes a disadvantage in terms of performance in this case. I can't say this is a bad optimization method — it has its merits — but it does tend to get stuck, which is especially noticeable on low-dimensional functions. Despite its generally modest results, the algorithm demonstrates stability in terms of “not showing significant drops on any particular type of problem” according to the color coding, unlike some algorithms that are in the top ranks but are marked in red for certain problem types.
Biological motivation — the crow metaphor is intuitive and logical, which makes the algorithm easy to understand and implement. There are many modifications of CSA (with Lévy flights, adaptive parameters, and chaotic maps), but most of them significantly complicate the algorithm, losing its main advantage — simplicity, provide only a minor improvement in performance, and turn CSA into a hybrid with other methods.
Minimum number of parameters — only three settings (N, fl, AP), which simplifies configuration compared to more complex metaheuristics. CSA is a good learning resource and a solid basic optimization method. It is not revolutionary, but it is reliable for simple tasks, and it has a place in a trader's arsenal as a fallback option when more complex methods are excessive or when ease of implementation is important.

Figure 2. Color coding of algorithms for the corresponding tests

Figure 3. Histogram of algorithm test results (on a scale from 0 to 100; the higher the score, the better, where 100 is the maximum possible theoretical result; the archive contains a script for calculating the ranking table)
Pros and cons of the CSA_crow algorithm:
Pros:
- An elegant and promising idea as a basis for further development.
- A small number of parameters.
Cons:
- High variance in results.
- Poor performance on high-dimensional problems.
An archive with the latest versions of the algorithm source code is attached to the article. The author of this article does not claim absolute accuracy in describing the canonical algorithms; many of them have been modified to improve their search capabilities. The conclusions and judgments presented in the articles are based on the results of the experiments conducted.
Programs used in the article
| # | Name | Type | Description |
|---|---|---|---|
| 1 | #C_AO.mqh | Include file | Parent class for population-based optimization algorithms |
| 2 | #C_AO_enum.mqh | Include file | Enumeration of population-based optimization algorithms |
| 3 | TestFunctions.mqh | Include file | Library of test functions |
| 4 | TestStandFunctions.mqh | Include file | Library of test bench functions |
| 5 | Utilities.mqh | Include file | Library of utility functions |
| 6 | CalculationTestResults.mqh | Include file | Script for calculating results for the comparison table |
| 7 | Testing AOs.mq5 | Script | A unified test bench for all population-based optimization algorithms |
| 8 | Simple use of population optimization algorithms.mq5 | Script | A simple example of using population-based optimization algorithms without visualization |
| 9 | Test_AO_CSA_crow.mq5 | Script | Test bench for CSA_crow |
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/19669
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.
Creating a Probabilistic Market-Neutral Trading Robot Based on a Return Distribution
Trading Options Without Options (Part 3): Complex Option Strategies
Bayesian Online Change-Point Detection (BOCPD) in MQL5: One Regime-Break Signal, Three Ways to Use It
Formulating Dynamic Multi-Pair EA (Part 10): Asymmetric Stop-Loss Logic Based on Pair-Specific Volatility Signatures
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use