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

    图片

    图片



  • 相关阅读:
    LeetCode Binary Tree Inorder Traversal
    LeetCode Populating Next Right Pointers in Each Node
    LeetCode Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode Reverse Linked List II
    LeetCode Populating Next Right Pointers in Each Node II
    LeetCode Pascal's Triangle
    Palindrome Construct Binary Tree from Preorder and Inorder Traversal
    Pascal's Triangle II
    LeetCode Word Ladder
    LeetCode Binary Tree Zigzag Level Order Traversal
  • 原文地址:https://www.cnblogs.com/longjin2018/p/9854522.html
Copyright © 2011-2022 走看看