冷热二进制搜索游戏

时间:2014-08-28 22:23:38

标签: algorithm binary-search

热或冷。

我认为你必须进行某种二分搜索,但我不确定如何。

  

你的目标是猜测一个介于1和N之间的秘密整数。你   反复猜测1和N之间的整数。每次猜测后你都会学习   如果它等于秘密整数(并且游戏停止);除此以外   (从第二次猜测开始),您将了解猜测是否更热   (更接近)或更密切(远离)秘密号码比你的   以前猜测。设计一种在lg中查找密码的算法   N + O(1)猜测。

     

提示:设计一种解决lg N + O问题的算法(1)   假设你被允许猜测-N范围内的整数   到2N。

我一直在绞尽脑汁,我似乎无法想出一个lg N + O(1)。

我发现了这个:http://www.ocf.berkeley.edu/~wwu/cgi-bin/yabb/YaBB.cgi?board=riddles_cs;action=display;num=1316188034但是无法理解图表,也没有描述其他可能的情况。

6 个答案:

答案 0 :(得分:2)

假设您知道您的密码整数在[a,b],并且您的上一次猜测是c

您希望将间隔除以2,并知道您的秘密整数是否位于[a,m][m,b]之间,m=(a+b)/2

诀窍是猜d(c+d)/2 = (a+b)/2

不失一般性,我们可以假设d大于c。然后,如果dc更热,则您的秘密整数将大于(c+d)/2 = (a+b)/2 = m,因此您的密码整数将位于[m,b]。如果dc更冷,则您的秘密整数将属于[a,m]

您需要能够在-N2N之间进行猜测,因为您无法保证上面定义的cd始终为{{1} }。您的第一个猜测可能是[a,b]1

所以,你在每次猜测时将你的间隔除以2,所以复杂度是log(N)+ O(1)。

一个简短的例子来说明这一点(随机选择的结果):

N

编辑,@ tmyklebu建议:

我们仍然需要证明我们的猜测总是落在Guess Result Interval of the secret number 1 *** [1 , N ] // d = a + b - c N cooler [1 , N/2 ] // N = 1 + N - 1 -N/2 cooler [N/4 , N/2 ] //-N/2 = 1 + N/2 - N 5N/4 hotter [3N/8, N/2 ] // 5N/4 = N/4 + N/2 + N/2 -3N/8 hotter [3N/8, 7N/16] //-3N/8 = 3N/8 + N/2 - 5N/4 . . . . . . . . . . . .

之下

通过重复发生,假设[-N,2N](我们之前的猜测)位于c

然后[a-(a+b), b+(a+b)] = [-b,a+2b]d = a+b-c <= a+b-(-b) <= a+2b

初始案例:d = a+b-c >= a+b-(a+2b) >= -ba=1, b=N, c=1确实在c

QED

答案 1 :(得分:2)

这是2010年IOI的一项任务,我参加了东道主科学委员会。 (我们要求的是最佳解决方案,而不仅仅是lg N + O(1),而后面的内容并不是最优的。)

不在-N .. 2N之外摆动并使用lg N + 2猜测是直截了当的;你需要做的只是表明二进制搜索的明显翻译。

如果您的某些内容无法在-N .. 2N之外摆动并进行lg N + 2猜测,请执行以下操作:

猜猜N/2,然后N/2+1。这告诉你答案所在的数组的哪一半。然后猜测那个半数组的结尾。你要么处于两个“中间”区域之一,要么就是两个“终点”区域之一。如果你处于中间位置,那么先做一件事,然后在lg N + 4猜测中获胜。结局有点棘手。

假设我需要在1 .. K中猜测一个数字,而不会偏离1 .. N之外,而我的最后一次猜测是1。如果我猜K/2而我更冷,那我接下来猜1;我花了两个猜测得到一个类似的子问题,大小是1/4。如果K/2更热,我知道答案在K/4 .. K。接下来猜猜K/2-1。这两个子句是K/4 .. K/2-1K/2 .. K,两者都很好。但是我花了三个猜测(在最坏的情况下)将问题的大小减半;如果我这样做,我最终会做lg N + 6猜测。

答案 2 :(得分:0)

解决方案接近二进制搜索。在每个步骤中,您都有一个数字可以处于的间隔。从整个时间间隔[1, N]开始。首先猜测两端 - 即数字1N。其中一个将更接近,因此您将知道您现在搜索的数字是[1, N/2][N/2 + 1, N](考虑到N,为简单起见)。现在你进入下一步,间隔时间缩小两倍。继续使用相同的方法。请记住,您已经探测过其中一个目标,但这可能不是您最后的猜测。

我不确定lg N + O(1)你的意思,但我建议的方法将执行O(log(N))操作,在最坏的情况下,它会完全记录log 4 (N)探针。

答案 3 :(得分:0)

