我做了一个小程序来帮助解决一个问题。就个人而言,我认为最好的方法是确定性的数学解决方案,但现在我缺乏咖啡因,甚至无法思考如何实现它。=)
相反,我采用了 SAR 方法。Stop and Reverse 是一种用于股票交易的技术 ( http://daytrading.about.com/od/stou/g/SAR.htm ),它被大量用于以最少的推理计算最优曲线。抛物线 SAR的Wikipedia 条目如下所示:
'抛物线 SAR 几乎是针对价格的每个趋势独立计算的。当价格处于上升趋势时,SAR 出现在价格下方并向上收敛。同样,在下降趋势中,SAR 出现在价格之上并向下收敛。
我根据您的问题对其进行了调整。我从你的系列中的一个随机值开始。然后代码进入有限次数的迭代。
我从系列堆栈中选择另一个随机值。如果新值加上堆栈总和低于目标值,则添加该值;如果优越,则减少。在满足条件(堆栈总和 = 目标)之前,我可以随心所欲地继续,或者如果循环找不到有效的解决方案,则中止。如果成功,我会记录堆栈和迭代次数。然后我重做一切。
一个非常粗略的代码如下。请原谅仓促。哦,它在 C# 中。=)
同样,它不能保证您将获得最佳路径;这是一种蛮力方法。可以细化;例如,检测目标命中是否完美匹配。
public static class SAR
{
//I'm considering Optimal as the smallest signature (number of members).
// Once set, all future signatures must be same or smaller.
private static Random _seed = new Random();
private static List<int> _domain = new List<int>() { 100, 80, 66, 24, 4, 2, 1 };
public static void SetDomain(string domain)
{
_domain = domain.Split(',').ToList<string>().ConvertAll<int>(a => Convert.ToInt32(a));
_domain.Sort();
}
public static void FindOptimalSAR(int value)
{
// I'll skip some obvious tests. For example:
// If there is no odd number in domain, then
// it's impossible to find a path to an odd
// value.
//Determining a max path run. If the count goes
// over this, it's useless to continue.
int _maxCycle = 10;
//Determining a maximum number of runs.
int _maxRun = 1000000;
int _run = 0;
int _domainCount = _domain.Count;
List<int> _currentOptimalSig = new List<int>();
List<String> _currentOptimalOps = new List<string>();
do
{
List<int> currSig = new List<int>();
List<string> currOps = new List<string>();
int _cycle = 0;
int _cycleTot = 0;
bool _OptimalFound = false;
do
{
int _cursor = _seed.Next(_domainCount);
currSig.Add(_cursor);
if (_cycleTot < value)
{
currOps.Add("+");
_cycleTot += _domain[_cursor];
}
else
{
// Your situation doesn't allow for negative
// numbers. Otherwise, just enable the two following lines.
// currOps.Add("-");
// _cycleTot -= _domain[_cursor];
}
if (_cycleTot == value)
{
_OptimalFound = true;
break;
}
_cycle++;
} while (_cycle < _maxCycle);
if (_OptimalFound)
{
_maxCycle = _cycle;
_currentOptimalOps = currOps;
_currentOptimalSig = currSig;
Console.Write("Optimal found: ");
for (int i = 0; i < currSig.Count; i++)
{
Console.Write(currOps[i]);
Console.Write(_domain[currSig[i]]);
}
Console.WriteLine(".");
}
_run++;
} while (_run < _maxRun);
}
}
这是调用者:
String _Domain = "100, 80, 66, 25, 4, 2, 1";
SAR.SetDomain(_Domain);
Console.WriteLine("SAR for Domain {" + _Domain + "}");
do
{
Console.Write("Input target value: ");
int _parm = (Convert.ToInt32(Console.ReadLine()));
SAR.FindOptimalSAR(_parm);
Console.WriteLine("Done.");
} while (true);
这是我对几个目标进行 100k 次迭代后的结果,给定了一个稍微修改的系列(出于测试目的,我将 25 换成了 24):
SAR for Domain {100, 80, 66, 24, 4, 2, 1}
Input target value: 50
Optimal found: +24+24+2.
Done.
Input target value: 29
Optimal found: +4+1+24.
Done.
Input target value: 75
Optimal found: +2+2+1+66+4.
Optimal found: +4+66+4+1.
Done.
现在使用您的原始系列:
SAR for Domain {100, 80, 66, 25, 4, 2, 1}
Input target value: 50
Optimal found: +25+25.
Done.
Input target value: 75
Optimal found: +25+25+25.
Done.
Input target value: 512
Optimal found: +80+80+66+100+1+80+25+80.
Optimal found: +66+100+80+100+100+66.
Done.
Input target value: 1024
Optimal found: +100+1+80+80+100+2+100+2+2+2+25+2+100+66+25+66+100+80+25+66.
Optimal found: +4+25+100+80+100+1+80+1+100+4+2+1+100+1+100+100+100+25+100.
Optimal found: +80+80+25+1+100+66+80+80+80+100+25+66+66+4+100+4+1+66.
Optimal found: +1+100+100+100+2+66+25+100+66+100+80+4+100+80+100.
Optimal found: +66+100+100+100+100+100+100+100+66+66+25+1+100.
Optimal found: +100+66+80+66+100+66+80+66+100+100+100+100.
Done.
缺点:值得再次提及:此算法不保证您会找到最佳值。它进行了蛮力近似。
优点:快。100k 次迭代最初可能看起来很多,但该算法在检测到越来越多的优化路径后开始忽略长路径,因为它减少了最大允许的循环数。