Java for Beginner
673 subscribers
541 photos
155 videos
12 files
827 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Святой JVM! Что за ересь?😂

https://t.me/Java_for_beginner_dev

#Mems
Основные типы и виды алгоритмов

1. Поисковые алгоритмы
Поисковые алгоритмы предназначены для нахождения элемента в структуре данных.

Линейный поиск
Линейный поиск проверяет каждый элемент структуры данных последовательно, пока не найдет нужный элемент. Этот алгоритм имеет линейную сложность O(n), так как время выполнения прямо пропорционально размеру входных данных.
int linearSearch(int[] array, int x) {
for (int i = 0; i < array.length; i++) {
if (array[i] == x) {
return i;
}
}
return -1;
}
Сложность: O(n). Если массив содержит n элементов, в худшем случае потребуется n операций для поиска.


Бинарный поиск
Бинарный поиск используется для поиска в отсортированном массиве. Алгоритм делит массив пополам на каждой итерации, уменьшая область поиска. Этот алгоритм имеет логарифмическую сложность O(log n), так как количество операций растет логарифмически относительно размера входных данных.
int binarySearch(int[] array, int x) {
int left = 0, right = array.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (array[mid] == x)
return mid;
if (array[mid] < x)
left = mid + 1;
else
right = mid - 1;
}
return -1;
}
Сложность: O(log n). Например, для массива из 1000 элементов потребуется примерно 10 операций в худшем случае.


2. Сортировочные алгоритмы

Сортировка данных — одна из ключевых задач в программировании. Приведем примеры с разной сложностью.

