浮点数的二进制搜索/二分法

时间:2017-07-08 21:49:35

标签: algorithm floating-point binary-search numerics

很容易找到一个带二分搜索even if it can be arbitrarily large的整数:首先猜测数量级,然后继续划分间隔。 This answer描述了如何找到任意有理数。

设置场景后,我的问题是类似的:我们如何猜测IEEE 754浮点数?假设它不是NaN,但其他一切都是公平的游戏。对于每个猜测,您的程序将被告知相关数字是高,等或更低。最大限度地减少最坏情况下所需的猜测次数。

(这不是一个家庭作业。虽然,我可能会把它作为一个,如果事实证明有一个有趣的答案,不仅仅是“通过大量的特殊案件处理来击败浮动数字难以致死”。 )

编辑:如果我更擅长搜索,我可以找到answer ---但只有当你已经知道重新解释为int时才会有效(有一些警告)。所以留下这个。感谢哈罗德给出了一个很好的答案!

2 个答案:

答案 0 :(得分:4)

IEEE-754 64位浮点数实际上是64位表示。此外,除NaN值外,浮点比较和正值的整数比较之间没有差异。 (也就是说,符号位未设置的两位模式将产生相同的比较结果,无论您将它们比较为int64_t还是double,除非其中一个位模式是浮点NaN-。 )

这意味着您可以通过一次猜测一个位来找到64个猜测中的数字,即使该数字是±∞。首先将数字与0进行比较;如果目标是“较少”,则以与下面相同的方式产生猜测,但在猜测之前否定它们。 (由于IEEE-754浮点数是符号/幅度,您可以通过将符号位设置为1来否定数字。或者您可以执行正位模式重新解释,然后浮点否定结果。)

之后,一次猜一位,从最高位值开始。如果数字大于或等于猜测,则将该位设置为1;如果数字较小,则将该位设置为0;并继续下一个位,直到没有更多。要构造猜测,请将位模式重新解释为double

有两点需要注意:

  1. 您无法通过比较测试来区分±0。这意味着如果你的对手希望你区分它们,他们将不得不向你提供一种询问与-0相等的方法,并且在你明显确定数字为0之后你将不得不使用这种机制。 (这将发生在第64次猜测)。这将增加一个猜测,总共65个。

  2. 如果您确信目标不是NaN,则没有其他问题。如果它可能是一个NaN,你需要小心你的比较:如果你总是问“这个X 这个猜测吗?”事情将会很好,因为NaN比较总会返回false 。这意味着在连续11次“否”回答(不计算建立符号的那个)之后,你会发现自己猜测∞,假设如果数不小于∞,它必须相等。但是,在这种情况下单独你也需要明确地测试相等性,因为如果目标是NaN,那也将是假的。这不会给计数增加额外的猜测,因为它总会在64次猜测用完之前很久就会发生。

答案 1 :(得分:0)

可以将相同的方法应用于浮点数。更糟糕的案例运行时间是O(log n)。

public class GuessComparer
{
    private float random;
    public GuessComparer() // generate a random float and keep it private
    {
        Random rnd = new Random();
        var buffer = new byte[4];
        rnd.NextBytes(buffer);
        random = BitConverter.ToSingle(buffer, 0);
    }
    public int CheckGuess(float quess) // answer whether number is high, lower or the same.
    {
        return random.CompareTo(quess);
    }
}
public class FloatFinder
{

    public static int Find(GuessComparer checker)
    {
        float guess = 0;
        int result = checker.CheckGuess(guess);
        int guesscount = 1;
        var high = float.MaxValue;
        var low = float.MinValue;
        while (result != 0)
        {
            if (result > 0) //random is higher than guess
                low = guess;
            else// random is lower than guess

                high = guess;

            guess = (high + low) / 2;
            guesscount++;
            result = checker.CheckGuess(guess);
        }
        Console.WriteLine("Found answer in {0}", guesscount);
        return guesscount;
    }

    public static void Find()
    {
        var checker = new GuessComparer();
        int guesses = Find(checker);
    }
}