我有一个复杂的算法来计算函数f(x)的结果。在现实世界中,f(x)是连续函数。然而,由于算法中的舍入误差,计算机程序中不是这种情况。下图给出了一个示例:
此外,我有一个包含数千个值的列表。
我正在寻找满足Fi值的所有x值,即f(xi)= Fi
我可以通过简单地迭代x值来解决这个问题,如下面的伪代码:
for i=0 to NumberOfChecks-1 do
begin
//calculate the function result with the algorithm
x=i*(xmax-xmin)/NumberOfChecks;
FunctionResult=CalculateFunctionResultWithAlgorithm(x);
//loop through the value list to see if the function result matches a value in the list
for j=0 to NumberOfValuesInTheList-1 do
begin
if Abs(FunctionResult-ListValues[j])<Epsilon then
begin
//mark that element j of the list matches
//and store the corresponding x value in the list
end
end
end
当然有必要使用大量的支票。否则我会错过一些x值。检查次数越多,结果越完整和准确。该列表完成90%或95%是可以接受的。
问题在于这种强力方法需要花费太多时间。正如我之前提到的,f(x)的算法非常复杂,检查次数很多,需要花费太多时间。
对于这个问题,什么是更好的解决方案?
答案 0 :(得分:3)
另一种方法是分两部分:生成所有结果,对它们进行排序,然后与现有结果的排序列表合并。
第一步是计算所有结果并将其与生成它们的x
值一起保存。那就是:
results = list of <x, result>
for i = 0 to numberOfChecks
//calculate the function result with the algorithm
x=i*(xmax-xmin)/NumberOfChecks;
FunctionResult=CalculateFunctionResultWithAlgorithm(x);
results.Add(x, FunctionResult)
end for
现在,按results
对FunctionResult
列表进行排序,并按结果对FunctionResult-ListValues
数组进行排序。
您现在有两个可以线性移动的排序列表:
i = 0, j = 0;
while (i < results.length && j < ListValues.length)
{
diff = ListValues[j] - results[i];
if (Abs(diff) < Episilon)
{
// mark this one with the x value
// and move to the next result
i = i + 1
}
else if (diff > 0)
{
// list value is much larger than result. Move to next result.
i = i + 1
}
else
{
// list value is much smaller than result. Move to next list value.
j = j + 1
}
}
答案 1 :(得分:2)
对列表进行排序,生成包含的数组SortedListValues
排序的ListValues
和数组SortedListValueIndices
包含每个条目的原始数组中的索引
SortedListValues
。你实际上只需要第二个和
您可以通过对数组进行排序来创建单个排序
(value,index)的元组使用value作为排序键。
在0..NumberOfChecks-1
中迭代您的范围并计算。{
每一步的函数值,然后使用二进制斩
在排序列表中搜索它的方法。
的伪代码:
// sort as described above
SortedListValueIndices = sortIndices(ListValues);
for i=0 to NumberOfChecks-1 do
begin
//calculate the function result with the algorithm
x=i*(xmax-xmin)/NumberOfChecks;
FunctionResult=CalculateFunctionResultWithAlgorithm(x);
// do a binary chop to find the closest element in the list
highIndex = NumberOfValuesInTheList-1;
lowIndex = 0;
while true do
begin
if Abs(FunctionResult-ListValues[SortedListValueIndices[lowIndex]])<Epsilon then
begin
// find all elements in the range that match, breaking out
// of the loop as soon as one doesn't
for j=lowIndex to NumberOfValuesInTheList-1 do
begin
if Abs(FunctionResult-ListValues[SortedListValueIndices[j]])>=Epsilon then
break
//mark that element SortedListValueIndices[j] of the list matches
//and store the corresponding x value in the list
end
// break out of the binary chop loop
break
end
// break out of the loop once the indices match
if highIndex <= lowIndex then
break
// do the binary chop searching, adjusting the indices:
middleIndex = (lowIndex + 1 + highIndex) / 2;
if ListValues[SortedListValueIndices[middleIndex] < FunctionResult then
lowIndex = middleIndex;
else
begin
highIndex = middleIndex;
lowIndex = lowIndex + 1;
end
end
end
可能的并发症:
答案 2 :(得分:2)
当然,这在很大程度上取决于数据,尤其是Fi的数字分布。另一个问题是f(x)看起来非常跳跃,消除了&#34;假设附近值的概念&#34;。
但是可以优化搜索。
下图。
答案 3 :(得分:2)
Jim Mischel的解决方案可以在O(i + j)中使用,而不是您目前拥有的O(i * j)解决方案。但是,他的代码中存在(非常)小错误。正确的代码是:
diff = ListValues[j] - results[i]; //no abs() here
if (abs(diff) < Episilon) //add abs() here
{
// mark this one with the x value
// and move to the next result
i = i + 1
}
答案 4 :(得分:-1)
最好的方法将转发函数f(x)的性质。
最佳解决方案是,如果您可以将翻转创建为F(x)
并使用它
如你所说F(x)
是连续的:
因此,您可以开始评估少量远点,然后查找有意义的范围,并优化f(x)=Fi
的x的“假设”
它不是防弹,但它是一种选择。
e.g。 Fi=5.7; f(1)=1.4 ,f(4)=4,f(16)=12.6, f(10)=10.1, f(7)=6.5, f(5)=5.1, f(6)=5.8
,您可以5 < x < 7
在与#1相同的行上,并且IF F(x)
难以计算,您可以使用插值,然后仅在可能的值处评估F(x)
。