13

二分探索を使用して、負でない入力整数の平方根 (最も近い整数に切り捨て) を再帰的に計算するプログラムを作成するのに助けが必要です。

これは私がこれまでに持っているものです:

import java.util.Scanner;

public class Sqrt {

  public static void main(String[] args) {

    Scanner console = new Scanner(System.in);

    System.out.print("Enter A Valid Integer: ");

    int value = console.nextInt();

    calculateSquareRoot(value);

  }

    public static int calculateSquareRoot(int value) {
      while (value > 0) {
      double sqrt = (int) Math.sqrt(value);
      System.out.println(sqrt);
    }
    return -1;
    }
}

平方根を計算するためにバイナリ検索を使用する必要があるという事実は、私を混乱させている部分です。これを行う方法について誰か提案があれば、大歓迎です。ありがとうございました

4

8 に答える 8

28

Teh codez:

def sqrt(n):
  low = 0
  high = n+1
  while high-low > 1:
    mid = (low+high) / 2
    if mid*mid <= n:
      low = mid
    else:
      high = mid
  return low

それを理解するには、ループ不変条件、つまり次のことを考えてみてください。

低<=n<高高

このコードを理解していれば、再帰バージョンを書くのは簡単なはずです。

于 2010-09-22T04:39:14.417 に答える
11

このJavaメソッドを使用できます(反復)

public class Solution {
    // basic idea is using binary search
    public int sqrt(int x) {
        if(x == 0 || x == 1) {
            return x;
        }
        int start = 1, end = x / 2;
        while(start <= end) {
            int mid = start + (end - start) / 2;
            if(mid == x / mid) {
                return mid;
            }
            if(mid < x / mid) {
                start = mid + 1;
            } else {
                end = mid - 1;
            }
        }

        return start - 1;
    }
}

独自の再帰メソッドを駆動できます

于 2015-03-19T20:31:37.240 に答える
10

本質的には、二分探索を使用して答えに近づくことができるという考えです。

たとえば、入力として 14 が与えられたとします。次に、14 の平方根が 0 から 14 の間にあることを確認します。つまり、0 と 14 が現在の「境界」です。これらの 2 つの終点を 2 等分し、中間点 7 を取得します。次に、候補として 7 を試します。7 の 2 乗が 14 より大きい場合、新しい境界 (0,7) が得られます。そうしないと、新しい境界 (7,14) が作成されます。

答えに「十分に近づく」まで、この二等分を繰り返し続けます。たとえば、14-0.01 と 14+0.01 の範囲内にある数の二乗がある場合、それを答えとして宣言します。

OK、HW にはそれだけのヒントで十分なはずです。StackOverflow を引用することを忘れないでください。

于 2010-09-22T03:09:37.247 に答える
4

これは宿題だと思いますので、ヒントだけを示します。

二分探索を行うには、可能な正しい値の中央値にできるだけ近い点を選びます。したがって、問題は、平方根の典型的な中央値とは何かということになります。これは定数であるか、乗算によって計算できます。明らかに、任意の定数を使用してもほとんどの入力では機能しないため、入力に定数を掛けて推測する必要があります。

乗算する定数 C の値については、入力として期待する値に基づいて選択する必要があります。たとえば、入力が約 250,000 になると予想される場合、次のようになります。

C * 250,000 ~= sqrt(250,000)
C = sqrt(250,000) / 250,000
C = 500 / 250,000
C = 1 / 500
于 2010-09-22T03:03:59.043 に答える
4

あなたの質問には、コンピューティングの重要な概念が 2 つあります。1 つ目は二分探索、2 つ目は再帰です。これは宿題なので、二分探索、再帰、およびそれらについての考え方を理解するための貢献を以下に示します。

二分探索は、ソリューションの「スペース」を半分に分割し、ソリューションが入っている半分を保持し、それを連続して実行して、プロセスがソリューションに収束することと考えてください。これを行うための重要な概念は、次のプロパティを持つソリューション「スペース」を設計する必要があるということです。

1) 細分化することができ、通常は半分または少なくとも 2 つに分割できます