Сортировка пузырьком
Этот метод сравнивает соседние элементы и меняет их местами, пока массив не будет отсортирован. Сложность алгоритма — квадратичная O(n²), что делает его неэффективным для больших массивов.
void bubbleSort(int[] array) {
int n = array.length;
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (array[j] > array[j + 1]) {
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
Сложность: O(n²). Для массива из 100 элементов потребуется до 10,000 операций в худшем случае.


Быстрая сортировка (QuickSort)
Этот алгоритм использует стратегию «разделяй и властвуй». Он выбирает опорный элемент (pivot), сортирует элементы относительно него, затем рекурсивно повторяет процесс для подмассивов. Сложность в среднем — O(n log n), но в худшем случае (если опорный элемент всегда оказывается крайним) — O(n²).
int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = (low - 1);
for (int j = low; j < high; j++) {
if (array[j] < pivot) {
i++;
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
int temp = array[i + 1];
array[i + 1] = array[high];
array[high] = temp;
return i + 1;
}

void quickSort(int[] array, int low, int high) {
if (low < high) {
int pi = partition(array, low, high);
quickSort(array, low, pi - 1);
quickSort(array, pi + 1, high);
}
}
Сложность: В среднем O(n log n), в худшем случае O(n²). Например, для массива из 1000 элементов потребуется порядка 10,000 операций в среднем случае и до 1,000,000 операций в худшем.


3. Алгоритмы работы с графами

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

Поиск в глубину (DFS)
DFS — это рекурсивный алгоритм, который исследует узлы до максимальной глубины перед возвратом и исследованием других путей. Сложность алгоритма составляет O(V + E), где V — количество узлов, а E — количество ребер.
void DFS(int v, boolean[] visited, List<List<Integer>> adj) {
visited[v] = true;
System.out.print(v + " ");
for (int x : adj.get(v)) {
if (!visited[x]) {
DFS(x, visited, adj);
}
}
}
Сложность: O(V + E). В зависимости от плотности графа, сложность может быть линейной или близкой к квадратичной.


#Java #Training #Medium #Algorithm
Поиск в ширину (BFS)
BFS использует очередь и исследует граф уровнями, начиная с начального узла. Как и DFS, сложность BFS — O(V + E).
void BFS(int v, boolean[] visited, List<List<Integer>> adj) {
Queue<Integer> queue = new LinkedList<>();
visited[v] = true;
queue.add(v);
while (!queue.isEmpty()) {
v = queue.poll();
System.out.print(v + " ");
for (int x : adj.get(v)) {
if (!visited[x]) {
visited[x] = true;
queue.add(x);
}
}
}
}
Сложность: O(V + E). В лучшем случае сложность линейная, но в плотных графах сложность может приближаться к квадратичной.


4. Динамическое программирование

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

Задача о рюкзаке
Динамическое программирование эффективно решает задачу о рюкзаке, используя таблицу для хранения промежуточных результатов. Сложность этого алгоритма — O(nW), где n — количество предметов, а W — вместимость рюкзака.
int knapSack(int W, int[] wt, int[] val, int n) {
int[][] K = new int[n + 1][W + 1];
for (int i = 0; i <= n; i++) {
for (int w = 0; w <= W; w++) {
if (i == 0 || w == 0)
K[i][w] = 0;
else if (wt[i - 1] <= w)
K[i][w] = Math.max(val[i - 1] + K[i - 1][w - wt[i - 1]], K[i - 1][w]);
else
K[i][w] = K[i - 1][w];
}
}
return K[n][W];
}
Сложность: O(nW). Например, если n = 10, W = 50, потребуется 500 операций для заполнения таблицы.


#Java #Training #Medium #Algorithm
Работа с датами в Java.

Работа с датами и временем — одна из важнейших задач в программировании. В Java для этого изначально были предоставлены классы Date и Calendar, которые позволяли работать с датами и временем. Однако, с течением времени, их функциональность оказалась недостаточной, и они были заменены более современными решениями, такими как java.time (включая классы LocalDate, LocalTime, LocalDateTime и другие), появившимися в Java 8.

Класс Date

Класс Date был частью Java с самой первой версии JDK 1.0 и представлял собой конкретный момент времени, измеряемый в миллисекундах с 1 января 1970 года (эта дата называется "эпоха UNIX").


import java.util.Date;

public class DateExample {
public static void main(String[] args) {
Date date = new Date(); // текущая дата и время
System.out.println("Current Date: " + date);

// Создание даты на основе миллисекунд от начала эпохи UNIX
Date specificDate = new Date(1633024800000L); // 1 октября 2021 года
System.out.println("Specific Date: " + specificDate);
}
}
Этот пример демонстрирует, как можно создать объект Date с текущей датой и временем, а также с определенной датой.


Недостатки класса Date

Изменяемость: Класс Date изменяем, что делает его использование в многопоточных приложениях небезопасным.
Устаревшие методы: Большинство методов класса Date устарели и заменены новыми методами в других классах (например, Calendar).
Сложность манипуляций с датами: Для выполнения операций с датами, таких как добавление дней, месяцев или лет, требуется использование других классов, например, Calendar, так как у Date нет встроенных методов для подобных операций.


Класс Calendar

Для преодоления ограничений Date, в Java 1.1 был введен класс Calendar. Этот класс предоставляет более сложные и гибкие методы для работы с датами и временем.

import java.util.Calendar;

public class CalendarExample {
public static void main(String[] args) {
// Получение текущей даты и времени
Calendar calendar = Calendar.getInstance();
System.out.println("Current Date: " + calendar.getTime());

// Установка конкретной даты
calendar.set(2021, Calendar.OCTOBER, 1);
System.out.println("Specific Date: " + calendar.getTime());

// Добавление дней к дате
calendar.add(Calendar.DAY_OF_MONTH, 5);
System.out.println("Date after adding 5 days: " + calendar.getTime());

// Получение отдельных частей даты
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // месяцы начинаются с 0
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("Year: " + year + ", Month: " + month + ", Day: " + day);
}
}
Этот пример показывает, как можно использовать Calendar для работы с датами, такими как установка конкретной даты, добавление дней и получение отдельных частей даты (год, месяц, день).


Ограничения Calendar

Изменяемость: Класс Calendar, как и Date, изменяем, что создает те же проблемы с безопасностью в многопоточных приложениях.
Сложность API: Интерфейс Calendar слишком сложен, что делает его использование затруднительным для простых задач.
Неточности в работе с временными зонами: Работа с временными зонами через Calendar может быть запутанной и неточной.


#Java #Training #Medium #Date #Calendar #Time
Современные решения: java.time API

В Java 8 был представлен новый API для работы с датами и временем — java.time, который призван заменить устаревшие классы Date и Calendar. Этот API базируется на новом подходе, обеспечивающем неизменяемость и четкую работу с датами, временем и временными зонами.

Основные классы java.time

LocalDate: Представляет собой дату без учета времени и часового пояса.
LocalTime: Представляет время без учета даты и часового пояса.
LocalDateTime: Комбинация даты и времени без учета часового пояса.
ZonedDateTime: Дата и время с учетом часового пояса.
Duration и Period: Представляют собой длительность и период времени соответственно.


import java.time.LocalDate;
import java.time.LocalDateTime;

public class NewDateTimeExample {
public static void main(String[] args) {
// Текущая дата
LocalDate currentDate = LocalDate.now();
System.out.println("Current Date: " + currentDate);

// Текущая дата и время
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current Date and Time: " + currentDateTime);

// Установка конкретной даты
LocalDate specificDate = LocalDate.of(2021, 10, 1);
System.out.println("Specific Date: " + specificDate);

// Добавление дней к дате
LocalDate dateAfterAddingDays = currentDate.plusDays(5);
System.out.println("Date after adding 5 days: " + dateAfterAddingDays);
}
}
Этот пример показывает, как легко и интуитивно можно использовать новые классы для работы с датами и временем.


Преимущества java.time API

Неизменяемость: Все классы в java.time неизменяемы, что делает их потокобезопасными.
Четкая работа с часовыми поясами: Классы, такие как ZonedDateTime, предоставляют четкий и интуитивный интерфейс для работы с временными зонами.
Удобный API: Новый API проще и удобнее в использовании, особенно для выполнения повседневных задач с датами и временем.
Поддержка различных календарей: java.time поддерживает не только григорианский календарь, но и другие календари, такие как японский или тайский.


#Java #Training #Medium #Date #Calendar #Time
Что выведет код?

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class Main {
public static void main(String[] args) {
LocalDate date = LocalDate.of(2024, 2, 29);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formattedDate = date.plusYears(4).format(formatter);
System.out.println(formattedDate);
}
}


#Tasks
А потом ты еще и устроился😂😂😂

https://t.me/Java_for_beginner_dev

#Mems
Основные методы классов Date, Calendar и java.time, примеры применения

Основные методы класса Date

getTime()
Метод getTime() возвращает количество миллисекунд, прошедших с 1 января 1970 года. Это значение полезно для хранения времени в числовом формате или для сравнения дат.
import java.util.Date;

public class DateMethodsExample {
public static void main(String[] args) {
Date date = new Date();
long time = date.getTime();
System.out.println("Milliseconds since January 1, 1970: " + time);
}
}


after() и before()
Эти методы позволяют сравнивать две даты. Метод after() возвращает true, если текущая дата позже указанной, а before() — если раньше.
import java.util.Date;

public class DateComparisonExample {
public static void main(String[] args) {
Date date1 = new Date();
Date date2 = new Date(date1.getTime() + 10000); // 10 секунд позже

System.out.println("Is date1 before date2? " + date1.before(date2)); // true
System.out.println("Is date1 after date2? " + date1.after(date2)); // false
}
}


toString()
Метод toString() возвращает строковое представление даты. Этот метод полезен для быстрой отладки или вывода даты в формате по умолчанию.
import java.util.Dateimport java.util.Date;

public class DateToStringExample {
public static void main(String[] args) {
Date date = new Date();
System.out.println("Current Date: " + date.toString());
}
}
Этот пример показывает, как можно вывести дату в виде строки.


Основные методы класса Calendar

getInstance()
Метод getInstance() создает и возвращает объект Calendar, настроенный на текущую дату и время по умолчанию. Этот метод используется для получения базового объекта Calendar, с которым можно работать.
import java.util.Calendar;

public class CalendarGetInstanceExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println("Current Date and Time: " + calendar.getTime());
}
}


set()
Метод set() позволяет устанавливать дату и время в объекте Calendar. Этот метод полезен, когда нужно работать с конкретной датой или временем.
import java.util.Calendar;

public class CalendarSetExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2021, Calendar.OCTOBER, 1);
System.out.println("Specific Date: " + calendar.getTime());
}
}


