Есть ли возможность улучшить код для параллельного нахождения комбинаций?
Мне необходимо создать алгоритм который будет выдавать комбинации размером в 10 элементов из начального массива данных. Многопоточность реализована передачей общего количества потоков и номера текущего потока, что позволяет перебирать комбинации с пропуском.
Изначальный код взял отсюда: https://www.technical-recipes.com/2017/obtaining-combinations-of-k-elements-from-n-in-c/
Он не имел возможности поиска для отдельного потока, поэтому я немного переписал код и попытался максимально его оптимизировать (убрать использование LINQ, списков, указать явное кол-во элементов). Сейчас мой код выглядит так:
// Эта функция почти не изменилась, за исключением того что я явно указал один параметр 10 и подставил его везде где он использовался
static public bool NextCombination(int[] num, int n)
{
bool finished = false;
bool changed = false;
for (int i = 9; !finished && !changed; --i)
{
if (num[i] < n - 10 + i)
{
++num[i];
if (i < 9)
for (int j = i + 1; j < 10; ++j)
num[j] = num[j - 1] + 1;
changed = true;
}
finished = i == 0;
}
return changed;
}
static public IEnumerable Combinations<MyClass>(MyClass[] elem, int start, int skip)
{
int size = elem.Length;
int[] numbers = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
long step = 0;
MyClass[] resultList = new MyClass[10]; // Массив который будет содержать текущую комбинацию
do
{
if ((step - start) % skip == 0) //Если текущая комбинация подходит к номеру текущего потока
{
// Сохранить текущую комбинацию в массив и выдать её через yield return. Так оно работает гораздо быстрее, чем через LINQ как это было в оригинальной статье
for (int i = 0; i < 10; ++i)
resultList[i] = elem[numbers[i]];
yield return resultList;
}
step++; // Счётчик текущей комбинации
} while (NextCombination(numbers, size));
}
Вызываю я эту функцию таким образом:
// Это выполняется в каждом отдельном потоке
// pool - массив который может достигать сотни элементов
// start - номер текущего потока (от 0)
// skip - кол-во потоков
public void SearchThread(MyClass[] pool, int start, int skip)
{
foreach (MyClass[] _ in Combinations(pool, start, skip))
{
//Обработка результата
//Выходной массив должен быть длинной в 10 элементов и не иметь повторов
}
}
// Код который запускает потоки:
List<Thread> searchThreads = new List<Thread>();
MyClass[] pool = new MyClass[100]; //Массив элементов, из которых будут собираться пары длинной в 10 объектов
for(int i = 0; i < 100; i++)
pool[i] = new MyClass();
int threads = threadCount; //Кол-во потоков которое я получаю с формы
try
{
for (int j = 0; j < threads; j++) {
int startIndex = j; //Необходимо скопировать значение в другую переменную, иначе два потока могут получить один айди
Thread newThread = new Thread(() => SearchThread(pool.ToArray(), wanted, startIndex, threads));
newThread.Start();
searchThreads.Add(newThread); //Сохраняю поток в список чтобы его можно было закрыть при завершении работы
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Хотелось бы узнать есть ли возможность улучшить код, либо же вовсе переделать его с нуля.