假设我有一个目标编号和一个可能值的列表,我可以选择这些值来创建一个序列,该序列一旦与每个选取的数字相加,便会求和到目标:
target = 31
list = 2, 3, 4
possible sequence: 3 2 4 2 2 2 4 2 3 2 3 2
我想:
这是我的attempt:
#include <iostream>
#include <random>
#include <chrono>
#include <vector>
inline int GetRandomInt(int min = 0, int max = 1) {
uint64_t timeSeed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
std::seed_seq ss{ uint32_t(timeSeed & 0xffffffff), uint32_t(timeSeed >> 32) };
std::mt19937_64 rng;
rng.seed(ss);
std::uniform_int_distribution<int> unif(min, max);
return unif(rng);
}
void CreateSequence(int target, std::vector<int> &availableNumbers) {
int numAttempts = 1;
int count = 0;
std::vector<int> elements;
while (count != target) {
while (count < target) {
int elem = availableNumbers[GetRandomInt(0, availableNumbers.size() - 1)];
count += elem;
elements.push_back(elem);
}
if (count != target) {
numAttempts++;
count = 0;
elements.clear();
}
}
int size = elements.size();
std::cout << "count: " << count << " | " << "num elements: " << size << " | " << "num attempts: " << numAttempts << std::endl;
for (auto it = elements.begin(); it != elements.end(); it++) {
std::cout << *it << " ";
}
}
int main() {
std::vector<int> availableNumbers = { 2, 3, 4 };
CreateSequence(31, availableNumbers);
}
但是如果数字列表不适合达到这样的总和,它可以无限循环。例如:
std::vector<int> availableNumbers = { 3 };
CreateSequence(8, availableNumbers);
3的序列的总和不等于8。此外,如果列表很大且目标数很高,则可能导致大量处理(导致很多检查失败)。
您将如何实现这种算法?
答案 0 :(得分:2)
您的建议代码可能很快速,因为它是启发式的。但是正如您所说,它可能陷入几乎无限循环中。
如果要避免这种情况,则必须搜索整套可能的组合。
让我们将算法定义为函数f
,并以标量目标t
和向量<b>
作为返回系数向量<c>
的参数,其中{{1} }和<b>
具有相同的尺寸:
<c>
首先,应将给定的数字集<c> = f(t, <b>)
简化为缩减集Sg
,以便我们减小解矢量Sr
的维数。例如。 <c>
可以减少为{2,3,4,11}
。我们通过以下方式来递归调用算法:将{2,3}
拆分为新的目标Sg
,其余数字作为新的给定集合ti
,并询问算法是否找到任何解决方案(非零向量)。如果是这样,请从原始给定集合Sgi
中删除该目标ti
。递归地重复此操作,直到找不到解决方案为止。
现在,我们可以将这组数字理解为多项式,在这里我们正在寻找可能的系数Sg
以获取目标ci
。让我们用t
来调用Sb
bi
中的每个元素。
我们的测试总和i={1..n}
是ts
的所有i
的总和,其中每个ci * bi
的范围可以从ci
到0
。
可能的测试数量ni = floor(t/bi)
是所有N
:ni+1
上的乘积。
现在通过将系数向量N = (n1+1) * (n2+1) * ... * (ni+1)
表示为整数向量并递增<c>
并遍历向量中的下一个元素,重置c1等等,对所有可能性进行迭代。 / p>
c1
该代码打印
#include <random>
#include <chrono>
#include <vector>
#include <iostream>
using namespace std;
static int evaluatePolynomial(const vector<int> &base, const vector<int> &coefficients)
{
int v=0;
for(unsigned long i=0; i<base.size(); i++){
v += base[i]*coefficients[i];
}
return v;
}
static bool isZeroVector(vector<int> &v)
{
for (auto it = v.begin(); it != v.end(); it++) {
if(*it != 0){
return false;
}
}
return true;
}
static vector<int> searchCoeffs(int target, vector<int> &set) {
// TODO: reduce given set
vector<int> n = set;
vector<int> c = vector<int>(set.size(), 0);
for(unsigned long int i=0; i<set.size(); i++){
n[i] = target/set[i];
}
c[0] = 1;
bool overflow = false;
while(!overflow){
if(evaluatePolynomial(set, c) == target){
return c;
}
// increment coefficient vector
overflow = true;
for(unsigned long int i=0; i<c.size(); i++){
c[i]++;
if(c[i] > n[i]){
c[i] = 0;
}else{
overflow = false;
break;
}
}
}
return vector<int>(set.size(), 0);
}
static void print(int target, vector<int> &set, vector<int> &c)
{
for(unsigned long i=0; i<set.size(); i++){
for(int j=0; j<c[i]; j++){
cout << set[i] << " ";
}
}
cout << endl;
cout << target << " = ";
for(unsigned long i=0; i<set.size(); i++){
cout << " +" << set[i] << "*" << c[i];
}
cout << endl;
}
int main() {
vector<int> set = {4,3,2};
int target = 31;
auto c = searchCoeffs(target, set);
print(target, set,c);
}
答案 1 :(得分:0)
按照ihavenoidea的建议,我还将尝试回溯。此外,我将按降序对数字进行排序,以加快处理速度。
注意:评论比答案更合适,但我不允许这样做。希望能帮助到你。如果需要,我将禁止回答。