Что не так с моим синтаксисом и эффективно ли я это делаю?

Я пытаюсь создать метод, который сообщит мне, будет ли число простым или нет, верно или ложно. вот код:

class prime
{

    public static boolean prime (int a, int b) 
    { 
        if (a == 0) 
        {
            return false; 
        }
        else if (a%(b-1) == 0) 
        {
            return false; 
        }
        else if (b>1) 
        {
            prime (a, b-1) ;
        }
        else
        {
            return true; 
        }

    }

    public static void main (String[] arg) 
    {
        System.out.println (prime (45, 45)) ; 
    }
}

когда я пытаюсь скомпилировать это, я получаю это сообщение об ошибке:

prime.java:23: missing return statement
    }
    ^
1 error

Я мог неправильно истолковать то, что говорится в сообщении об ошибке, но мне кажется, что отсутствует отсутствующий оператор возврата, поскольку у меня есть оператор возврата для каждого возможного набора условий. если a равно 0, то он возвращает false, если нет, то он проверяет, делится ли a на b, если да, то он возвращает, если нет, то если b больше 1, он начинает сначала. если b не больше 1, он также возвращается.

  • Также кажется немного запутанным, чтобы заставить этот метод принимать два целых числа, которые являются одним и тем же целым числом.

  • Что не так с моим синтаксисом/почему я получаю сообщение об ошибке? Есть ли способ сделать так, чтобы метод, который я использую в основном, должен был принимать только один int (возможно, другой метод разбивает этот int на два клона, которые затем передаются в public static boolean primeproper?

  • или есть более эффективный способ сделать это, что я полностью отсутствует?


person David    schedule 10.03.2010    source источник
comment
Дэвид, я бы посоветовал просмотреть некоторые учебные пособия на веб-сайте Java. Выполните поиск в Google для java и нажмите на первый результат. Вам нужно немного поработать над основами, прежде чем вы попытаетесь заняться рекурсивными искателями простых чисел. Обратите особое внимание на вызовы методов и структуры управления.   -  person Jonathon Faust    schedule 10.03.2010
comment
это не техническая домашняя работа в том, что я учу сам. Это не для школы, а для себя. Я уже использую книгу, должен ли я переключиться на учебники по Java или остаться с книгой, которую я использую?   -  person David    schedule 10.03.2010
comment
Если у вас есть книга, держитесь за нее. Настойчивость окупится. Возможно, вам больше всего поможет использование IDE, которая будет указывать на ошибки по мере их совершения. Вы смотрели на Netbeans или Eclipse?   -  person Jonathon Faust    schedule 10.03.2010
comment
Я установил Эклипс. я понятия не имею, как его использовать, хотя.   -  person David    schedule 10.03.2010


Ответы (8)


В вашей функции prime есть четыре возможных пути кода, один из которых не возвращает ничего. Это то, на что жалуется сообщение об ошибке. Вам необходимо заменить:

prime (a, b-1) ;

с:

return prime (a, b-1) ;

в случае else if (b>1).

Сказав это, на самом деле это не очень хороший способ вычислить, является ли число простым. Проблема в том, что каждый рекурсивный вызов выделяет кадр стека, и вы столкнетесь с серьезными проблемами переполнения стека, если попытаетесь выяснить, является ли 99 999 999 простым числом?

Рекурсия — очень хороший инструмент для решения определенного подмножества задач, но вы должны знать о глубине стека. Что касается более эффективных решений, существует множество тестов, которые вы можете выполнить, чтобы определить, что число не простое, а затем проверить остальные только методом грубой силы.

Одна вещь, о которой вы должны знать, это сначала проверить делимость на меньшие числа, так как это уменьшит область поиска быстрее. И не используйте деление там, где подойдет умножение, умножение обычно быстрее (хотя и не всегда).

И некоторые, возможно, хитрые трюки в духе:

  • каждое число, кроме 2, оканчивающееся на 2, 4, 6, 8 или 0, не является простым.
  • каждое число, кроме 5, оканчивающееся на 5, не является простым. Одни только эти два правила сократят пространство поиска на 60%. Предполагая, что вы получили свой тестовый номер в виде строки, проверить последнюю цифру этой строки несложно даже до преобразования в целочисленный тип.

Есть несколько более сложных правил для проверки делимости. Если вы возьмете число, кратное 9, и просуммируете все цифры, чтобы получить новое число, затем сделаете это снова с этим числом, а затем продолжите, пока не получите одну цифру, вы обнаружите, что это всегда 9.

Это даст вам еще 10% сокращения пространства для поиска, хотя и с более затратной по времени проверкой. Имейте в виду, что эти проверки выгодны только для действительно больших чисел. Преимущества не так велики, скажем, для 32-битных целых чисел, поскольку там предварительно вычисленное растровое изображение было бы намного эффективнее (см. ниже).

Для упрощения начала я бы использовал следующее итеративное решение:

public static boolean prime (int num) {
    int t = 2;
    while (t * t <= num) {
        if ((num % t) == 0) {
            return false;
        }
        t++;
    }
    return true;
}

Если вам нужна реальная скорость в вашем коде, вообще не вычисляйте ее каждый раз. Вычислите его один раз, чтобы создать битовый массив (это сделает один из методов sieve) всех простых чисел в интересующем вас диапазоне, а затем просто проверьте свои значения по этому битовому массиву.

Если вам даже не нужны затраты на вычисление массива при каждом запуске вашей программы, сделайте это один раз и сохраните битовый массив в файл на диске, загружая его при запуске вашей программы.

На самом деле у меня есть список первых 100 миллионов простых чисел в файле, и мне проще и быстрее использовать grep, чтобы определить, является ли число простым, чем запускать код для его вычисления :-)


Что касается того, почему ваш алгоритм (исправленный оператором return) настаивает на том, что 7 не является простым, он будет настаивать на том, что каждое число не является простым (не проверял отрицательные числа, я почти уверен они вызовут серьезные проблемы - ваша первая проверка, вероятно, должна быть if (a < 1) ...).

Давайте посмотрим, что происходит, когда вы вызываете prime(3,3).

В первый раз он попадает в третье условие, поэтому вызывает prime(3,2).

Затем выполняется второе условие, поскольку 3 % (2-1) == 0 истинно (N % 1 всегда 0).

Поэтому он возвращает ложь. Вероятно, это можно исправить, изменив третье условие на else if (b>2), хотя я не проверял это тщательно, так как в любом случае не думаю, что рекурсивное решение - хорошая идея.


Следующий полный фрагмент кода сделает то, что вам нужно, хотя я ценю ваше любопытство и желание узнать, что вы сделали не так. Это знак того, кто действительно собирается стать хорошим резчиком кода.

public class prime
{
    public static boolean isPrime (int num) {
        int t = 2;
        while (t * t <= num) {
            if ((num % t) == 0) {
                return false;
            }
            t++;
        }
        return true;
    }


    public static void main (String[] arg) 
    {
        System.out.println (isPrime (7)) ; 
    }
}
person paxdiablo    schedule 10.03.2010
comment
что значит пока? а что значит т++? Также не должно ли num%t всегда быть = 0, поскольку num/t=t? - person David; 10.03.2010
comment
@David, «пока» будет продолжать цикл до тех пор, пока условие истинно (пока t не будет больше квадратного корня из числа, где вам гарантируется, что число не может быть точным кратным t). t++ добавляет 1 к t. И num%t будет равно 0 только в том случае, если num точно кратно t, т. е. число не простое. - person paxdiablo; 10.03.2010

Похоже, у вас сложилось впечатление, что, поскольку рекурсия в конечном итоге найдет базовый случай, который попадет в оператор возврата, то этот возврат будет всплывать через все рекурсивные вызовы. Это не правда. Каждый рекурсивный вызов должен выдавать такой результат:

return prime(a, b - 1);
person tom    schedule 10.03.2010
comment
хорошо, но когда я делаю это изменение, он думает, что 7 не простое число. что происходит не так? - person David; 10.03.2010
comment
Дэвид: когда b равно 2, выражение a%(b-1) == 0 истинно для всех a, поэтому оно всегда возвращает false. - person Chris Dodd; 10.03.2010

Если b больше, чем 1, ваша функция ничего не вернет.

Может быть return prime (a, b-1) ; ?

person Phil Rykoff    schedule 10.03.2010
comment
То есть вы должны использовать return prime (a, b-1) ; вместо просто prime (a, b-1) ; - person David; 10.03.2010
comment
но если b>1, то он снова будет проходить через простое число, пока не окажется, что b>1. - person David; 10.03.2010
comment
также это не заставляет его работать правильно. если я это сделаю, то он говорит, что 7 не является простым. 7 является простым. - person David; 10.03.2010
comment
Том хорошо выразил это в своем ответе. Вам по-прежнему необходимо явно возвращать результат вызова метода Prime(...). Каждый вызов метода должен достигать оператора return, а не просто достигать его в конце концов. - person David; 10.03.2010
comment
(Это сбивает с толку. Я Дэвид из комментариев № 1 и № 4, а не спрашивающий). Если логика неверна, это другая проблема. Чтобы решить проблему с синтаксисом, вам просто нужно поставить return перед вызовом prime(...). - person David; 10.03.2010
comment
я знаю, что неправильная логика - это другая проблема, но мне интересно, есть ли у вас какие-либо идеи, что с этим делать? - person David; 10.03.2010
comment
Ах я вижу. Я думаю, что ответ paxdiablo содержит полезную информацию о способах улучшения вашего алгоритма. - person David; 10.03.2010
comment
это так, но это не объясняет, почему мне говорят, что 7 является простым числом, что мне действительно хотелось бы знать, чтобы я знал, что я сделал неправильно. - person David; 10.03.2010

