Re[10]: Задачи на числа. Решение. Покритикуйте. (часть #1)
От: m1st  
Дата: 10.04.12 13:57
Оценка:
Исправленная и улучшенная версия.

OperationsWithNumbersStarter — стартовый класс:
package chapt01.b;

import static chapt01.b.OperationsWithNumbersSolver.*;

/**
 * @author m1st
 * 
 */
public class OperationsWithNumbersStarter {
    public static void main(String[] args) {
    // 0. Ввести с консоли n целых чисел и поместить их в массив. На консоль
    // вывести:
    int[] sourceNumbers = readInput();
    if (sourceNumbers.length == 0) {
        System.out.println("sourceNumbers.length == 0");
        System.exit(0);
    }
    // 1. Четные и нечетные числа
    findEvenAndOddNumbers(sourceNumbers);

    // 2. Наибольшее и наименьшее число.
    findMaxAndMinNumbers(sourceNumbers);

    // 3. Числа, которые делятся на 3 или на 9.
    findModNumbers(3, sourceNumbers);

    // 4. Числа, которые делятся на 5 и на 7.
    findModNumbers(5, 7, sourceNumbers);

    // 5. Элементы, расположенные методом пузырька по убыванию модулей.
    int[] sourceNumbersClone1 = sourceNumbers.clone();
    sortNumbersWithBubbleReverseByAbs(sourceNumbersClone1);

    // 6. Все трехзначные числа, в десятичной записи которых нет одинаковых
    // цифр.
    findNumbersWithThreeDifferentDigits(sourceNumbers);

    // 7. Наибольший общий делитель и наименьшее общее кратное этих чисел.
    findGreatestCommonDivisor(sourceNumbers);
    findLeastCommonMultiple(sourceNumbers);

    // 8. Простые числа.
    findPrimeNumbers(sourceNumbers);

    // 9. Отсортированные числа в порядке возрастания и убывания.
    sortNumbersByAscAndDesc(sourceNumbers);

    // 10. Числа в порядке убывания частоты встречаемости чисел.
    findNumbersFrequencyByDesc(sourceNumbers);

    // 11. “Счастливые” числа.
    findHappyNumbers(sourceNumbers);

    // 12. Числа Фибоначчи: f0 = f1 = 1, f (n) = f (n–1) + f (n–2).
    findFibonacciNumbers(sourceNumbers);

    // 13. Числа-палиндромы, значения которых в прямом и обратном порядке
    // совпадают.
    findPalindromicNumbers(sourceNumbers);

    // 14. Элементы, которые равны полусумме соседних элементов.
    int[] sourceNumbersClone2 = sourceNumbers.clone();
    findNumbersOfHalfSumOfNeighbors(sourceNumbersClone2);

    // 15. Период десятичной дроби p = m/n для первых двух целых
    // положительных чисел n и m, расположенных подряд.
    findPeriodForFirstTwoPositiveNumbersInARow(sourceNumbers);

    // 16. Построить треугольник Паскаля для первого положительного числа.
    buildPascalTriangleForFirstPositiveNumber(sourceNumbers);
    }
}



OperationsWithNumbersSolver — класс, содержащий методы вычислений:
package chapt01.b;

import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;

/**
 * @author m1st
 * 
 */
public class OperationsWithNumbersSolver {
    public static void println(Object obj) {
    System.out.println(obj);
    }

    public static void print(Object obj) {
    System.out.print(obj);
    }

    public static void println() {
    System.out.println();
    }

    public static int[] toIntArray(Collection<Integer> list) {
    int[] result = new int[list.size()];
    Iterator<Integer> it = list.iterator();
    for (int i = 0; i < list.size(); i++) {
        result[i] = it.next();
    }
    return result;
    }

    public static int[] readInput(Scanner sc) {
    List<Integer> input = new ArrayList<Integer>();
    print("Введите целые числа через пробел, для окончания введите q: ");
    while (sc.hasNext()) {
        String number = sc.next();
        if (number.equals("q")) {
        break;
        } else {
        try {
            input.add(Integer.parseInt(number));
        } catch (NumberFormatException e) {
            sc.close();
            print("Неправильный формат. Программа завершена. ");
            System.exit(0);
        }
        }
    }
    return toIntArray(input);
    }

    public static int[] readInput(InputStream is) {
    return readInput(new Scanner(is));
    }

    public static int[] readInput() {
    return readInput(System.in);
    }

    public static List<Integer> toIntegerList(int[] array) {
    List<Integer> result = new ArrayList<Integer>(array.length);
    for (int element : array) {
        result.add(element);
    }
    return result;
    }

    public static void findEvenAndOddNumbers(int[] sourceNumbers) {
    List<Integer> evens = new ArrayList<Integer>();
    List<Integer> odds = new ArrayList<Integer>();
    for (int sourceNumber : sourceNumbers) {
        if (sourceNumber % 2 == 0) {
        evens.add(sourceNumber);
        } else {
        odds.add(sourceNumber);
        }
    }
    println("Чётные числа: " + evens);
    println("Нечётные числа: " + odds);
    }

    public static void findMaxAndMinNumbers(int[] sourceNumbers) {
    List<Integer> numbers = toIntegerList(sourceNumbers);
    println("Наибольшее число: " + Collections.max(numbers));
    println("Наименьшее число: " + Collections.min(numbers));
    }

    public static void findModNumbers(int divider, int[] sourceNumbers) {
    print("Числа, которые делятся на " + divider + ": ");
    for (int sourceNumber : sourceNumbers) {
        if (sourceNumber % divider == 0) {
        print(sourceNumber + ", ");
        }
    }
    println();
    }

    public static void findModNumbers(int divider1, int divider2,
        int[] sourceNumbers) {
    print("Числа, которые делятся на " + divider1 + " и на " + divider2
        + ": ");
    for (int sourceNumber : sourceNumbers) {
        if ((sourceNumber % divider1 == 0)
            && (sourceNumber % divider2 == 0)) {
        print(sourceNumber + ", ");
        }
    }
    println();
    }

    public static void sortNumbersWithBubbleReverseByAbs(int[] sourceNumbers) {
    boolean swapped;
    int temp, j = 0;
    do {
        swapped = false;
        j++;
        for (int i = 0; i < sourceNumbers.length - j; i++) {
        if (Math.abs((long) sourceNumbers[i]) < Math
            .abs((long) sourceNumbers[i + 1])) {
            temp = sourceNumbers[i];
            sourceNumbers[i] = sourceNumbers[i + 1];
            sourceNumbers[i + 1] = temp;
            swapped = true;
        }
        }
    } while (swapped);
    print("Элементы, расположенные методом пузырька по убыванию модулей: "
        + Arrays.toString(sourceNumbers) + "\n");
    }

    public static void findNumbersWithThreeDifferentDigits(int[] sourceNumbers) {
    int digit1, digit2, digit3;
    print("Все трехзначные числа, в десятичной записи которых нет одинаковых цифр: ");
    for (int sourceNumber : sourceNumbers) {
        digit1 = sourceNumber / 100;
        digit2 = sourceNumber / 10 % 10;
        digit3 = sourceNumber % 10;
        // Ищем трехзначные числа
        if ((sourceNumber >= 100 && sourceNumber <= 999)
            || (sourceNumber <= -100 && sourceNumber >= -999)) {
        if ((digit1 != digit2) && (digit2 != digit3)
            && (digit1 != digit3)) {
            print(sourceNumber + ", ");
        }
        }
    }
    println();
    }

    public static long findGreatestCommonDivisor(long a, long b) {
    while (b != 0) {
        long temp = b;
        b = a % b;
        a = temp;
    }
    return a;
    }

    public static void findGreatestCommonDivisor(int[] sourceNumbers) {
    long result = sourceNumbers[0];
    if (result == 0) {
        println("Наибольший общий делитель: " + result);
    } else {
        for (int i = 1; i < sourceNumbers.length; i++) {
        result = findGreatestCommonDivisor(result, sourceNumbers[i]);
        }
        println("Наибольший общий делитель: " + result);
    }
    }

    public static long findLeastCommonMultiple(long a, long b) {
    return a * (b / findGreatestCommonDivisor(a, b));
    }

    /**
     * Метод вычисляет НОК чисел в массиве. Вычисление завершается на первом
     * нулевом числе (оно не входит в НОК). <strong>Результат метода в случае
     * переполнения не определен</strong>
     * 
     * @param sourceNumbers
     *            числа для нахождения НОК
     * @return НОК. В случае переполнения результат не определен.
     */
    public static void findLeastCommonMultiple(int[] sourceNumbers) {
    long result = sourceNumbers[0];
    if (result == 0) {
        println("Наименьшее общее кратное: " + result);
    } else {
        for (int i = 1; i < sourceNumbers.length; i++) {
        if (sourceNumbers[i] == 0) {
            break;
        }
        result = findLeastCommonMultiple(result, sourceNumbers[i]);
        }
    }
    println("Наименьшее общее кратное: " + result);
    }

    public static boolean isPrimeNumber(int number) {
    if (number <= 1) {
        return false;
    }
    if (number == 2) {
        return true;
    }
    if (number % 2 != 0) {
        return true;
    }
    final double ceiledNumberSqrt = Math.ceil(Math.sqrt(number));
    for (long i = 3; i <= ceiledNumberSqrt; i += 2) {
        if (number % i == 0) {
        return false;
        }
    }
    return false;
    }

    public static void findPrimeNumbers(int[] sourceNumbers) {
    print("Простые числа: ");
    for (int sourceNumber : sourceNumbers) {
        if (isPrimeNumber(sourceNumber)) {
        print(sourceNumber + ", ");
        }
    }
    println();
    }

    public static void sortNumbersByAscAndDesc(int[] sourceNumbers) {
    List<Integer> numbers = toIntegerList(sourceNumbers);
    Collections.sort(numbers);
    println("Отсортированные числа в порядке возрастания: " + numbers);
    Collections.reverse(numbers);
    println("Отсортированные числа в порядке убывания: " + numbers);
    }

    public static Map<Integer, Integer> sortNumbersWithDescByValue(
        Map<Integer, Integer> map) {
    List<Entry<Integer, Integer>> frequencyList = new ArrayList<Entry<Integer, Integer>>(
        map.entrySet());
    Collections.sort(frequencyList,
        new Comparator<Entry<Integer, Integer>>() {
            public int compare(Entry<Integer, Integer> o1,
                Entry<Integer, Integer> o2) {
            return ((Entry<Integer, Integer>) (o2)).getValue()
                .compareTo(
                    ((Entry<Integer, Integer>) (o1))
                        .getValue());
            }
        });
    Map<Integer, Integer> result = new LinkedHashMap<Integer, Integer>();
    for (Entry<Integer, Integer> frequency : frequencyList) {
        result.put(frequency.getKey(), frequency.getValue());
    }
    return result;
    }

    public static void findNumbersFrequencyByDesc(int[] sourceNumbers) {
    List<Integer> numbers = toIntegerList(sourceNumbers);
    Map<Integer, Integer> map = new HashMap<Integer, Integer>();
    int frequency = 1;
    for (Integer number : numbers) {
        if (map.containsKey(number)) {
        map.put(number, ++frequency);
        } else {
        map.put(number, 1);
        }
    }
    println("Числа в порядке убывания частоты встречаемости чисел (число=встречаемость): "
        + sortNumbersWithDescByValue(map));
    }

    public static void findHappyNumbers(int[] sourceNumbers) {
    print("“Счастливые” числа (сумма 1-ой пары чисел = сумме 2-ой пары): ");
    int digit1, digit2, digit3, digit4;
    for (int sourceNumber : sourceNumbers) {
        if ((sourceNumber >= 1000 && sourceNumber <= 9999)
            || (sourceNumber <= -1000 && sourceNumber >= -9999)) {
        digit1 = sourceNumber / 1000;
        digit2 = sourceNumber / 100 % 10;
        digit3 = sourceNumber % 100 / 10;
        digit4 = sourceNumber % 10;
        if (digit1 + digit2 == digit3 + digit4) {
            print(sourceNumber + ", ");
        }
        }
    }
    println();

    }

    /**
     * Метод находит числа Фибоначчи в массиве. <strong>Результат метода в
     * случае переполнения не определен</strong>
     * 
     * @param sourceNumbers
     *            числа для нахождения чисел Фибоначчи
     * @return числа Фибоначчи. В случае переполнения результат не определен.
     */
    public static void findFibonacciNumbers(int[] sourceNumbers) {
    print("Числа Фибоначчи: ");
    if (sourceNumbers.length >= 3) {
        if (sourceNumbers[0] == 0) {
        print("0, ");
        if (sourceNumbers[1] == 1) {
            print("1, ");
        }
        }
        for (int i = 2; i < sourceNumbers.length; i++) {
        if (sourceNumbers[i] == ((long) sourceNumbers[i - 2] + (long) sourceNumbers[i - 1])) {
            print(sourceNumbers[i] + ", ");
        }
        }
    } else {
        print("sourceNumbers.length < 3");
    }
    println();
    }

    public static long reverseInt(int value) {
    long result = 0;
    while (value != 0) {
        result = result * 10 + value % 10;
        value /= 10;
    }
    return result;
    }

    public static void findPalindromicNumbers(int[] sourceNumbers) {
    print("Числа-палиндромы: ");
    for (int sourceNumber : sourceNumbers) {
        if (sourceNumber == reverseInt(sourceNumber)) {
        print(sourceNumber + ", ");
        }
    }
    println();
    }

    /**
     * Метод находит элементы, которые равны полусумме соседних элементов в
     * массиве. <strong>Результат метода в случае переполнения не
     * определен</strong>
     * 
     * @param sourceNumbers
     *            числа для нахождения элементов, которые равны полусумме
     *            соседних элементов в массиве
     * @return элементы, которые равны полусумме соседних элементов в массиве. В
     *         случае переполнения результат не определен.
     */
    public static void findNumbersOfHalfSumOfNeighbors(int[] sourceNumbers) {
    print("Элементы, которые равны полусумме соседних элементов: ");
    if (sourceNumbers.length >= 3) {
        for (int i = 1; i < sourceNumbers.length - 1; i++) {
        if (sourceNumbers[i] == ((double) ((long) sourceNumbers[i - 1] + (long) sourceNumbers[i + 1]) / 2)) {
            print(sourceNumbers[i] + ", ");
        }
        }
    } else {
        print("sourceNumbers.length < 3");
    }
    println();
    }

    public static int[] findFirstTwoPositiveNumbersInARow(int[] sourceNumbers) {
    int[] twoPositiveNumbers = new int[2];
    for (int i = 0; i < sourceNumbers.length - 1; i++) {
        if ((sourceNumbers[i] > 0) && (sourceNumbers[i + 1] > 0)) {
        twoPositiveNumbers[0] = sourceNumbers[i];
        twoPositiveNumbers[1] = sourceNumbers[i + 1];
        break;
        }
    }
    return twoPositiveNumbers;
    }

    public static void findPeriodForFirstTwoPositiveNumbersInARow(
        int[] sourceNumbers) {
    String message = "Период десятичной дроби для первых двух целых положительных чисел, расположенных подряд: ";
    if (sourceNumbers.length < 2) {
        println(message + "sourceNumbers.length < 2");
        return;
    }
    int[] twoPositiveNumbers = findFirstTwoPositiveNumbersInARow(sourceNumbers);
    if (twoPositiveNumbers.length < 2) {
        println(message
            + "нет двух целых положительных чисел, расположенных подряд");
        return;
    }
    int numerator = twoPositiveNumbers[0];
    int denominator = twoPositiveNumbers[1];
    if (numerator % denominator == 0) {
        print(numerator / denominator + ".(0)");
        return;
    }
    // Выделение из знаменателя наибольшей 2^
    int s = denominator % 2;
    int l = 0;
    while (s == 0) {
        denominator = denominator / 2;
        s = denominator % 2;
        l++;
    }
    // Выделение из знаменателя наибольшей 5^
    s = denominator % 5;
    int l1 = 0;
    while (s == 0) {
        denominator = denominator / 5;
        s = denominator % 5;
        l1++;
    }
    // Вычисление длины периода с новым знаменателем
    int periodLenth = 1;
    int r = 10;
    while (r != 1) {
        r = (10 * r) % denominator;
        if (r == 0) {
        break;
        }
        periodLenth++;
    }
    long beforePeriodLenth;
    if (l1 > l) {
        beforePeriodLenth = l1;
    } else {
        beforePeriodLenth = l;
    }
    denominator = twoPositiveNumbers[1];
    print(message + numerator / denominator + ".");
    numerator = numerator % denominator;
    for (int i = 0; i < beforePeriodLenth; i++) {
        print((numerator * 10) / denominator);
        numerator = (numerator * 10) % denominator;
    }
    print("(");
    for (int i = 0; i < periodLenth; i++) {
        print((numerator * 10) / denominator);
        numerator = (numerator * 10) % denominator;
    }
    print(") = " + twoPositiveNumbers[0] + " / " + twoPositiveNumbers[1] + "\n");
    }

    public static int findFirstPositiveNumber(int[] sourceNumbers) {
    for (int sourceNumber : sourceNumbers) {
        if (sourceNumber > 0) {
        return sourceNumber;
        }
    }
    return 0;
    }

    public static void buildPascalTriangleForFirstPositiveNumber(
        int[] sourceNumbers) {
    final int firstPositiveNumber = findFirstPositiveNumber(sourceNumbers);
    if (firstPositiveNumber == 0) {
        println("!in.hasNext()");
    } else {
        print("Сколько строк треугольника Паскаля для числа "
            + firstPositiveNumber + " отображать? ");
        Scanner sc = new Scanner(System.in);
        if (!sc.hasNext()) {
        sc.close();
        println("Треугольник Паскаля для первого положительного числа: нет чисел > 0");
        } else {
        final int rows = sc.nextInt();
        for (int y = 0; y < rows; y++) {
            int c = firstPositiveNumber;
            for (int i = 0; i < rows - y; i++) {
            print("   ");
            }
            for (int x = 0; x <= y; x++) {
            print("   " + c + " ");
            c = c * (y - x) / (x + 1);
            }

            println();
        }
        }
        sc.close();
    }
    }
}
Re[11]: Задачи на числа. Решение. Покритикуйте. (часть #1)
От: m1st  
Дата: 15.04.12 04:02
Оценка:
Код наиболее последней версии переехал сюда: http://ideone.com/a72cO
Re[12]: Задачи на числа. Решение. Покритикуйте. (часть #1)
От: maxkar  
Дата: 18.04.12 17:13
Оценка:
Здравствуйте, m1st, Вы писали:

M>Код наиболее последней версии переехал сюда: http://ideone.com/a72cO


По текущей версии.
  1. findModNumbers что-то он не то делает "в общем случае". Там явно не проверка на все числа (достаточно только делимости на четыре числа). Да и два вложенных цикла пугают. В одном из правильных вариантов код вообще не должен смотреть на длину args. И может даже на его пустоту не проверять, там все случаи абсолютно одинаковы.
  2. findNumbersWithThreeDifferentDigits — вычисление цифр лучше делать уже после проверки диапазона. В этом случае для "непрошедших" чисел выполняется меньше вычислений. На мелких задачах это незаметно. На больших объемах данных разница может стать заметной. Кстати, в счастливых числах это как раз сделано правильно.
  3. isPrimeNumber — все нечетные числа считаются простыми. Вообще, похоже на опечатку. Там должно быть обратное условие и обратный результат
  4. isPrimeNumber — а ceiledNumberSqrt лучше бы тоже целым сделать (и скастить в целочисленный тип, если нужно). Меньше лишних конвертаций типов.
  5. findNumbersFrequencyByDesc — frequency считается неправильно. Попробуйте на массиве [1,1,1,2,2,2,3,3,3] протестировать. "частоту" тоже нужно брать из map. Если она null — класть 1, иначе увеличивать частоту на 1. Тогда и containsKey не понадобится (если ключа нет, будет прочитан null). Два варианта (нашли/не нашли), естественно, останутся.
  6. findNumbersOfHalfSumOfNeighbors — Преобразования в числы с плавающей точкой мне не очень нрваятся. Я уже предлагал умножить обе части на 2L, тогда и каст в double не понадобится . Да, еще — переполнения после каста в long больше быть не может. Т.е. условие будет (2L * sourceNumbers[i] == sourceNumbers[i-1] + (long) sourceNumbers[i-1]).

Отдельно отмечу, что в findPeriodForFirstTwoPositiveNumbersInARow появились комментарии. В нем стали видны законченные логические блоки. Хотя я в алгоритм не вчитывался, для восприятия общая структура метода стала гораздо проще, чем раньше.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.