add()
Метод add() позволяет изменять дату и время путем добавления или вычитания определенного количества дней, месяцев или лет.

import java.util.Calendar;

public class CalendarAddExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 5);
System.out.println("Date after adding 5 days: " + calendar.getTime());
}
}
Этот пример показывает, как добавить 5 дней к текущей дате.


get()
Метод get() позволяет получить значение конкретного поля даты, например, год, месяц, день или час.
import java.util.Calendar;

public class CalendarGetExample {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH) + 1; // месяцы начинаются с 0
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("Year: " + year + ", Month: " + month + ", Day: " + day);
}
}


#Java #Training #Medium #Date #Calendar #Time
Основные методы java.time API

now()
Метод now() доступен в большинстве классов java.time (например, LocalDate, LocalTime, LocalDateTime) и используется для получения текущей даты, времени или их комбинации.
import java.time.LocalDate;
import java.time.LocalDateTime;

public class DateTimeNowExample {
public static void main(String[] args) {
LocalDate currentDate = LocalDate.now();
LocalDateTime currentDateTime = LocalDateTime.now();
System.out.println("Current Date: " + currentDate);
System.out.println("Current Date and Time: " + currentDateTime);
}
}


of()

Метод of() используется для создания объекта LocalDate, LocalTime или LocalDateTime с заданными значениями даты и/или времени.
import java.time.LocalDate;