Чтобы повысить эффективность, больше думайте о своих условиях. Вам действительно нужно проверять каждый фактор от 2 до N? Есть ли другая точка остановки, которая поможет быстрее выполнить проверку простых чисел?

Чтобы улучшить API, подумайте о том, чтобы сделать рекурсивный метод закрытым, с общедоступной точкой входа, которая поможет запустить процесс. Например:

public static boolean prime(int n) {
  return recurse(n, n);
}

private static boolean recurse(int a, int b) {
 ...
}

Создание метода private означает, что его нельзя вызывать из другого класса. Он фактически невидим для пользователей класса. Цель здесь состоит в том, чтобы скрыть «уродливый» дополнительный параметр, предоставив общедоступный вспомогательный метод.

Подумайте о множителях некоторых составных чисел. 10 множителей до 52. 12 множителей до 62. 14 множителей до 72. Теперь подумайте о 25. 25 множителей до 55. А как насчет 9? Вы видите закономерность? Кстати, если это не домашнее задание, пожалуйста, дайте мне знать. Быть таким дидактиком тяжело для меня.

person erickson    schedule 10.03.2010
comment
Разве мне не нужно проверять каждый фактор из 2-(n-1)? [n-1 не n, так как если бы он проверил деление на n, он подумал бы, что число не простое, потому что оно%n было бы =0. - person David; 10.03.2010
comment
и что значит сказать, что метод такой закрытый? что это делает? - person David; 10.03.2010
comment
ВТФ? Теперь пойду поищу дидактику :-) - person paxdiablo; 10.03.2010
comment
ну, это не очень хорошо отвечает на вопрос. - person David; 10.03.2010

В ответ на вопрос, почему число 7 не работает, представьте, что вы — компьютер, и проработайте свою логику. Вот что вы написали.

class prime
{

    public static boolean prime (int a, int b) 
    { 
        if (a == 0) 
        {
            return false; 
        }
        else if (a%(b-1) == 0) 
        {
            return false; 
        }
        else if (b>1) 
        {
            // Have to add the return statement
            // here as others have pointed out!
            return prime(a, b-1);
        }
        else
        {
            return true; 
        }

    }

    public static void main (String[] arg) 
    {
        System.out.println (prime (45, 45)) ; 
    }
}

Итак, начнем с 7.

if(7 == 0) // not true, don't enter this block
else if(7 % 6 == 0) // not true
else if(7 > 1) // true, call prime(7, 6)

if(7 == 0) // not true, don't enter this block
else if(7 % 5 == 0) // not true
else if(6 > 1) // true, call prime(7, 5)

if(7 == 0) // not true, don't enter this block
else if(7 % 4 == 0) // not true
else if(5 > 1) // true, call prime(7, 4)

... продолжайте вызывать Prime(7, 2)

if(7 == 0) // not true, don't enter this block

else if(7 % 1 == 0) true, return false

Когда вы приступите к вызову prime(n, 2), он всегда будет возвращать false, потому что у вас есть логическая ошибка.

person Jonathon Faust    schedule 10.03.2010

Ваш рекурсивный метод должен возвращать значение, чтобы его можно было развернуть.

    public static boolean prime (int a, int b) 
{
    if (a == 0) 
    {
        return false; 
    }
    else if (a%(b-1) == 0) 
    {
        return false; 
    }
    else if (b>1) 
    {
        return prime (a, b-1) ;
    }
    else
    {
        return true; 
    }

}

Я мог бы написать это по-другому, но это причина того, что вы не можете скомпилировать код.

person Rob Goodwin    schedule 10.03.2010
comment
@David, он имеет в виду, что когда рекурсия подходит к условию остановки, будет серия returns, поскольку рекурсивные методы prime извлекаются из стека. Он имеет в виду, что возвращаемое значение будет передано обратно исходному вызову Prime(). - person Jonathon Faust; 10.03.2010

Я думаю, что на первоначальный вопрос уже был дан ответ - вам нужно вставить return в тело else if (b>1) - я просто хотел указать, что ваш код все равно будет падать, если в качестве значения для b будет задано 1, выбрасывая ArithmeticException, поскольку a%(b-1) будет оцениваться как a%0, вызывая деление на ноль.

Вы можете избежать этого, сделав первый оператор if if (a == 0 || b == 1) {} Это не улучшит способ поиска простых чисел программой, это просто гарантирует, что будет на один способ меньше ее сбой.

person user286640    schedule 10.03.2010

Подобно ответу @paxdiblo, но немного более эффективно.

public static boolean isPrime(int num) {
    if (num <= 1 || (num & 1) == 0) return false;
    for (int t = 3; t * t <= num; t += 2)
        if (num % t == 0)
            return false;
    return true;
}

Как только определено, что число нечетное, все четные числа можно пропустить. Это уменьшит вдвое число, которое необходимо проверить.

person Peter Lawrey    schedule 27.06.2010