The code has clear logic and it's well organized, I noticed you've used different approaches, which is a good thing.
In the first instance, you applied simulated annealing, however your tweak function looks too "weak", because it allows to modify only one set at a time, limiting your search space. Additionally you discarded every invalid solutions, which could reduce the algorithm's effectiveness in exploring.
In the second and third instances you applied RMHC, however, similar to the first instance, your tweak function only modify one set at a time.
In the last three instances, you applied RMHC with the steepest step and restart strategies, which can indeed lead to more optimal solutions
Possible improvements could be:
Implementing a more powerful tweak function in all instances, in order to allow larger modifications and a more effective search
Defining a fitness function that allows to keep track of factors like the number of elements covered by a solution
To address RMHC limitations (local optima) you could try implementing different techniques such as simulated annealing, which can help the algorithm escape local optima more effectively.
General comments and possible improvements
The code has clear logic and it's well organized, I noticed you've used different approaches, which is a good thing. In the first instance, you applied simulated annealing, however your tweak function looks too "weak", because it allows to modify only one set at a time, limiting your search space. Additionally you discarded every invalid solutions, which could reduce the algorithm's effectiveness in exploring. In the second and third instances you applied RMHC, however, similar to the first instance, your tweak function only modify one set at a time. In the last three instances, you applied RMHC with the steepest step and restart strategies, which can indeed lead to more optimal solutions
Possible improvements could be: