4

我正在尝试在仅接受整数买入/卖出金额的市场上进行与准确汇率匹配的货币交易。我想以特定的比率进行最大的交易。这是一个玩具程序,不是真正的交易机器人,所以我使用的是 C#。

我需要一种算法,即使分子和分母可能很大(100000+),也能在合理的时间内返回答案。

static bool CalcBiggestRationalFraction(float target_real, float epsilon, int numerator_max, int denominator_max, out int numerator, out int denominator)
{
    // target_real is the ratio we are tryig to achieve in our output fraction (numerator / denominator)
    // epsilon is the largest difference abs(target_real - (numerator / denominator)) we are willing to tolerate in the answer
    // numerator_max, denominator_max are the upper bounds on the numerator and the denominator in the answer
    //
    // in the case where there are multiple answers, we want to return the largest one
    //
    // in the case where an answer is found that is within epsilon, we return true and the answer.
    // in the case where an answer is not found that is within epsilon, we return false and the closest answer that we have found.
    //
    // ex: CalcBiggestRationalFraction(.5, .001, 4, 4, num, denom) returns (2/4) instead of (1/2).


}

在考虑我实际尝试的内容之前,我问了一个类似的问题(http://stackoverflow.com/questions/4385580/finding-the-closest-integer-fraction-to-a-given-random-real)完成,事实证明我正在尝试解决一个不同但相关的问题。

4

3 回答 3

6

解决您的问题的规范方法是使用连分数展开。特别是,请参阅本节

于 2010-12-08T20:50:40.487 回答
2

如果你想要未减少的分数,那么你可以做一个优化:因为你永远不会对n/2感兴趣,因为你想要2n/44n/81024n/2048,我们只需要检查一些号码。只要我们检查 2 的任何倍数,我们就不需要检查 2。因此,我相信您可以尝试denominator_max通过的分母denominator_max/2,并且您将隐式检查这些数字的所有因数,这将是2通过的所有内容denominator_max/2

我现在不在编译器,所以我没有检查这段代码的正确性,甚至没有检查它是否可以编译,但它应该很接近。

static bool CalcBiggestRationalFraction(float target_real, float epsilon, 
    int numerator_max, int denominator_max, 
    out int numerator, out int denominator)
{
    if((int)Math.Round(target_real * denominator_max) > numerator_max)
    {
        // We were given values that don't match up.
        // For example, target real = 0.5, but max_num / max_den = 0.3
        denominator_max = (int)(numerator_max / target_real);
    }

    float bestEpsilon = float.MAX_VALUE;
    for(int den = denominator_max; den >= denominator_max/2, den--)
    {
        int num = (int)Math.Round(target_real * den);
        float thisEpsilon = Math.abs(((float)num / den) - target_real);
        if(thisEpsilon < bestEpsilon)
        {
            numerator = num;
            denominator = den;
            bestEpsilon = thisEpsilon;
        }
    }

    return bestEpsilon < epsilon;
}
于 2010-12-08T20:13:53.713 回答
1

让我们试试这个:

首先,我们需要把浮点数变成分数。我能想到的最简单的方法是找到 epsilon 的数量级,将浮点数乘以该顺序,然后截断以获得分子。

long orderOfMagnitude = 1
while(epsilon * orderOfMagnitude <1)
   orderOfMagnitude *= 10;

numerator = (int)(target_real*orderOfMagnitude);
denominator = orderOfMagnitude;

//sanity check; if the initial fraction isn't within the epsilon, then add sig figs until it is
while(target_real - (float)numerator / denominator > epsilon)
{
   orderOfMagnitude *= 10;
   numerator = (int)(target_real*orderOfMagnitude);
   denominator = orderOfMagnitude;
}

现在,我们可以将分数分解为最少的项。我所知道的最有效的方法是尝试除以小于或等于分子和分母中较小者的平方根的所有素数。

var primes = new List<int>{2,3,5,7,11,13,17,19,23}; //to start us off

var i = 0;
while (true)
{
   if(Math.Sqrt(numerator) < primes[i] || Math.Sqrt(denominator) < primes[i]) break;

   if(numerator % primes[i] == 0 && denominator % primes[i] == 0)
   {
      numerator /= primes[i];
      denominator /= primes[i];
      i=0;
   }
   else
   {
      i++;
      if(i > primes.Count)
      {
        //Find the next prime number by looking for the first number not divisible
        //by any prime < sqrt(number).
        //We are actually unlikely to have to use this, because the denominator
        //is a power of 10, so its prime factorization will be 2^x*5^x
        var next = primes.Last() + 2;
        bool add;
        do
        {
           add = true;
           for(var x=0; primes[x] <= Math.Sqrt(next); x++)
              if(next % primes[x] == 0)
              {
                add = false;
                break;
              }

           if(add)
              primes.Add(next);
           else
              next+=2;   
        } while(!add);
      }
   }
}
于 2010-12-08T20:33:06.807 回答