public class DateTimeOfExample {
public static void main(String[] args) {
LocalDate specificDate = LocalDate.of(2021, 10, 1);
System.out.println("Specific Date: " + specificDate);
}
}


plusDays(), minusDays() и другие аналогичные методы
Эти методы позволяют добавлять или вычитать дни, месяцы, годы и другие временные единицы из объекта LocalDate или LocalDateTime.
import java.time.LocalDate;

public class DateTimePlusMinusExample {
public static void main(String[] args) {
LocalDate currentDate = LocalDate.now();
LocalDate dateAfterAddingDays = currentDate.plusDays(5);
LocalDate dateAfterSubtractingMonths = currentDate.minusMonths(2);
System.out.println("Date after adding 5 days: " + dateAfterAddingDays);
System.out.println("Date after subtracting 2 months: " + dateAfterSubtractingMonths);
}
}


parse()
Метод parse() позволяет создавать объекты LocalDate, LocalTime, LocalDateTime из строковых представлений даты или времени.
import java.time.LocalDate;

public class DateTimeParseExample {
public static void main(String[] args) {
String dateString = "2023-08-28";
LocalDate date = LocalDate.parse(dateString);
System.out.println("Parsed Date: " + date);
}
}


format()
Метод format() используется для преобразования объекта LocalDate, LocalTime, LocalDateTime в строку с использованием определенного формата.
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class DateTimeFormatExample {
public static void main(String[] args) {
LocalDate date = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
String formattedDate = date.format(formatter);
System.out.println("Formatted Date: " + formattedDate);
}
}


#Java #Training #Medium #Date #Calendar #Time
Класс Random в Java. Особенности и внутреннее устройство

Генерация случайных чисел — одна из важных задач в программировании, которая находит применение в самых разных областях, начиная от криптографии и заканчивая играми. В Java для этой цели существует класс Random, который позволяет создавать последовательности псевдослучайных чисел.


Основы работы класса Random

Класс Random в Java предоставляет средства для генерации случайных чисел различного типа: целых чисел, чисел с плавающей точкой, булевых значений и т. д. Этот класс базируется на генераторе псевдослучайных чисел (PRNG, pseudo-random number generator), который использует начальное значение (seed) для создания предсказуемой последовательности чисел, которая выглядит случайной.

Когда создается объект Random без указания начального значения, используется текущее время системы в миллисекундах в качестве seed, что делает каждую последовательность уникальной для каждого запуска программы.
import java.util.Random;

public class RandomExample {
public static void main(String[] args) {
Random random = new Random();
int randomInt = random.nextInt();
System.out.println("Random Integer: " + randomInt);
}
}
Этот код создает объект Random и генерирует случайное целое число.