2) 分割後の 2 つのピースのうち、どちらの半分に解があるかを判断する方法があるため、プロセスを半分だけで繰り返すことができます。

再帰には、それ自体を呼び出す関数 (OO で言えばメソッド) が含まれます。再帰は、結論に収束するプロセスに対して非常にうまく機能します。永久に再帰するか、何らかのリソース (通常はメモリ) がなくなるまで再帰し、致命的に停止します。再帰の 2 つの重要な概念は次のとおりです。

1) 何らかの不変性による収束 (不変性については以下で詳しく説明します)。

2) 終了条件 (十分な収束を認めるもの)。

さて、あなたの平方根ルーチンのために。ルーチンの要件は次のとおりです。

1) 整数入力。

2) 実際の平方根に最も近いフロア整数を与える整数平方根近似。

3) 再帰を使用します。

4) 二分探索を使用します。

これを行うには、平方根に関する数学を知っておくと役立ちます。初等微積分と解析幾何学の概念も役に立ちます。いくつかの推論をしましょう。

任意の正の整数 x があります。そのルート y が必要です。y のテスト値を選択すると、y * y = x の場合、それが x の根であるかどうかがわかります。y が大きすぎる場合、y * y > x。y が小さすぎる場合、y * y < x。また、0 <= root <= x であり、0 と 1 の平方根は自明に 0 と 1 であることもわかっています。y * y <= x の最大の整数 (つまり、フロア値) を探しているので、次のようになります。それも説明します。

ここで役立ついくつかの数学的推論があります。x = y * y であることがわかっています。ここで、y は x の平方根です。つまり、y = x/y です。

うーん... y が大きすぎて x の平方根にならない場合はどうなりますか? 次に: x < y * y および: x/y < y これは、x/y も小さすぎて x の平方根にならないことを意味します。したがって、y が大きすぎる場合、x/y < x の平方根 < y であることがわかります。では、新しいテスト値として x/y と y の間の新しい y、たとえば y1 を見つけてみましょう。x/y と y の平均で十分です。y1 = (x/y0 + y0)/2 は、y0 が大きすぎる場合、y0 よりも x の平方根に近い y1 を返します。

これは収束しますか?まあ、正の実数を使用する数学では、平均は常に値を上回りますが、反復ごとに近づきます。これは、解「空間」を連続して 2 つの部分に分割し、どちらを保持するかを知るという条件を満たします。この場合、以前の値よりも下に新しい値を計算し、それより下にはまだ答えがあるため、新しい値よりも上のすべての値を破棄できます。答えを超える新しい値が存在しない状態に到達すると停止します。ただし、コンピューターを使用すると、実数のバイナリ近似が得られます。整数では、除算で切り捨てがあります。これは、収束に有利または不利に影響する可能性があります。さらに、答えは平方根以下の最大の整数になるはずです。これ'

整数除算のターンケーションのため、y1 = (x/y0 + y0)/2 は、連続する反復が整数ルートまたはルートの下限値 (つまり、ルートより小さい最大の整数) に到達するまで収束します。これが理想です。根よりも大きくなければならない根の提案された値、たとえば x 自体から始めると、yn * yn <= x である yn の最初の値が望ましい結果になります。

簡単な答えは、y0 > y から始めると、y 以下の最初の新しい yn であり、次に y - yn < 1 になります。これで、必要な回答の条件を正確に満たす終了条件が得られました。

ここでは、基本的な反復および再帰ソリューションを示します。ソリューションには、x に負の値が入力されないようにするための安全機能は含まれていません。1 つの大きな懸念事項は、誰かが 0 の平方根を見つけたい場合に備えてゼロ除算を避けることです。それは些細な答えなので、再帰的方法と反復的方法の両方がゼロによる除算が行われる前に 0 を返します。再帰的解法と反復解法はどちらも、0 と 1 の平方根を見つけるための自明なケースで機能します。

Java では常に int および long 演算を使用して実行する必要がある別の分析があります。Java は int または long オーバーフローについて何もしないため、大きな懸念事項は整数オーバーフローです。オーバーフローは 2 の補数の値になり (他の場所で調べてください)、偽の結果につながる可能性があり、Java は int または long オーバーフローで例外をスローしません。

