创建变量之间的运算符排列
Creating permutations of operators between variables
假设我有以下输入向量
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() - 1
或3
。
使用数字创建排列,因此使用问题中的(二进制)示例
将是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 元组的向量),因此即使使用单个变量,它也能很好地工作。
- 比较并显示使用最小值(a,b)和最大值(a、b)升序排列的4个数字
- 为什么比较运算符如此快速
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 使用C++中的模板和运算符重载执行矩阵运算
- 为什么这个运算符<重载函数对 STL 算法不可见?
- 为什么不;名字在地图上是按顺序排列的吗
- 增量运算符与后缀混淆
- 一个关于在C++中重载布尔运算符的问题
- C++优先级队列,按对象的唯一指针的特定方法升序排列
- 运算符C++ "delete []"仅删除 2 个前值
- 模板类无法识别友元运算符
- 我可以使用条件运算符初始化C风格的字符串文字吗
- 按对象的特定方法按升序排列的C++优先级队列
- 关闭||运算符优化
- 通过继承类使用来自不同命名空间的运算符
- C++Cast运算符过载
- 如何使用AngelScript注册SFML Vector2运算符
- 重载元组索引运算符-C++
- 如何使用重载的相等(==)运算符向测试用例添加描述
- 创建变量之间的运算符排列