Внутреннее устройство класса Random

Класс Random реализует линейный конгруэнтный метод (LCG) для генерации псевдослучайных чисел. Этот метод заключается в следующем:

𝑋𝑛+1=(𝑎⋅𝑋𝑛+𝑐) mod  𝑚
где:
Xn — текущее значение (seed).
𝑎 — множитель (multiplier).
𝑐 — приращение (increment).
𝑚 — модуль (modulus).


В Java, значения для a, c и m подобраны таким образом, чтобы генератор обладал хорошими статистическими свойствами и большой длиной периода. На практике это означает, что последовательность чисел, сгенерированных объектом Random, будет выглядеть случайной, хотя на самом деле она полностью детерминирована и повторяема при одинаковом начальном значении.

Пример создания объекта Random с seed:
import java.util.Random;

public class RandomWithSeedExample {
public static void main(String[] args) {
Random random = new Random(42); // Создаем Random с фиксированным seed
int randomInt1 = random.nextInt();
int randomInt2 = random.nextInt();

System.out.println("Random Integer 1: " + randomInt1);
System.out.println("Random Integer 2: " + randomInt2);
}
}
В этом примере при каждом запуске программы будут генерироваться одни и те же числа, так как seed фиксирован.


#Java #Training #Medium #Random
Особенности класса Random

Детерминированность: Класс Random генерирует последовательности чисел, которые можно воспроизвести, если известно начальное значение (seed). Это полезно, например, для тестирования, когда нужно получать одни и те же результаты на каждом запуске программы.
Многопоточность: Random не является потокобезопасным. При использовании в многопоточной среде возможны конфликты, приводящие к некорректной работе. Для таких случаев лучше использовать ThreadLocalRandom, который оптимизирован для многопоточных приложений.
Период генератора: Период генератора — это максимальная длина последовательности псевдослучайных чисел, прежде чем она начнет повторяться. У Random достаточно большой период, чтобы его было сложно заметить в реальных приложениях, однако, он все равно существует.


Альтернативы: Существует несколько альтернатив классу Random, такие как SecureRandom, который обеспечивает более высокую степень случайности, и SplittableRandom, который эффективнее в многопоточных сценариях.

Влияние начального значения (seed)

Одной из ключевых особенностей работы с Random является возможность задания начального значения (seed). Понимание этой концепции важно для ситуаций, когда требуется воспроизводимость экспериментов или процессов.

Фиксированный seed: Если используется фиксированный seed, последовательность случайных чисел всегда будет одинаковой. Это полезно для тестирования, где результаты должны быть предсказуемыми и сравнимыми.
Случайный seed: Когда seed не задан явно, используется текущее время в миллисекундах. Это обеспечивает разнообразие последовательностей чисел при каждом запуске программы, но делает воспроизводимость невозможной без логирования seed.

import java.util.Random;

public class RandomSeedExample {
public static void main(String[] args) {
Random random1 = new Random(12345);
Random random2 = new Random(12345);

System.out.println("Random 1:");
System.out.println(random1.nextInt());
System.out.println(random1.nextInt());

System.out.println("Random 2:");
System.out.println(random2.nextInt());
System.out.println(random2.nextInt());
}
}
В этом примере оба объекта Random генерируют одинаковые числа, так как они используют один и тот же seed.


Недостатки и ограничения

Несмотря на широкое применение, класс Random имеет некоторые ограничения:
Псевдослучайность: Генератор не создает истинно случайных чисел. Для криптографии и других задач, требующих высокой степени случайности, лучше использовать SecureRandom.
Неоптимальность в многопоточности: Random не оптимизирован для использования в многопоточных приложениях. Если несколько потоков одновременно используют один и тот же объект Random, это может привести к снижению производительности и даже к ошибкам.
Ограниченная функциональность: Random не предоставляет методов для генерации всех возможных распределений случайных чисел (например, нормального распределения). Для этих целей потребуется использовать дополнительные библиотеки или писать собственную реализацию.


#Java #Training #Medium #Random
Что выведет код?

import java.util.Random;