この場合、x の大きな値で内部オーバーフローが発生する可能性がある演算を回避するのは簡単です。y0 * y0 < x などの終了条件を作成すると、x が Integer.MAX_VALUE の平方根よりも大きい場合、中間値である y0 * y0 が最大の int 値をすぐに超えるため、オーバーフローの危険があります。ただし、終了条件を y0 < x / y0 に並べ替えることができます。計算にはまだ問題があります: ((x / y0) + y0) / 2) x と y0 が Integer.MAX_VALUE の場合、Integer.MAX_VALUE + 1 を試行するためです。 > y であることが保証されている x。x / 2 は、x > 1 のすべての値に対して機能します。x が 0 または 1 の場合、x の平方根は単に x であるため、これらの値を簡単にテストして、正しい自明な値を返すことができます。値 < 0 または値 > Integer.MAX_VALUE を使用しないようにコードを作成できます。int の代わりに long を使用する場合も同じことが適用できます。現実世界でのコンピューティングへようこそ!

public static int intSqRootRecursive (int x) {
    // square roots of 0 and 1 are trivial and x / 2 for
    // the y0 parameter will cause a divide-by-zero exception
    if (x == 0 || x == 1) {
        return x;
    }
    // starting with x / 2 avoids overflow issues
    return intSqRootRecursive (x, x / 2);
} // end intSqRootRecursive

private static int intSqRootRecursive(int x, int y0) {
    // square roots of 0 and 1 are trivial
    // y0 == 0 will cause a divide-by-zero exception
    if (x == 0 || x == 1) {
        return x;
    } // end if
    if (y0 > x / y0) {
        int y1 = ((x / y0) + y0) / 2;
        return intSqRootRecursive(x, y1);
    } else {
        return y0;
    } // end if...else
} // end intSqRootRecursive

public static int intSqRootIterative(int x) {
    // square roots of 0 and 1 are trivial and
    // y == 0 will cause a divide-by-zero exception
    if (x == 0 || x == 1) {
        return x;
    } // end if
    int y;
    // starting with y = x / 2 avoids overflow issues
    for (y = x / 2; y > x / y; y = ((x / y) + y) / 2);
    return y;
} // end intSqRootIterative

再帰的なソリューションをテストして、フレーム スタックで生成されるインスタンスの数を調べることができますが、収束が非常に速いことがわかります。興味深いのは、反復ソリューションが再帰ソリューションよりもはるかに小さくて高速であることです。これは多くの場合そうではなく、スタック リソースが再帰の深さに対して十分であると予測できる場合に再帰が使用される理由です。

于 2012-08-14T22:22:56.357 に答える
3

二分探索を使用した Java での再帰的ソリューションは次のとおりです。

public class FindSquareRoot {

    public static void main(String[] args) {
        int inputNumber = 50;
        System.out.println(findSquareRoot(1, inputNumber, inputNumber));
    }

    public static int findSquareRoot(int left, int right, int inputNumber){

        // base condition
        if (inputNumber ==0 || inputNumber == 1){
            return inputNumber;
        }

        int mid = (left + right)/2;

        // if square of mid value is less or equal to input value and 
        // square of mid+1 is less than input value. We found the answer. 
        if (mid*mid <= inputNumber && (mid+1)*(mid+1) > inputNumber){
            return mid;
        }

        // if input number is greater than square of mid, we need 
        // to find in right hand side of mid else in left hand side.
        if (mid*mid < inputNumber){
            return findSquareRoot(mid+1, right, inputNumber);
        }
        else{
            return findSquareRoot(left, mid-1, inputNumber);
        }

    }
}
于 2015-08-20T05:59:51.980 に答える
1

反復バイナリ ソリューション:

public static double sqrt(int n) {

    double low = 0;
    double high = n;
    double mid = (high - low) / 2;

    while (Math.abs((mid * mid) - n) > 0.000000000001) {
        if ((mid * mid) > n) {

            high = mid;
            mid = (high - low) / 2;

        } else{

            low = mid;
            mid = mid + ((high - low) / 2);

        }
    }
    return mid;
}
于 2013-01-14T07:58:13.560 に答える