在这里,我的两分钱就是这个问题,因为我已经沉迷了两天。我不会对别人已经说过的话说些什么新东西,但是我会以一种可能让一些人轻易理解解决方案的方式来解释它(或者至少那是我的方式设法理解它。)

从~2 lg N解决方案中提取,如果我知道[a, b]中存在解决方案,我想知道它是否在左半边[a, (a + b) / 2]或右边一半[(a + b) / 2, b],点(a + b) / 2将两半分开。那我该怎么办?我想a然后b;如果我对b感到寒冷,我知道我在第一个(左)的一半,如果我变得更热,我知道我在第二个(右)的一个。因此,猜测ab是了解相对于中点(a + b) / 2的秘密整数位置的方法。但是ab并不是我能猜到的唯一知道秘密位置的点。 (a - 1, b + 1)(a - 2, b + 2),...等都是有效的点对,以便知道秘密位置,因为所有这些对的中点都是(a + b) / 2,中间点是原始间隔[a, b]。事实上,任何两个数字cd都可以使用(c + d) / 2 = (a + b) / 2

因此,考虑[a, b]作为我们知道秘密整数存在于其中的区间,将c作为我们猜测的最后一个数字。我们想要确定关于中点(a + b) / 2的秘密的位置,因此我们要猜测一个新的数字d以知道(a + b) / 2的秘密相对位置。我们怎么知道这个号码d?通过求解方程(c + d) / 2 = (a + b) / 2,得到d = a + b - c。根据{{​​1}}进行猜测,我们会根据答案(较冷或较热)适当缩小范围d,然后重复此过程,将[a, b]作为我们的最后一次猜测并尝试对数字进行新的猜测d例如具有相同的条件。

要确定初始条件,我们应该从ea = 1b = N开始。我们在c = 1猜测建立一个参考(因为第一个猜测不能告诉你任何有用的东西,因为没有先前的猜测)。然后,我们继续进行新的猜测并根据每个猜测调整封闭间隔。 @R2B2's answer中的表格解释了这一切。

但是在尝试编写此解决方案时,您必须保持警惕。当我尝试在python中对其进行编码时,我首先遇到了错误,即c卡住[a, b]时它很小(如[a, a + 1]),a和{{} 1}}会向内移动。我不得不对循环外部的间隔大小为2的情况进行分阶段处理并单独处理它们(就像我对间隔大小为1的情况一样)。

答案 4 :(得分:0)

任务是脑筋急转弯 但我会尽量保持简单

  1. 为解决2lnN中的问题,我们进行以下猜测: 1和N:确定哪一个是最热的一半(1,N / 2)或(N / 2,N),例如(N / 2,N)是最热的一半,那么让我们进行下一个猜测 N / 2和N:再次确定哪一个更热(N / 2,3 / 4N)或(3 / 4N,N),依此类推 ...因此,每半个部分需要2个猜测,因此我们应进行2 * lnN个猜测。 在这里,我们看到每次我们需要重复上一个间隔的边界时再重复一次-在我们的示例中,点“ N”被重复。

  2. 为解决1 * lnN猜想而不是2 * lnN的问题,我们需要找到一种方法来对每个间隔的一半划分只花一个猜想,该方法的很好的插图在{ {3}} 这样做的想法是避免在后续步骤中再次重复每个间隔的边界点之一,但要聪明地通过镜像点为每个半划分步骤花费一个猜测。 聪明的主意是,当我们要确定当前间隔的哪一半更热时,我们不必仅尝试其边界,但是我们也可以尝试位于其边界之外的任何点,这些猜测点必须相距相等(换句话说,是镜像的)相对于有趣间隔的中心,即使这些猜测点为负(即<0)或> N,也没什么不好(这不受任务条件的限制) 因此要猜测这个秘密,我们可以使用间隔为(-N,2N)的pounts来自由地进行猜测

答案 5 :(得分:0)

public class HotOrCold {
    static int num=5000;// number to search
    private static int prev=0;

    public static void main(String[] args) {
        System.out.println(guess(1,Integer.MAX_VALUE));
        
    }
    public static int guess(int lo,int hi) {
        while(hi>=lo) {
            boolean one=false;
            boolean two=false;
            if(hi==lo) {
                return lo;
            }
            if(isHot(lo)) {
                one=true;
            }
            if(isHot(hi)) {
                two=true;
            }
            if(!(one||two)) {
                return (hi+lo)/2;// equal distance
            }
            if(two) {
                lo=hi-(hi-lo)/2;
                continue;//checking two before as it's hotter than lo so ignoring the lo
            }
            if(one) {
                hi=lo+(hi-lo)/2;
            }
            
        }
        
        return 0;
        
    }

    public static boolean isHot(int curr) {
        boolean hot=false;
        if(Math.abs(num-curr)<Math.abs(num-prev)) {
            hot=true;
        }
        prev=curr;
        return hot;
    }
}