public class Main {
public static void main(String[] args) {
Random random = new Random(12345);
int value1 = random.nextInt(10);
int value2 = random.nextInt(10);
int value3 = random.nextInt(10);
System.out.println(value1 + ", " + value2 + ", " + value3);
}
}


#Tasks
Варианты ответа:
Anonymous Quiz
25%
2, 5, 7
50%
5, 2, 8
13%
1, 0, 1
13%
1, 0, 0
Новый челлендж - посчитайте Вашу энергию за сегодня!!!😉😂🤪

https://t.me/Java_for_beginner_dev

#Mems
Основные методы класса Random, примеры применения

nextInt()
Этот метод генерирует случайное целое число в пределах от Integer.MIN_VALUE до Integer.MAX_VALUE. Он часто используется в различных приложениях, где необходимо случайным образом выбирать индекс массива или генерировать идентификаторы.
import java.util.Random;

public class NextIntExample {
public static void main(String[] args) {
Random random = new Random();
int randomInt = random.nextInt();
System.out.println("Random Integer: " + randomInt);
}
}


nextInt(int bound)
Этот метод возвращает случайное целое число в диапазоне от 0 (включительно) до указанного значения bound (исключительно). Это полезно для выбора случайного элемента из массива или списка фиксированного размера.
import java.util.Random;

public class NextIntBoundExample {
public static void main(String[] args) {
Random random = new Random();
int randomInt = random.nextInt(10); // Генерация числа от 0 до 9
System.out.println("Random Integer (0-9): " + randomInt);
}
}


nextLong()
Метод nextLong() генерирует случайное длинное целое число (тип long) в пределах от Long.MIN_VALUE до Long.MAX_VALUE.
import java.util.Random;

public class NextLongExample {
public static void main(String[] args) {
Random random = new Random();
long randomLong = random.nextLong();
System.out.println("Random Long: " + randomLong);
}
}


nextFloat()
Этот метод возвращает случайное число с плавающей точкой типа float в диапазоне от 0.0 до 1.0 (исключительно). Применяется в ситуациях, когда требуется получить случайное дробное число в пределах фиксированного диапазона.
import java.util.Random;

public class NextFloatExample {
public static void main(String[] args) {
Random random = new Random();
float randomFloat = random.nextFloat();
System.out.println("Random Float: " + randomFloat);
}
}


nextDouble()
Метод nextDouble() аналогичен nextFloat(), но возвращает значение типа double. Он используется для генерации случайных дробных чисел с большей точностью.
import java.util.Random;

public class NextDoubleExample {
public static void main(String[] args) {
Random random = new Random();
double randomDouble = random.nextDouble();
System.out.println("Random Double: " + randomDouble);
}
}


nextBoolean()
Метод nextBoolean() генерирует случайное логическое значение (true или false). Этот метод полезен в ситуациях, когда необходимо принять случайное бинарное решение, например, выбор направления или вариант действия.
import java.util.Random;

public class NextBooleanExample {
public static void main(String[] args) {
Random random = new Random();
boolean randomBoolean = random.nextBoolean();
System.out.println("Random Boolean: " + randomBoolean);
}
}


nextBytes(byte[] bytes)
Метод nextBytes(byte[] bytes) заполняет переданный массив байтов случайными значениями. Это полезно, когда нужно сгенерировать массив случайных байтов, например, для использования в криптографических алгоритмах или для случайной инициализации данных.
import java.util.Random;

public class NextBytesExample {
public static void main(String[] args) {
Random random = new Random();
byte[] randomBytes = new byte[10];
random.nextBytes(randomBytes);

System.out.println("Random Bytes: ");
for (byte b : randomBytes) {
System.out.print(b + " ");
}
}
}


#Java #Training #Medium #Random
Примеры применения методов класса Random

1. Симуляция броска кубика

Представим, что нужно создать программу, которая будет симулировать бросок кубика. Это классическая задача, где необходимо сгенерировать случайное число от 1 до 6.
import java.util.Random;

public class DiceRollSimulation {
public static void main(String[] args) {
Random random = new Random();
int diceRoll = random.nextInt(6) + 1; // Генерация числа от 1 до 6
System.out.println("Dice Roll: " + diceRoll);
}
}
В этом примере мы используем метод nextInt(int bound) с параметром 6, чтобы получить случайное число от 0 до 5, и добавляем 1, чтобы результат был в диапазоне от 1 до 6.


