В вашем вопросе я вижу две важные вычислительные концепции. Первый - двоичный поиск, второй - рекурсия. Поскольку это домашнее задание, вот вклад в понимание двоичного поиска, рекурсии и того, как думать о них.
Подумайте о бинарном поиске, разделив решение «пространство» пополам, сохранив половину решения и выполнив его последовательно, чтобы процесс сходился в решении.Ключевыми понятиями для осуществления этого является то, что вам нужно спроектировать решение «пространство», которое имеет следующие свойства:
1) могут быть разделены, как правило, в два раза или, по крайней мере, две части
2) двух штук после деления, есть способ определить, какая половина имеет решение, чтобы процесс можно было повторить только на одну половину.
Рекурсия включает функцию (метод в разговоре O-O), вызывающую себя. Рекурсия отлично работает для процесса, который сходится к выводу. Он либо повторяется навсегда, либо до тех пор, пока у вас не закончится какой-то ресурс, обычно память, и он смертельно остановится. Двумя ключевыми понятиями для рекурсии являются:
1) конвергенция через некоторую инвариантность (подробнее об инвариантности ниже).
2) условие прекращения (которое признает достаточную сходимость).
Теперь, для вашей рутины с квадратным корнем. Требования к подпрограмме:
1) Целочисленный ввод.
2) Целочисленное квадратичное приближение, которое дает целое число, самое близкое к фактическому квадратному корню.
3) Используйте рекурсию.
4) Используйте двоичный поиск.
Это помогает знать математику о квадратных корнях для этого. Также полезны концепции элементарного исчисления и аналитической геометрии. Давайте сделаем некоторые рассуждения.
У нас есть произвольное положительное целое число x. Нам нужен его корень y. Если мы выберем тестовое значение для y, мы увидим, является ли он корнем x, если y * y = x. Если y слишком велико, y * y> x. если y слишком мало, y * y < x. Мы также знаем, что 0 < = корень < = x и что квадратные корни 0 и 1 тривиально равны нулю и 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. Итак, давайте найдем новый y, скажем y1, между x/y и y в качестве нового тестового значения. Среднее значение x/y и y будет выполнено. y1 = (x/y0 + y0)/2 даст y1, который ближе к квадратному корню из x, чем y0, если y0 слишком велико.
Сходится ли это? Ну, в математике, использующей положительные действительные числа, среднее значение всегда будет выше значения, но приближается к каждой итерации. Это удовлетворяет условию, что мы последовательно разделяем решение «пространство» на две части и знаем, какой из них сохранить. В этом случае мы последовательно вычисляем новые значения ниже предыдущих и ниже которых ответ остается, позволяя отбросить все значения выше нового. Мы останавливаемся, когда достигаем условия, при которых не существует новых значений над ответом. Однако использование компьютеров приводит к бинарным приближениям действительных чисел. С целыми числами в делении происходит усечение. Это может повлиять на конвергенцию выгодно или неблагоприятно. Кроме того, ваш ответ должен быть самым большим целым числом, меньшим или равным квадратному корню. Разумно взглянуть на схожесть, которую мы получим.
Вследствие целочисленного деления, y1 = (x/y0 + y0)/2 будет сходиться до тех пор, пока последующие итерации не достигнут целочисленного корня или значения пола для (то есть наибольшего целого числа меньше) корня. Это идеально. Если мы начнем с предлагаемого значения для корня, который должен быть больше, чем корень, скажем, самого х, первое значение для yn, где yn * yn < = x - желаемый результат.
Простой ответ заключается в том, что, когда мы начинаем с y0> y, первый новый yn, который меньше или равен y, тогда y - yn < 1. То есть yn теперь является значением пола, для которого мы мы смотрели, и теперь у нас есть условие завершения, которое точно удовлетворяет условиям для требуемого ответа.
Вот основные итеративные и рекурсивные решения. Решения не включают функции безопасности, чтобы отрицательные значения не вводились для x. Одна из главных проблем заключается в том, чтобы избежать деления на ноль, если кто-то хочет найти квадратный корень из 0. Так как это тривиальный ответ, как рекурсивные, так и итеративные методы возвращают 0 до деления на ноль. И рекурсивные, и итеративные решения работают с тривиальными случаями для нахождения квадратных корней 0 и 1.
Существует еще один анализ, который всегда должен выполняться с помощью int и длинной арифметики в Java. Основная проблема заключается в переполнении целых чисел, поскольку Java ничего не делает о int или long overflow. Переполнение приводит к значениям двух добавочных значений (посмотрите, что в другом месте), что может привести к фиктивным результатам, и Java не генерирует исключений с int или long overflow.
В этом случае легко избежать арифметики, которая может привести к внутреннему переполнению с большими значениями 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. Однако мы всегда можем начинать со значения, меньшего х, что гарантировано> у. x/2 работает для всех значений x> 1. Так как квадратный корень из x, где x равен либо 0, либо 1, просто x, мы можем легко проверить эти значения и просто вернуть правильное и тривиальное значение. Вы можете создать код для предотвращения использования значений < 0 или значений> Integer.MAX_VALUE. То же самое можно применить, если мы используем long вместо int. Добро пожаловать на компьютер в реальном мире!
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
Вы можете проверить рекурсивное решение, чтобы узнать, сколько экземпляров будет результат на стек кадра, но вы увидите, что она сходится очень быстро. Интересно видеть, что итеративное решение намного меньше и быстрее, чем рекурсивное, что часто бывает не так, и поэтому рекурсия используется, когда можно предсказать, что ресурсы стека достаточны для глубины рекурсии.
Если это домашнее задание, он должен быть помечен как таковые. (У меня нет репутации, чтобы сделать это.) – AlcubierreDrive
, а также user209260. lol – 2010-09-22 03:21:28