生成一个集合的所有可能序列的并行算法

Parallel algorithm to produce all possible sequences of a set

本文关键字:有可能 并行算法 集合 一个      更新时间:2023-10-16

在尝试生成给定字符集的所有可能字符串时,我遇到了困难。设S为符号集。我需要处理长度为nS的所有可能组合。例如,如果S={'a','b','+','-'}n=4,算法应处理以下序列:

aaaa
aaab
abab
+aa-
// And all other sequences in the universe

目前我的算法是一个低效的递归算法,如下所述。我有两个问题:

  1. 有没有更快的算法?
  2. 是否有并行算法来解决这个问题?

当前实现:(简化)

void workhorse(vector<char> &input, vector<char>::iterator i)
{
    if(i==inputs.end()) {
        // process the input
        return;
    }
    else {
        for( const auto& symbol : S) {
            *i=symbol;
            workhorse(input, i+1);
        }
    }
}   

你的算法看起来已经很有效了,你没有浪费任何工作。唯一可以稍微改进的是由于递归导致的函数调用开销。但是递归是好的,因为它允许简单的并行化:

#include <thread>
#include <array>
#include <string>
#include <vector>
using namespace std;
array<char,3> S = {{ 'a', 'b', 'c' }};
const int split_depth = 2;
void workhorse(string& result, int i) {
    if (i == result.size()) {
        // process the input
        return;
    }
    if (i == split_depth) {
        vector<thread> threads;
        for (char symbol : S) {
            result[i] = symbol;
            threads.emplace_back([=] {
                string cpy(result);
                workhorse(cpy, i + 1);
            });
        }
        for (thread& t: threads) t.join();
    } else {
        for (char symbol : S) {
            result[i] = symbol;
            workhorse(result, i + 1);
        }
    }
}
int main() {
    string res(6, 0);
    workhorse(res, 0);
}

确保在编译时启用c++ 11特性和线程,例如

$ g++ -O3 -std=c++11 -lpthread [file].cpp

这个版本的函数将依次枚举长度不超过split_depth的所有前缀,然后生成一个线程来进一步处理每个前缀。因此,它将总共启动|S|^split_depth线程,您可以根据您的硬件并发性进行调整。

您需要所有的排列或组合吗?从你的例子中,它似乎是组合(顺序不重要,符号可以重复),但在你的代码中,它看起来像你可能试图生成排列(猜测函数名)。组合是一个简单的以n为基数计数的问题-在这种情况下是4^4,排列将是少得多的4!但是稍微复杂一点的递归算法取决于你是否想保持字典顺序。无论哪种方式,算法都是计算机科学的基本支柱之一,并且已经被很好地覆盖了,试试其他的Q:

生成所有可能的组合

生成字符串所有可能排列的列表

你可以迭代地做。但是它不会快很多。

假设集合中的字符为数字。

'a' = 0, 'b' = 1, '+' = 2, '-' = 3

从0000开始,然后递增,直到3333。

0000年,0001年,0002年,0003年,0010年,0011年,等等…

这可以很容易地并行化。对于两个线程,让第一个线程执行从0000到1333的工作,另一个线程执行从2000到3333的工作。显然,这可以很容易地扩展到任意数量的线程。

没有什么可做的了。如果你的程序很慢,那是因为有太多的组合可以选择。通过此代码找到所有组合所需的时间线性取决于存在的组合的数量。