号码搜索(效率最高)

时间:2017-05-04 13:26:47

标签: java random numbers

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

  1. 猜测是正确的;
  2. 猜测太大,所以你应该猜一个较小的数字;
  3. 猜测太小,所以你应该猜一个更大的数字。
  4. 在案例3中,N的值将增加P,其中P是另一个随机数(范围1到200)。

    如果初始值N = 800且P = 150。你猜是按以下顺序:  Example

    如何编码以下代码,尤其是涉及两个数字(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);
    

3 个答案:

答案 0 :(得分:1)

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

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}

[注意:基于快速模拟,多次运行的平均值约为115,因此效率改进平均应从115步减少]
[注意:代码的变化量不同,每次猜测都太低; OP的评论可能暗示增加是随机选择一次,在这种情况下,上述代码中猜测数量的增加需要改变]

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

在看到霍尔格的建议之前,我尝试了一些基本的调整;然后我还试图实现他的算法。但是,我没有找到进行显着改进的调整(有些情况更糟)。

使用100,000次运行,标准二进制搜索平均为127.7步(注意:与我之前基于较低运行次数的估计略有差异)。假设我正确地实现了Holger的算法,在100,000时,平均值为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;
            }

我很好奇其他人是否有比直接二进制搜索更好的实现。

有趣的是,带有标准二进制搜索的1..1000范围平均需要8个步骤,其中O(log n )复杂度。通过允许猜测改变,它将平均值移动大约120步。

答案 1 :(得分:0)

一旦我理解了你想要做的事情,我就重新设计了我的解决方案。这将为您提供一些统计数据。对于每个猜测,当前解决方案包含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);
}

输出:

Average attempts (of 1000000 tests): 266
Most attempts in one run: 72228
Least attempts in one run: 1

答案 2 :(得分:0)

您可以尝试执行类似于二分查找的操作。只需考虑二进制搜索需要对输入进行排序。如果输入没有排序,你必须自己排序。

不要猜测一个随机数,而只是猜测一个正好位于分区中间的数字。然而,与每次减半的二分搜索相比,在这种情况下它是一个移动目标,因此需要调整搜索范围。