Java 数字搜索(最有效)

Java 数字搜索(最有效),java,random,numbers,Java,Random,Numbers,假设N是一个随机数(范围从1到1000)。我们需要猜测N,对于每个猜测,可能会给出以下反馈之一: 猜测是正确的 猜测太大,所以你应该猜一个较小的数字 猜测太小,所以你应该猜测一个更大的数字 在情况3中,N的值将增加p,其中p是另一个随机数(范围1到200) 如果初始值N=800,p=150。您可以按照以下顺序进行猜测: 如何编写以下代码,尤其是当它涉及两个数字(N和p)时。我曾考虑使用二进制搜索,但如果我们不知道P的值,这将是一个问题 这是我目前的代码: myGuess=0; checkCod

假设N是一个随机数(范围从1到1000)。我们需要猜测N,对于每个猜测,可能会给出以下反馈之一:

  • 猜测是正确的
  • 猜测太大,所以你应该猜一个较小的数字
  • 猜测太小,所以你应该猜测一个更大的数字
  • 在情况3中,N的值将增加p,其中p是另一个随机数(范围1到200)

    如果初始值N=800,p=150。您可以按照以下顺序进行猜测:

    如何编写以下代码,尤其是当它涉及两个数字(N和p)时。我曾考虑使用二进制搜索,但如果我们不知道P的值,这将是一个问题

    这是我目前的代码:

    myGuess=0;
    checkCode=0;
    int lower = 1, upper = 999;
    myGuess = (lower+upper)/2;
    
    do{
        if (checkCode == 2) {
        upper = myGuess - 1;
        }
    else if (checkCode == 3){
        lower = myGuess + 1;
        upper += ran.nextInt(200);  //Need to guess the P value
        }
    
        myGuess = (lower+upper)/2;
    }while(checkCode!=1);
    

    一旦我明白你想做什么,我就重新设计了我的解决方案。这会给你一些统计数字。对于每个猜测,当前解决方案包含一个介于0和13之间的随机数,以及将下限和上限相加并除以2。为什么是13岁?这似乎是完成这项任务的最佳时机

    public static void main(String args[]) throws IOException {
    
        int numTests = 1000000;
        long averageTries = 0;
        int maxAttempts = 0;
        int minAttempts = Integer.MAX_VALUE;
    
        for (int i = 0; i < numTests; i++) {
            int numAttempts = 0;
            int answer = (int) (Math.random() * 1000) + 1;
            int lower = 1;
            int upper = 1000;
            int myGuess;
    
            do {
                myGuess = (int) (((lower + upper) / 2) + (Math.random() * 14));
                numAttempts++;
    
                if (myGuess > answer) {
                    upper = myGuess;
                } else if (myGuess < answer) {
                    lower = myGuess;
                    upper += (lower + upper) / 2;
                    answer += (int) (Math.random() * 200) + 1;
                }
    
            } while (myGuess != answer);
            averageTries += numAttempts;
    
            if (numAttempts > maxAttempts) {
                maxAttempts = numAttempts;
            }
            if (numAttempts < minAttempts) {
                minAttempts = numAttempts;
            }
        }
        System.out.println("Average attempts (of " + numTests + " tests): " + (averageTries / numTests));
        System.out.println("Most attempts in one run: " + maxAttempts);
        System.out.println("Least attempts in one run: " + minAttempts);
    }
    

    第一步是获得一个工作猜测系统。这段代码提供了二进制搜索方法的大致指南。第二步是分析如何提高效率。(注意:可以恢复部分S.O.P()以查看进度)

    [注:根据快速模拟,多次运行的平均值约为115,因此效率改进应平均从115个步骤减少]
    [注:代码中的更改量因每次猜测过低而不同;OP的评论可能会建议随机选择一次增加,在这种情况下,上述代码中要猜测的数量的增加需要更改]

    编辑:
    从逻辑上讲,如果猜测低的移动第一是猜测,那么使用某种偏向于选择高的似乎是合乎逻辑的。正如霍尔格在各种评论中所建议的那样,有一些方法可以进行调整

    在看到霍尔格的建议之前,我尝试了一些基本的调整;然后我还尝试实现他的算法。然而,我还没有发现有明显的改善(有些更糟)

    使用100000次运行,标准的二进制搜索平均127.7步(注:与我之前基于较低运行计数的估计略有上升)。假设我正确地实现了霍尔格算法,100000步的平均值是126.6步

    由于我缺乏进一步研究的数学技能(不幸的是目前还没有时间),因此简单的修改似乎不会从根本上改变平均算法的效率。我没有调查更严重的案件。在Math StackExchange上问这个问题会很有趣,看看他们是否能提供任何明确的输入。我做了一个快速的谷歌搜索,但没有时间阅读可能会带来一些改进的学术论文(同样,在速度和算法复杂性方面存在未知的权衡)

    当然,我可能没有正确地执行霍尔根的建议。以下是我直接从注释中使用的代码(如果猜数太低,则替换猜数计算中的更改):

                  if (tryHolgen) {
                    double risc = 200.0/(upperBound-lowerBound);
                    if (risc <= 1) {
                        guess = (upperBound + lowerBound) /2;
                    }
                    else {
                        guess = upperBound - 
                          Math.max((int)((upperBound - lowerBound)/risc/2),1);
                    }
                  else {
                    guess = (lowerBound + upperBound) / 2;
                }
    
    if(tryHolgen){
    双risc=200.0/(上界下界);
    
    如果(RISC

    )你可以尝试做类似于二进制搜索的事情。只要考虑二进制搜索需要输入排序。如果输入没有排序,你必须自己排序。


    而不是猜测一个随机数,只要猜测一个正好在分区的中间,但是与二进制搜索相比,每次都减半,在这种情况下它是一个移动的目标,所以搜索的边界需要为此调整。

    <代码>上+= RAN。NEXT(200)
    这一行对我来说毫无意义。你为什么认为你需要在这里更改
    上限呢?如果你的数字太小,你将更新下限,而不是别的。你应该为
    检查code
    定义命名常量,而不是直接使用
    1
    2
    3
    ,读者必须这样做猜测。我不确定当你的猜测太低时P是否应该改变。事实上,你的示例建议P用一个随机值初始化一次。在哪里设置过
    N
    (即要猜测的数字)?在哪里设置checkCode=1的代码(因此实际上已经猜到了数字).有没有任何迹象表明,在猜得太小之后,真正的上界会是什么(或者必须假设上界实际上移动了200,即使要猜的数字移动了1..200)。在这种情况下,您可能会调整二进制搜索,以选择较高的猜测值,而不是范围的中间值,以减少增加的可能性。但是,“上界增加”与“要猜测的数字已更改”是完全不同的说法。它们之间没有因果关系,因此您不能使用两者来“解释”另一个。虽然这个答案确实提供了二进制搜索的实现,并解决了OP糟糕的代码示例中缺少的细节,但它并没有回答效率问题。在更新边界时包含答案有什么意义?边界是根据您的猜测而设置的,其他什么都没有。@Tom它没有-我想一开始它是需要的,但是没有它就完全可以了。@AlgorithmR,谢谢你的代码实现。我会尝试一下,如果可行的话会更新你。@AlgorithmR,变量N无法设置或获取。在你的代码实现中,你不知怎的使用了“answer+=(lower+upper)/2”这是我第一次使用stackoverflow和WOW!事实上,我对每个人回答问题的速度都感到震惊,尤其是在编码和输出方面。你们是谁?机器人。哈哈。#很多人都测试了你们的代码实现,它按预期工作。我以前的代码(平均步数:150)不知怎么地工作了sa
    private static int doGuess()
    {
        int lowerBound = 1;
        int upperBound = 1000;
        int numberToGuess = ThreadLocalRandom.current().nextInt(upperBound) + 1;
        int guess = 0;
        int steps = 0;
        int increases = 0;
    
        while (guess != numberToGuess) {
            ++steps;
    
            guess = (lowerBound + upperBound) / 2;
    
    //            System.out.printf("[%5d] Guessing %d (is: %d)%n",
    //                    steps,
    //                    guess,
    //                    numberToGuess);
    
            if (guess == numberToGuess) {
                System.out.printf("Guessed %d in %d steps (%d increases)%n",
                        numberToGuess,
                        steps,
                        increases);
                continue;
            }
            else if (guess > numberToGuess) {
    //                System.out.println("Guess is too high!");
                // adjust upper bound to be guess
                upperBound = guess;
            }
            else {
    //                System.out.println("Guess is too low; changing number");
                numberToGuess += ThreadLocalRandom.current().nextInt(200) + 1;
    
                // adjust lower bound to this guess
                lowerBound = guess;
    
                // the number moved, so adjust upper bound by max range
                upperBound += 200;
    
                // track increases
                ++increases;
            }
        }
    
        return steps;
    }
    
    public static void main(String[] args)
    {
        List<Integer> steps = new ArrayList<>();
        int iterations = 10;
    
        for (int i = 0; i < iterations; ++i) {
            steps.add(doGuess());
        }
    
        IntSummaryStatistics stats = 
                steps.stream().collect(IntSummaryStatistics::new,
                        IntSummaryStatistics::accept,
                        IntSummaryStatistics::combine);
    
        System.out.println(stats);
    }
    
    Guessed 8838 in 145 steps (83 increases)
    Guessed 6301 in 106 steps (59 increases)
    Guessed 3239 in 58 steps (30 increases)
    Guessed 5785 in 109 steps (58 increases)
    Guessed 2547 in 56 steps (27 increases)
    Guessed 16071 in 300 steps (164 increases)
    Guessed 3847 in 54 steps (31 increases)
    Guessed 3125 in 42 steps (24 increases)
    Guessed 6708 in 93 steps (57 increases)
    Guessed 7433 in 143 steps (74 increases)
    IntSummaryStatistics{count=10, sum=1106, min=42, average=110.600000, max=300}
    
                  if (tryHolgen) {
                    double risc = 200.0/(upperBound-lowerBound);
                    if (risc <= 1) {
                        guess = (upperBound + lowerBound) /2;
                    }
                    else {
                        guess = upperBound - 
                          Math.max((int)((upperBound - lowerBound)/risc/2),1);
                    }
                  else {
                    guess = (lowerBound + upperBound) / 2;
                }