2. Генерация случайного пароля
Допустим, нам нужно сгенерировать случайный пароль длиной 8 символов, состоящий из букв и цифр. Мы можем использовать методы класса Random для этой задачи.
import java.util.Random;

public class PasswordGenerator {
public static void main(String[] args) {
Random random = new Random();
String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
StringBuilder password = new StringBuilder();

for (int i = 0; i < 8; i++) {
int index = random.nextInt(characters.length());
password.append(characters.charAt(index));
}

System.out.println("Generated Password: " + password.toString());
}
}
Этот код создает случайный пароль длиной 8 символов, используя метод nextInt(int bound) для выбора случайного индекса символа из строки, содержащей все возможные символы пароля.


3. Симуляция монетки
Если вам нужно смоделировать подбрасывание монетки (где есть только два возможных исхода — орел или решка), можно использовать метод nextBoolean().
import java.util.Random;

public class CoinFlipSimulation {
public static void main(String[] args) {
Random random = new Random();
boolean coinFlip = random.nextBoolean();
if (coinFlip) {
System.out.println("Heads");
} else {
System.out.println("Tails");
}
}
}
В этом примере случайное значение true означает "Орел", а false — "Решка".


4. Случайный выбор из списка
Представьте, что вам нужно случайным образом выбрать элемент из списка, например, случайное имя из списка имен.
import java.util.Random;
import java.util.List;
import java.util.Arrays;

public class RandomNamePicker {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
Random random = new Random();
String randomName = names.get(random.nextInt(names.size()));

System.out.println("Randomly Picked Name: " + randomName);
}
}
Здесь метод nextInt(int bound) используется для выбора случайного индекса из списка имен.


5. Генерация случайной даты
Если вам нужно сгенерировать случайную дату в определенном диапазоне, можно воспользоваться комбинацией Random и Calendar.
import java.util.Random;
import java.util.Calendar;

public class RandomDateGenerator {
public static void main(String[] args) {
Random random = new Random();
Calendar calendar = Calendar.getInstance();

int year = random.nextInt(2022 - 2000) + 2000; // Год от 2000 до 2021
int dayOfYear = random.nextInt(calendar.getActualMaximum(Calendar.DAY_OF_YEAR)) + 1;

calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.DAY_OF_YEAR, dayOfYear);

System.out.println("Random Date: " + calendar.getTime());
}
}
В этом примере мы генерируем случайную дату между 2000 и 2021 годами, используя случайное число для года и дня года.


#Java #Training #Medium #Random
Рекурсия в Java.

Рекурсия — это важная концепция в программировании, когда метод вызывает сам себя для решения задачи. В Java рекурсия используется для решения проблем, которые могут быть разбиты на более простые подзадачи того же типа.

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

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


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

Пример простой рекурсивной функции — вычисление факториала:

public class FactorialExample {
public static int factorial(int n) {
if (n == 0) {
return 1; // Базовый случай
} else {
return n * factorial(n - 1); // Рекурсивный случай
}
}

public static void main(String[] args) {
int result = factorial(5);
System.out.println("Factorial of 5 is: " + result);
}
}
В этом примере функция factorial вызывает саму себя, пока не достигнет базового случая, когда n равно 0.


Виды рекурсии

Прямая рекурсия (Direct Recursion)

В прямой рекурсии функция напрямую вызывает саму себя. Это самый простой и распространенный вид рекурсии.
public class DirectRecursionExample {
public static void countdown(int n) {
if (n == 0) {
System.out.println("Done!");
} else {
System.out.println(n);
countdown(n - 1); // Прямой рекурсивный вызов
}
}

public static void main(String[] args) {
countdown(5);
}
}


Косвенная рекурсия (Indirect Recursion)

