Поиск суммы цифр числа независимо от длины с помощью лямбда выражения в Java

119
11 июня 2019, 12:10

Пытаюсь решить задачу на Java именно с помощью лямбда выражения.

Необходимо в одном лямбда выражении обойти числа от 1 до 1000 и получить массив чисел(выборку из чисел от 1 до 1000), суммы квадратов цифр которых это четные числа.

Это вариант без лямбда:

List<Integer> list = new ArrayList<>();
for (int i = 1; i<1001; i++){
    int sum = 0;
    for(int a=i; a>0; a/=10){
        sum += (a%10)*(a%10);
    }
    if (sum%2==0) list.add(i);
}

Если кто представляет как, пожалуйста, помогите.

Код поправил, спасибо.

Answer 1

В первом приближении задача решается так:

List<Integer> result = IntStream.range(1, 1000)
        .filter(n -> isDigitsSquaresEven(n))
        .boxed()
        .collect(Collectors.toList())

IntStream.range() даёт нам стрим из int'ов от 1 до 1000 (не включая 1000) с шагом 1. Это практически аналог самого простого цикла for. Дальше при помощи условия в filter мы проверяем, какие из элементов стрима подходят под наше условие (сумма квадратов цифр должна быть чётной) и оставляем только их. Затем собираем все оставшиеся числа в список. Обратите внимание на boxed() - с его помощью мы переходим от IntStream к обычному Stream, чтобы была возможнось использовать более простую перегрузку collect().

Само собой, мы можем isDigitsSquaresEven() реализовать как отдельный метод, в котором будут все проверки, но это же не интересно! Давайте превратим его в лямбду. Тут нам поможет IntStream.iterate(). С его помощью можно получить стрим, который начинается с определённого числа, а каждое последующее число будет результатом применения функции к предыдущему. Например,

int num = 192837;
IntStream ints = IntStream.iterate(num, i -> i / 10);

даст нам стрим, состоящий из результатов деления исходного числа 192837 на 10: 192837, 19283, 1928, 192, 19, 1, 0, 0, 0... Стрим бесконечный, поэтому его нужно как-то ограничить. filter нам тут не поможет: он хоть и отбросит все нули, но IntStream.iterate() продолжит производить числа до бесконечности. Ограничивать нужно именно количество производимых чисел, и тут нам поможет limit(). Как узнать, сколько чисел брать? Очень просто - значение десятичного логарифма исходного числа даст нам количество разрядов в нём: Math.log10(192837) == 5.2851903664659154. Округляем и получаем нужное число: Math.floor(Math.log10(192837)) + 1 == 6.0. Округляем вниз из-за свойств десятичного логарифма: log10(1) == 0, log10(10) == 1, log10(100) == 2. Теперь можно и логику добавить:

int num = 192837;
int sum = IntStream.iterate(num, i -> i / 10)
        // Сейчас стрим бесконечный: 192837, 19283, 1928, 192, 19, 1, 0, 0, 0...
        .limit(((Double) Math.floor(Math.log10(num))).longValue() + 1)
        // А сейчас в нём всего 6 элементов: 192837, 19283, 1928, 192, 19, 1
        .map(i -> i % 10)
        // Теперь в стриме цифры исходного числа: 7, 3, 8, 2, 9, 1
        .map(i -> i * i)
        // Теперь - их квадраты: 49, 9, 64, 4, 81, 1
        .sum()
        // Теперь - их сумма: 208

Начиная с Java 9 в стримах есть метод takeWhile(), с помощью которого можно ограничить стрим условием:

int sum = IntStream.iterate(num, i -> i / 10)
        .takeWhile(i -> i > 0)
        .map(i -> i % 10)
        .map(i -> i * i)
        .sum()

Собираем всё вместе и получаем:

List<Integer> result = IntStream.range(1, 1000)
        .filter(num ->
            IntStream.iterate(num, i -> i / 10)
                .limit(((Double) Math.floor(Math.log10(num))).longValue() + 1)
                .map(i -> i % 10)
                .map(i -> i * i)
                .sum() % 2 == 0
        )
        .boxed()
        .collect(Collectors.toList())

Или, в Java 9 варианте:

List<Integer> result = IntStream.range(1, 1000)
        .filter(num ->
            IntStream.iterate(num, i -> i / 10)
                .takeWhile(i -> i > 0)
                .map(i -> i % 10)
                .map(i -> i * i)
                .sum() % 2 == 0
        )
        .boxed()
        .collect(Collectors.toList())
Answer 2

Я старался по максимому написать все с использованием лямбды, но не так все просто оказалось...

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;
public class Lyambda {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        IntStream.range(0, 1000).forEach(
                i -> {
                    int sum = 0;
                    int number = i;
                    for(int j = 0; j < ("" + i).length(); j++) {
                        sum += (int) Math.pow(number%10, 2);
                        number = number/10;
                    }
                    if (sum%2 == 0)
//                      System.out.println("i = " + i + " sum = " + sum);
                        list.add(i);
                }
            );
        //Проверка
        for (Integer integer : list) {
            System.out.println(integer);
        }
    }
}

Откуда я брал информацию:
http://www.adam-bien.com/roller/abien/entry/java_8_from_ordinary_for https://stackoverflow.com/questions/1306727/way-to-get-number-of-digits-in-an-int

Answer 3

Могу предложить такое извращение (про порядок в выборке вроде не упоминалось)

public class RecExample {
    List<Integer> list = new ArrayList<>();
    Consumer<Integer> recConsumer = i -> {
        if (i < 2)
            return;
        if ((String.valueOf(i).chars().map(c -> c - 48).reduce(0, (a, c) -> a + c * c) & 1) == 0)
            list.add(i);
        this.recConsumer.accept(i - 1);
    };
    public static void main(String[] args) {
        RecExample recExample = new RecExample();
        recExample.recConsumer.accept(1000);
        System.out.println(recExample.list);
    }
}
READ ALSO
Использование record screen appium

Использование record screen appium

Для записи видео с экрана телефона использую след команды:

137
Java Tomcat переход с ip на домен

Java Tomcat переход с ip на домен

Я купил вдс сервер и установил на нем Tomcatна вдс есть ip и домен (которые вроде привязан к этому ip) Кинул свой проект в webapps

119
VK API БОТ отправка сообщений

VK API БОТ отправка сообщений

Делаю бота vk с помощью Java SDK

135