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

削減されていない分数が必要な場合は、次の1つの最適化を実行できます。2n/ 4、4n / 8、または1024n / 2048が必要なため、n / 2には関心がないためいくつかチェックするだけで済みます。数字。2の倍数をチェックするとすぐに、2をチェックする必要はありません。したがって、で分母を試すことができ、これらの数値のすべての要素を暗黙的にチェックしていると思います。denominator_maxdenominator_max/22denominator_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

これを試してみましょう:

まず、フロートを分数に変換する必要があります。これを行うために私が考える最も簡単な方法は、イプシロンの大きさのオーダーを見つけ、フロートにそのオーダーを掛け、切り捨てて分子を取得することです。

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 に答える