zoukankan      html  css  js  c++  java
  • Algs4-1.4.34B热还是冷-lgN

    1.4.34热还是冷。你的目标是猜出1到N之间的一个秘密的整数。每次猜完一个整数后,你会知道你的猜测和这个秘密整数是比较热(接近)还是比较冷(远离)。设计一个算法在~2lgN之内找到这个秘密整数,然后再设计一个算法在~1lgN之内找到这个秘密整数。
    答:解决2lgN中的不得不在每一次猜两个数就实现了lgN要求。 要达到最多使用lgN次猜数就可以猜出秘密数,那么要确保每一次猜数都能将秘密数所在的区间减半。
    算法:
          为了确保通过每一个猜数的温变信息能将秘密数所在区间减半,只要以秘密数所在区间的中点作为对称点,将当前猜数的对称数作为新的猜数即可。但新的猜数有时会不在1~N区间,如果题意要求猜数只能是1~N的数,不适用此算法。
        初始时秘密数区间为1~N,先猜1,秘密数是1时猜中,不是1时再猜N。秘密数是N时猜中,秘密数即不是1也不是N时,如果N相对1变冷说明秘密数离1近,那么秘密数在1~N/2区间,如果变热说明秘密数离N近,那么秘密数在N/2~N区间,由此将秘密数所在的区间减半,如果即不变冷也不变热,说明秘密数在区间的中间。
          区间在每一次猜数后缩减,每次只会对左右边界中的一个边界进行调整到达缩减效果,要么调整左边界,要么只调整右边界。变冷时调整的是离当前猜数近的边界,变热时调整的是离当前猜数远的边界。确定新的区间后找到新区间的中点,然后计算出中点与当前猜数的距离,然后再计算出新的猜数。
    图片

    public class E1d4d34B
    {
      public static void main(String[] args)
      {
        int N=Integer.parseInt(args[0]);
        int key=Integer.parseInt(args[1]);
        StdOut.printf(" N=%d,key=%d,guesskey=%d",N,key,guessKey(N,key));
      }//end main
     
      public static int guessKey(int N,int key)
      {
        int lo=1;
        int hi=N;
        int center;
        int distance=0;
        
        int g1=lo;
        int g2=hi;

        if (g1==key) return g1;
        while(true)
        {
          StdOut.printf("key=%d,lo=%d,hi=%d,g1=%d,g2=%d",key,lo,hi,g1,g2);
          if (g2==key)
             return g2;
          else if (Math.abs(g1-key)>Math.abs(g2-key))//hot  
          {
            StdOut.printf(",hot");
            if  (lo==farBoundary(lo,hi,g2))//变热时调整与当前猜数较远一点的边界
                  // 由于java中int值 3/2 的结果会是1,当区间在1~2,秘密数为2时,
                  //猜数在1的左边时变冷需要调整左边界,那么新的左边还是1,造成无限循环。
                 //所以当新边界值与旧边界值相同时,直接缩减1。
                 if(lo==(lo+hi)/2)
                     lo++;
                  else
                      lo=(lo+hi)/2;
             else
                  if(hi==(lo+hi)/2)
                      hi--;
                  else
                      hi=(lo+hi)/2;
             //
             center=(lo+hi)/2;//取新的区间的中心点值
             distance=distanceOfGuessToCenter(g2,center);//当前猜数与新区间中心点值的距离
             StdOut.printf(",distance=%d ",distance);
             g1=g2;
             g2=nextGuess(center, distance, g2);//下一个猜数是g2以中心点对称的数。
             }//end if hot
          else if(Math.abs(g1-key)<Math.abs(g2-key))//cold
          {
            StdOut.printf(",cold");
            if  (lo==nearBoundary(lo,hi,g2))//变冷时调整与当前猜数较近一点的边界
                    if(lo==(lo+hi)/2)
                       lo++;
                    else
                        lo=(lo+hi)/2;
            else
                    if(hi==(lo+hi)/2)
                        hi--;
                    else
                        hi=(lo+hi)/2;
             //
             center=(lo+hi)/2;
             distance=distanceOfGuessToCenter(g2,center);
             StdOut.printf(",distance=%d ",distance);
             g1=g2;
             g2=nextGuess(center, distance, g2);
          }//end if cold
       else
          {
            g1=g2;
            g2=(lo+hi)/2;
            StdOut.printf(",not change ");
          }
        }//end while
      }//end guessKey
     
      public static int nearBoundary(int lo,int hi,int guess)
      {
         if(Math.abs(guess-lo)<Math.abs(guess-hi))
            return lo;
         else
           return hi;
      }
        public static int farBoundary(int lo,int hi,int guess)
      {
         if(Math.abs(guess-lo)<Math.abs(guess-hi))
            return hi;
         else
           return lo;
      }
        
        public static int distanceOfGuessToCenter(int guess,int center)
        {
          return Math.abs(guess-center);
        }

       public static int nextGuess(int center,int distance,int currentGuess)
       {
           int nextGuess1=center+distance;
           int nextGuess2=center-distance;
           if (nextGuess1==currentGuess)
               return nextGuess2;
           else
               return nextGuess1;
       }
    }//end class

    下一个猜数的计算公式推导引起的代码变更:
    设:gn为下一个猜数,lo为左边界,hi为右边界,m为区间中点,g为当前猜数(对应代码中的g2),d为g与m的距离。
    当g与hi相近时,过程1得出gn=lo+hi-g
    当g与lo相近时,过程2得出gn=lo+hi-g
    图片
    两者相同,将上述代码调整后得到:
    public class E1d4d34C
    {
      public static void main(String[] args)
      {
        int N=Integer.parseInt(args[0]);
        int key=Integer.parseInt(args[1]);
        StdOut.printf(" N=%d,key=%d,guesskey=%d",N,key,guessKey(N,key));
      }//end main
     
      public static int guessKey(int N,int key)
      {
        int lo=1;
        int hi=N;
       
        int g1=lo;
        int g2=hi;

        if (g1==key) return g1;
        while(true)
        {
          StdOut.printf("key=%d,lo=%d,hi=%d,g1=%d,g2=%d",key,lo,hi,g1,g2);
          if (g2==key)
             return g2;
          else if (Math.abs(g1-key)>Math.abs(g2-key))//hot 
          {
            StdOut.printf(",hot ");
            if  (lo==farBoundary(lo,hi,g2))
                   // 由于java中int值 3/2 的结果会是1,当区间在1~2,秘密数为2时,
                  //猜数在1的左边时变冷需要调整左边界,那么新的左边还是1,造成无限循环。
                 //所以当新边界值与旧边界值相同时,直接缩减1。             
                 if(lo==(lo+hi)/2)
                     lo++;
                  else
                      lo=(lo+hi)/2;
             else
                  if(hi==(lo+hi)/2)
                      hi--;
                  else
                      hi=(lo+hi)/2;
             //
             g1=g2;
             g2=nextGuess(lo, hi, g2);//下一个猜数是g2以中心点对称的数。
             }//end if hot
          else if(Math.abs(g1-key)<Math.abs(g2-key))//cold
          {
            StdOut.printf(",cold ");
            if  (lo==nearBoundary(lo,hi,g2))//变冷时调整与当前猜数较近一点的边界
                    if(lo==(lo+hi)/2)
                       lo++;
                    else
                        lo=(lo+hi)/2;
            else
                    if(hi==(lo+hi)/2)
                        hi--;
                    else
                        hi=(lo+hi)/2;
             //
             g1=g2;
             g2=nextGuess(lo, hi, g2);
          }//end if cold
       else
          {
            g1=g2;
            g2=(lo+hi)/2;
            StdOut.printf(",not change ");
          }
        }//end while
      }//end guessKey
     
      public static int nearBoundary(int lo,int hi,int guess)
      {
         if(Math.abs(guess-lo)<Math.abs(guess-hi))
            return lo;
         else
           return hi;
      }
        public static int farBoundary(int lo,int hi,int guess)
      {
         if(Math.abs(guess-lo)<Math.abs(guess-hi))
            return hi;
         else
           return lo;
      }
       

       public static int nextGuess(int lo,int hi,int currentGuess)
       {
           return lo+hi-currentGuess;
       }
    }//end class
    以上代码冷热部分稍作调整后可以将相同代码合并。
    参考资料:
    1)http://stackoverflow.com/questions/25558951/hot-and-cold-binary-search-game
    图片
    2)http://www.ithao123.cn/content-10018711.html

    图片

    图片



  • 相关阅读:
    Android Framework 学习(八):屏幕刷新机制
    Android Framework 学习(七):Service启动原理
    Android Framework 学习(六):UI线程概念
    Android Framework 学习(五):Activity 启动流程
    Android Handler 机制(六):如何检测性能卡顿
    Android Handler 机制(五):ThreadLocal的工作原理
    Android Framework 学习(四):Binder机制与代理模式
    Android Framework 学习(三):Android 跨进程通信机制
    Android Handler 机制(四):屏障消息(同步屏障)
    Android Handler 机制(三):Handler 机制与管道 Pipe 机制
  • 原文地址:https://www.cnblogs.com/longjin2018/p/9854522.html
Copyright © 2011-2022 走看看