创建变量之间的运算符排列

Creating permutations of operators between variables

本文关键字:运算符 排列 之间 变量 创建      更新时间:2023-10-16

假设我有以下输入向量

vector<string> variables = { "A", "B", "C", "D" };
vector<string> operators = { "+" };

因此,我想获得这些变量之间运算符的所有不同排列。变量始终位于固定位置 - 只有运算符会更改。

对于以前的输入向量,我需要有以下输出:

A + B + C + D

这很简单,因为只有一个运算符可用("+")。但是,如果我有以下输入向量:

vector<string> variables = { "A", "B", "C", "D" };
vector<string> operators = { "+", "-" };

则需要以下输出:

A + B + C + D
A - B + C + D
A + B - C + D
A + B + C - D
A - B - C + D
A + B - C - D
A - B + C - D
A - B - C - D

现在我有了变量之间这些运算符的所有可能变体。

到目前为止,我写了以下函数

template<class T>
vector<vector<T> > getAllPermutations(vector<T> input) {
vector<vector<T> > res;
do {
vector<T> current_vector;
for (T index : input)
current_vector.push_back(index);
res.push_back(current_vector);
} while (next_permutation(input.begin(), input.end()));
return res;
}

但这只能解决部分问题,因为我不知道每次需要调用此函数时如何生成正确的输入向量。

如何解决这个问题?

编辑:这是仅使用循环的另一个版本:

#include <vector>
#include <string>
#include <cmath>
#include <iostream>
using namespace std;
vector<string> make_combinations(
const vector<string>& variables, const vector<string>& operators)
{
vector<string> out;
if (variables.empty()) return out;
auto num_combs = pow(operators.size(), variables.size() - 1);
out.reserve(num_combs);
string current;
for (size_t i_comb = 0; i_comb < num_combs; i_comb++)
{
current += variables[0];
auto num = i_comb;
for (size_t i_var = 1; i_var < variables.size(); i_var++)
{
current += " ";
current += operators[num % operators.size()];
current += " ";
num /= operators.size();
current += variables[i_var];
}
out.push_back(current);
current.clear();
}
return out;
}
int main()
{
vector<string> variables = { "A", "B", "C", "D" };
vector<string> operators = { "+", "-" };
auto out = make_combinations(variables, operators);
for (const auto& str : out)
{
cout << str << endl;
}
return 0;
}

输出与以前相同,但顺序不同。


这是一个简单的递归算法来做到这一点:

#include <vector>
#include <string>
#include <cmath>
#include <iostream>
using namespace std;
void make_combinations_rec(
vector<string>::const_iterator varsIt, vector<string>::const_iterator varsEnd,
const vector<string>& operators, vector<string>& out, string& current)
{
// Add current variable
current += *varsIt;
auto rem = varsIt->size();
// Check if this was the last variable
if ((++varsIt) == varsEnd)
{
// Add complete expression
out.push_back(current);
}
else
{
// For every operator
for (auto& op : operators)
{
// Add operator surrounded by whitespace
current += " ";
current += op;
current += " ";
make_combinations_rec(varsIt, varsEnd, operators, out, current);
// Remove operator
current.erase(current.size() - op.size() - 2);
}
}
// Remove variable
current.erase(current.size() - rem);
}
vector<string> make_combinations(
const vector<string>& variables, const vector<string>& operators)
{
vector<string> out;
if (!variables.empty())
{
// Reserve space in advance
out.reserve(pow(operators.size(), variables.size() - 1));
string current;
make_combinations_rec(
variables.begin(), variables.end(), operators, out, current);
}
return out;
}
int main()
{
vector<string> variables = { "A", "B", "C", "D" };
vector<string> operators = { "+", "-" };
auto out = make_combinations(variables, operators);
for (const auto& str : out)
{
cout << str << endl;
}
return 0;
}

输出:

A + B + C + D
A + B + C - D
A + B - C + D
A + B - C - D
A - B + C + D
A - B + C - D
A - B - C + D
A - B - C - D

对于一种可能的解决方案,如果您将operators的大小视为数字基数,将variables的大小(减一)视为由基数表示的数字中的位数,可能会有所帮助。因此,对于"+""-"的例子,您有两个operators这意味着基数是2,这很简单,因为它是二进制算术。如果您有四个operators(通过添加例如"*""/")则基数4.使用基数创建的数字中的"数字"数(与您的示例一起)将为variables.size() - 13

使用数字创建排列,因此使用问题中的(二进制)示例

将是
000
001
010
011
100
101
110
111

然后使用此数字集合的数字作为转换,以获取每个变量之间的运算符。也就是说,0表示"+"operators[0]1表示"-"operators[1]

对于任何通用基数 B 和位数 D 解决这个问题有点困难,但这是一个古老而长期解决的问题,有大量的描述、算法和示例可用。

您希望所有排列都带有可用运算符的重复(也称为 n 元组)。编写一个泛型函数来生成给定长度的所有 n 元组非常容易。这是一个迭代解决方案:

template <typename T>
std::vector<std::vector<T>> n_tuples(std::initializer_list<T> a, std::size_t const n)
{
std::vector<std::vector<T>> tn = {{}};
for (std::size_t i = 0; i < n; ++i) {
auto tn_1 = std::move(tn);
for (auto const& x : a) {
for (auto const& t : tn_1) {
t.push_back(x);
tn.push_back(t);
}
}
}
return tn;
}

以下是您在情况下如何使用它:

int main()
{
std::vector<char> vars = {'A', 'B', 'C', 'D'};
for (auto const& ops : n_tuples({'+', '-'}, vars.size() - 1)) {
for (std::size_t i = 0; i < ops.size(); ++i) {
std::cout << vars[i] << " " << ops[i] << " ";
}
std::cout << vars.back() << "n";
}
}

这给出了输出:

A + B + C + D
A - B + C + D
A + B - C + D
A - B - C + D
A + B + C - D
A - B + C - D
A + B - C - D
A - B - C - D

请注意,n_tuples处理正确n == 0的情况(通过返回包含单个空向量 -0 元组的向量),因此即使使用单个变量,它也能很好地工作。