В косвенной рекурсии функция A вызывает функцию B, которая в свою очередь вызывает функцию A. Этот процесс может быть продолжен с участием нескольких функций, которые вызывают друг друга.
public class IndirectRecursionExample {
public static void functionA(int n) {
if (n > 0) {
System.out.println("A: " + n);
functionB(n - 1);
}
}

public static void functionB(int n) {
if (n > 0) {
System.out.println("B: " + n);
functionA(n - 1);
}
}

public static void main(String[] args) {
functionA(5);
}
}


Хвостовая рекурсия (Tail Recursion)

Хвостовая рекурсия — это особый вид рекурсии, когда рекурсивный вызов является последним действием в функции. В хвостовой рекурсии результат рекурсивного вызова сразу возвращается без дополнительных операций. Это позволяет компиляторам оптимизировать работу, избегая создания нового стека вызовов для каждой рекурсии.
public class TailRecursionExample {
public static int tailFactorial(int n, int acc) {
if (n == 0) {
return acc;
} else {
return tailFactorial(n - 1, n * acc); // Хвостовой рекурсивный вызов
}
}

public static void main(String[] args) {
int result = tailFactorial(5, 1);
System.out.println("Factorial of 5 is: " + result);
}
}
В этом примере хвостовая рекурсия позволяет эффективно вычислить факториал без необходимости удерживать промежуточные результаты в стеке вызовов.


#Java #Training #Medium #Recursion
Нерегулярная рекурсия (Non-Regular Recursion)

Нерегулярная рекурсия используется в случаях, когда количество рекурсивных вызовов не фиксировано. Она часто используется для обхода графов, деревьев и других структур данных.
public class TreeNode {
int value;
TreeNode left, right;

TreeNode(int value) {
this.value = value;
left = right = null;
}
}

public class TreeTraversal {
public static void inOrder(TreeNode node) {
if (node != null) {
inOrder(node.left); // Рекурсивный вызов для левого поддерева
System.out.println(node.value);
inOrder(node.right); // Рекурсивный вызов для правого поддерева
}
}

public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);

inOrder(root);
}
}
В этом примере используется нерегулярная рекурсия для обхода бинарного дерева в порядке in-order.


Внутреннее устройство рекурсии


Рекурсия в Java и других языках программирования основывается на стеке вызовов. Когда функция вызывает саму себя, текущее состояние функции (значения переменных, адрес возврата и т.д.) сохраняется в стеке вызовов. После завершения рекурсивного вызова управление возвращается к предыдущему состоянию.

Стек вызовов — это структура данных, работающая по принципу "последним пришел — первым вышел" (LIFO). Каждый рекурсивный вызов добавляет новый фрейм в стек, который содержит информацию о текущем состоянии функции. После завершения рекурсивного вызова этот фрейм удаляется из стека, и программа возвращается к предыдущему вызову.


Пример работы стека вызовов на примере вычисления факториала числа 3.
factorial(3)
-> 3 * factorial(2)
-> 2 * factorial(1)
-> 1 * factorial(0)
-> return 1
-> return 1 * 1 = 1
-> return 2 * 1 = 2
-> return 3 * 2 = 6
На каждом этапе рекурсивного вызова в стек добавляется новый фрейм. Когда функция достигает базового случая (factorial(0)), она начинает возвращаться, удаляя фреймы из стека и комбинируя результаты.


Преимущества и недостатки рекурсии

Преимущества:
Простота и элегантность: Рекурсия позволяет выражать сложные задачи простым и интуитивно понятным способом, особенно для задач, которые имеют естественную рекурсивную структуру.
Минимизация кода: Рекурсивные функции могут быть короче и понятнее, чем их итеративные аналоги.
Легкость реализации: В некоторых задачах (например, обход графа) рекурсия позволяет легко реализовать алгоритм без необходимости явного использования стека или других структур данных.


Недостатки:
Риск переполнения стека: Если глубина рекурсии слишком велика, это может привести к переполнению стека вызовов и завершению программы с ошибкой StackOverflowError.
Затраты на память и производительность: Каждый рекурсивный вызов требует дополнительной памяти для хранения состояния в стеке, что может негативно сказаться на производительности программы.
Сложность отладки: Рекурсивные программы сложнее отлаживать из-за необходимости отслеживания множества уровней вызовов.


#Java #Training #Medium #Recursion