Java напишите программу которая генерирует и отображает случайную строчную латинскую букву
Практические задачи по Java — для курсов и прочих занятий
Несколько вводных слов
Последние несколько лет я читаю курс по программированию на Java. Со временем он менялся — то добавлялись, то выкидывались разные части, менялась последовательность тем, менялся подход к построению плана самих занятий, и так далее. То есть, курс совершенствовался. Одной из основных проблем, возникших при подготовке курса — это задачи. О них и пойдёт речь.
Дело в том, что каждое моё занятие состоит из двух частей. На первой я выступаю в роли лектора — рассказываю с примерами кода о какой-то новой теме (классы, наследование, дженерики и так далее). Вторая часть — практическая. Очевидно, что нет смысла просто рассуждать о программировании, надо программировать. Приоритет на занятиях — решение задач, то есть программирование чего-то как-то. Программирование на занятиях отличается от программирования дома, так как на занятиях можно задать вопрос, показать код, получить быструю оценку кода, комментарии по улучшению, исправлению написанного. Очень легко было найти задачи для самых первых занятий. Задачи на циклы, условные операторы, и ООП (к примеру, написать класс «Собака» или класс «Вектор»). Сервисы вроде leetcode позволяют даже проверить правильность решения таких задач сразу, онлайн. Но какие задачи дать студентам на занятии, которое было посвящено коллекциям? Потокам? А аннотациям? За несколько лет я придумал, или переработал несколько таких задач, и эта статья, по сути, является сборником этих задач (к некоторым задачам прилагается решение).
Конечно, все задачи уже где-то появлялись. Однако, эта статья ориентирована на преподавателей курсов по программированию (для языков, похожих на Java, большинство задач подойдёт), или тех, кто преподаёт программирование частным образом. Эти задачи можно использовать «из коробки» на своих занятиях. Изучающие Java тоже могут попробовать решать их. Но такие решения требуют сторонней проверки и оценки.
Некоторые самые простые задачи, которые уже десятилетия все используют, я тоже включил в эту статью. Пожалуй, для того, чтобы не начинать сразу с абстрактных классов.
Это вопрос о производительности. Я могу конвертировать из прописных в строчные и наоборот, используя этот код:
От строчной к прописной:
Из прописных в строчные:
Я знаю, что Java предоставляет следующие методы: .toUpperCase( ) и .toLowerCase( ) . Думая о производительности, какой самый быстрый способ сделать это преобразование, используя побитовые операции, как я показал это в коде выше, или используя методы .toUpperCase( ) и .toLowerCase( ) ? Спасибо.
Изменить 1: обратите внимание, как я использую десятичное 65503, который является двоичным 1111111111011111. Я использую 16 бит, а не 8. Согласно ответу, в настоящее время с большим количеством голосов на Сколько биты или байты есть в символе?:
Символ Unicode в кодировке UTF-16 составляет от 16 (2 байта) до 32 бит (4 байта), хотя большинство обычных символов занимают 16 бит. Это кодировка, используемая Windows для внутренних целей.
Код в моем вопросе предполагает UTF-16.
4 ответа
Первый тест (в верхний регистр):
Второй контрольный показатель (в нижнем регистре):
Я включил только несколько разных букв (хотя все они были проверены), так как все они имеют схожие результаты.
Да, метод, написанный вами, будет немного быстрее, если вы решите выполнить преобразование с помощью простой побитовой операции, тогда как методы Java имеют более сложную логику для поддержки символов Юникода, а не только кодировку ASCII.
Если вы посмотрите на String.toLowerCase () вы заметите, что там много логики, так что если вы работали с программным обеспечением, которое должно обрабатывать огромные количества ASCII только, и ничего больше, вы можете увидеть некоторую выгоду от использования более прямого подхода.
Но , если вы не пишете программу, которая тратит большую часть своего времени на преобразование ASCII, вы не сможете заметить никакой разницы даже с помощью профилировщика (и если вы пишете такую программу). .вы должны искать другую работу).
Ваш код работает только для символов ANSII. Что касается языков, в которых не существует четкого преобразования между строчными и прописными буквами, например Немецкий ß (пожалуйста, исправьте меня, если я ошибаюсь, мой немецкий ужасен) или когда буква / символ написаны с использованием многобайтовой кодовой точки UTF-8. Корректность важнее, чем производительность, и проблема не так проста, если вам приходится работать с UTF-8, как это видно из метода String.toLowerCase(Locale) .
Просто придерживайтесь предоставленных методов .toLowerCase() и .toUpperCase() . Добавление двух отдельных классов для выполнения двух методов, которые уже были предоставлены java.lang , является излишним и замедлит вашу программу (с небольшим запасом).
Есть ли в Java какие-либо функции для генерации случайных символов или строк? Или нужно просто выбрать случайное целое число и преобразовать его код ascii в символ?
Чтобы сгенерировать случайный символ в az:
Вы даже можете пойти дальше, (char)(r.nextInt('z' - 'a') + 'a') если не знаете, сколько букв в алфавите.
Есть много способов сделать это, но да, это включает в себя создание случайного числа int (например, с использованием java.util.Random.nextInt ), а затем его использование для сопоставления с файлом char . Если у вас есть определенный алфавит, то что-то вроде этого будет отличным вариантом:
Обратите внимание, что java.util.Random на самом деле это генератор псевдослучайных чисел, основанный на довольно слабой формуле линейного сравнения . Вы упомянули о необходимости криптографии; вы можете захотеть исследовать использование гораздо более сильного криптографически безопасного генератора псевдослучайных чисел в этом случае (например java.security.SecureRandom ).
Спасибо, это действительно краткое решение, если вы хотите сгенерировать определенный набор символов. Ответ на весь алфавит короче. Вы можете немного повысить эффективность и читаемость, используя массивы. См. Мой шестнадцатеричный пример .
Вы также можете использовать RandomStringUtils из проекта Apache Commons:
Создает значения в диапазонах az, AZ.
Как именно (char) (base + rnd % 26) работает? Это потому, что добавление char к int преобразует char в его номер ASCII?
@ Lèsemajesté - Ага, вот почему. Каждый char также неявно конвертируется в int, поэтому 'a' можно рассматривать как целочисленное значение (97), а затем смещение, скажем, на 3 даст 100, которое при обратном преобразовании в a char будет 'd'.
Этот тоже работает.
В следующем 97 ascii значение маленькой буквы "a".
в приведенных выше 3 числах для a, b, c или d, и если вы хотите, чтобы все символы были такими, как от a до z, вы замените 3 числа на 25.
Чтобы создать случайную строку, используйте метод anyString .
Вы можете создавать строки из более ограниченного набора символов или с ограничениями минимального / максимального размера.
Обычно вы запускаете тесты с несколькими значениями:
учитывая, что chars вы можете создать "перемешанный" диапазон символов:
затем взяв первые n символы, вы получите случайную строку длины n . Окончательный код прост:
NB : состояние n > 0 проверяется slice
РЕДАКТИРОВАТЬ
как правильно заметил Стив, randomString каждую букву использует не более одного раза. В качестве обходного пути вы можете повторить алфавитный код несколько m раз перед вызовом shuffle :
или просто укажите свой алфавит как String :
Это будет использовать каждый символ не более одного раза в случайной строке. Это может быть не то, что нужно OP.
Это простое, но полезное открытие. Он определяет класс с именем RandomCharacter с 5 перегруженными методами для случайного получения определенного типа символа. Вы можете использовать эти методы в своих будущих проектах.
Чтобы продемонстрировать, как это работает, давайте взглянем на следующую тестовую программу, отображающую 175 случайных строчных букв.
если вы снова запустите еще раз:
Я отдаю должное Я. Даниэлю Ляну за его книгу « Введение в программирование на Java, всеобъемлющая версия, 10-е издание», в которой я цитировал эти знания и использую их в своих проектах.
Примечание . Если вы не знакомы с перегруженными методами, вкратце, перегрузка метода - это функция, которая позволяет классу иметь более одного метода с одинаковым именем, если их списки аргументов различаются.
Есть ли у Java какие-либо функции для генерации случайных символов или строк? Или нужно просто выбрать случайное целое число и преобразовать этот целочисленный код ascii в символ?
ОТВЕТЫ
Ответ 1
Существует много способов сделать это, но да, это включает в себя создание случайного int (используя, например, java.util.Random.nextInt ), а затем используя это для сопоставления с char . Если у вас есть определенный алфавит, то что-то вроде этого отличное:
Отметим, что java.util.Random на самом деле является генератором псевдослучайных чисел на основе довольно слабой линейная конгруэнтная формула. Вы упомянули о необходимости криптографии; вы можете изучить использование более сильного криптографически защищенного генератора псевдослучайных чисел в этом случае (например, java.security.SecureRandom ).
Ответ 2
Чтобы создать случайный char в a-z:
Ответ 3
Вы также можете использовать RandomStringUtils из проекта Apache Commons:
Ответ 4
Генерирует значения в диапазонах a-z, A-Z.
Ответ 5
В соответствии с 97 значением ascii малого "a".
в выше 3 числа для a, b, c или d, и если u хочет, чтобы все символы, такие как a-z, вы заменяли 3 числа на 25.
Ответ 6
Это тоже работает.
Ответ 7
Чтобы создать случайную строку, используйте метод anyString.
Вы можете создавать строки из более ограниченного набора символов или с ограничениями минимального/максимального размера.
Обычно вы запускаете тесты с несколькими значениями:
Ответ 8
данный chars вы можете создать "перетасованный" диапазон символов:
затем беря первые символы n , вы получите случайную строку длины n . Окончательный код просто:
NB: условие n > 0 перечеркнуто slice
EDIT
как правильно указал Стив, randomString использует не более одного раза каждую букву. Как обходной путь вы можете повторить алфавит m раз перед вызовом shuffle :
или просто укажите свой алфавит как String :
Ответ 9
Ответ 10
У тебя это есть. Просто создайте случайные коды ASCII самостоятельно. Для чего вам это нужно?
Ответ 11
Взгляните на класс Java Randomizer. Я думаю, вы можете рандомизировать символ, используя метод randomize (char []).
Ответ 12
Ответ Polygenelubricants также является хорошим решением, если вы хотите генерировать только шестнадцатеричные значения:
Ответ 13
Мое предложение для генерации случайной строки со смешанным случаем типа: "DthJwMvsTyu".
Этот алгоритм основан на кодах ASCII букв, когда его коды az (от 97 до 122) и AZ (от 65 до 90) отличаются в 5-м бите (2 ^ 5 или 1
random.nextInt(2) : результат равен 0 или 1.
Верхний A равен 65, а нижний a равен 97. Разница только на 5-м бите (32), поэтому для генерации случайного символа мы делаем двоичный код OR '|' случайный charCaseBit (0 или 32) и случайный код от A до Z (от 65 до 90).
Ответ 14
Вот код для генерации случайного буквенно-цифрового кода. Сначала вы должны объявить строку разрешенных символов, которую вы хотите включить в случайное число, а также определить максимальную длину строки.
Используйте метод toString(), чтобы получить String из StringBuilder
Ответ 15
Это простое, но полезное открытие. Он определяет класс с именем RandomCharacter с 5 перегруженными методами для случайного получения определенного типа символов. Вы можете использовать эти методы в ваших будущих проектах.
Чтобы продемонстрировать, как это работает, давайте взглянем на следующую тестовую программу, отображающую 175 случайных строчных букв.
если вы запустите еще раз:
Я отдаю должное Ю.Даниэлю Лянгу за его книгу " Введение в программирование на Java, полная версия", 10-е издание, где я привел эти знания.
Ответ 16
Если вы не возражаете добавить новую библиотеку в свой код, вы можете генерировать символы с помощью MockNeat (отказ от ответственности: я один авторов).
Ответ 17
Позаботьтесь о том, что вы хотите, считая, что вы считаете "0", "1", "2".. как символы.
Обратиться к теме написания случайных генераторов букв навела мысль о том, что в JS существует нетипичная нативная функция преобразования строки в n-ичное число, где n = 2..36. 36 в стандарте языка придумано не случайно — это сумма количества цифр и малых английских букв, из которых предлагается писать такие числа. Это значит, что парой нативных функций уже можно построить полезный генератор небольших строк из буквоцифр.
Это значит, что для некоторых задач можно не писать относительно честные генераторы на основе унылых строк вида «abcdefghijklmno. ».
Результаты можно использовать в любом языке программирования, в котором получим любым путём используемые функции, но будем говорить о джаваскрипте, поскольку длина и простота функций в виде текста в браузере имеет значение в различных практических задачах.
Узнав о ключевых словах, легко нагуглить достижения в строительстве случайных генераторов буквоцифр, которые предлагаются, в частности, на StackOverflow. Можно видеть забавную картину, что сначала пишут добросовестный многословный алгоритм с выбором символов из строки, его бешено плюсуют, как здесь, потом кто-то вспоминает, что есть такой очень краткий способ, который иногда очень хорошо ложится в решение задачи. Правда, не всегда доводят дело до конца, и решение нужно допиливать, самостоятельно оценивать скорость и наглядность решения.
Рассмотрим варианты решений под разные типичные задачи. Попробуем оценить быстродействие некоторых решений — иногда нужно генерировать случайные строки в больших объёмах. В результате получится рассказ о методах и их сравнение. Посмотрим, о чём обычно спрашивают в интернете, когда речь касается генерации случайных символов.
Не будем касаться вопросов качества случайных последовательностей — возьмём за основу генератор Math.random(). Его улучшения сильно зависят от потребностей и есть тема отдельного исследования. Не будем забывать о балансе вероятностей появления различных символов, чтобы наш алгоритм не ухудшал вероятности распределения чисел или символов. Рассмотрим и классические алгоритмы с последовательностью символов в качестве образца для генератора.
Задача в общем виде ставится так:
"Нужно получить последовательность случайных символов (цифр, букв, больших и малых букв, всего вместе, букв национального алфавита) так, чтобы вероятность появления любого символа была равной для всех символов."
Если вопрос касается небольшого количества цифр, то решение уже есть, надо лишь отрезать нужный их кусок от результата функции Math.random().
Посмотрим сначала, с чем придётся иметь дело: сколько цифр после точки даёт функция Math.random().
В двоичном представлении (javascript: Math.random().toString(2)):
В 36-ричном (javascript: Math.random().toString(36)):
Как видим, надо быть осторожными и в ожидании количества цифр, и в точности, зависящей от системы счисления. За минимальный предел надо брать минимальное из показанного количества цифр и полностью не верить никому. Среди десятичных цифр показан минимум — 16, поэтому максимальным числом взяли 15, что и без того чересчур оптимистично, но и ценность нашей первой функции небольшая.
Результаты профилирования функции randN() без проверки пределов (300 тыс. циклов по 15 символов в каждом, значения в миллисекундах, усреднение по 20 измерениям — всего вычислено 300000*15*20 = 90 млн. символов):
То же, но с проверкой пределов аргумента:
Первый поучительный вывод: хотя проверки кажутся быстрыми операциями, не везде они сделаны по-настоящему быстрыми.
Уже интересно? А ведь это была всего лишь подготовка инструментов для настоящего исследования. Они будут работать для всех остальных измерений, отличаться будет основной цикл и общее заданное их число. Полученная функция [ 1 ] randN() даёт нам очень немного: всего лишь строчку до 15 случайных цифр. А впереди — буквы, цифры с буквами, русские и другие национальные. Самое интересное будет — сравнить, насколько отличается по скорости применение нативных функций вида .toString(36) по сравнению с более традиционным кросс-языковым алгоритмом.
Латинские буквоцифры
В этом есть небольшой национализм, поэтому в заголовок закралась политкорректная формулировка. Уже французы и испанцы останутся недовольны тем, что не все их буквы алфавита входят в предлагаемый набор. Но она будет полезна для генерации случайных имён, пусть лишь с одними маленькими латинскими буквами.
Предположим для начала, что нам не нужно будет очень много буквоцифр — максимум 10 вполне хватит, которые сможет выдавить из себя Firefox.
Как изменились скорости (с проверкой пределов: мы же — серьёзные люди)?
Кое-кто прошёл тест в 1.5 раза дольше, Хром вообще показал чудеса, выполнив преобразование в 36-ричное быстрее, чем в десятичное (а двоичное он просчитал за 350 мс), IE — немного быстрее себя, за пределами погрешностей.
Основной итог такой: ничего не потеряв, мы вычислили буквоцифры примерно так же быстро, как и случайные числа.
Сравним теперь скорости с унылым классическим алгоритмом, где нужно руками набирать весь алфавит и цифры впридачу. Не хотелось его писать, но наука требует жертв.
Буквоцифры из классики
Он вычислил 15 символов в тесте вместо 10 в прежнем алгоритме, показал в среднем даже хорошие результаты, и у него нет проблем с масштабированием. Из минусов — надо много буковок писать. Но ведь и первый алгоритм не сказал своего последнего слова. Большой плюс алгоритма со строкой — возможность собрать строку из любых символов и даже предусмотреть в ней частотность символов, правда, кратную относительно минимальной частоты встречаемости единственного упомянутого символа.
Значит, в этом алгоритме решаются вообще все поставленные задачи и сверх того, вопросы частотности. Может ли нативная toString(36) противопоставить что-то и занять хотя бы часть ниши? Ведь у неё — малые латинские буквоцифры на выдаче или только цифры, а на stackoverflow хотят всё, что только можно себе представить: малые с большими, буквы без цифр, не за горами — национальные наборы.
Реванш toString(36)
Нужно решить 2 проблемы с этим подходом: обеспечить масштабируемость и хотя бы научиться выдавать только цифры. Тогда, если алгоритм будет быстр, он займёт свою нишу. Уже хотя бы потому, что он краток.
Воспользуемся тем, что сможем набирать строку из нескольких символов за 1 раз.
Вычислялось 15 символов (фактически, не менее 20), функция масштабируема, может брать любой положительный аргумент. В целом сравнение выглядит довольно провально: алгоритм с toString(36) играет роль догоняющего, сравниваясь по скорости в районах 10, 20, 30, символов и проигрывая в промежутках. При этом поддерживает только 2 (пока что) набора символов: цифры и буквоцифры. Можно ли его распространить на буквы, не теряя в краткости? Да.
Что тут сказать? Тестировали на получение 15 символов, фактически 20. 90-140 миллионов символов за весь тест. Получили чисто буквенные строки. В регекспах теперь можно писать и другие условия выкусывания ненужных элементов. Например, выключить часть букв. Регекспы проседают у всех. Ведут себя супер-провально в IE8 — получили не 90 миллионов случайных символов в секунду, как в Хроме, а каких-то 16, что тоже много, смотря с чем сравнивать. В сравнении с наборами символов проигрывают по всем статьям.
Выводы. Использование экзотической нативной функции .toString(36) — штука красивая. Её выгодно использовать не в составе функции, а для непосредственного взятия случайной строки до 10 символов в коде один раз. Тогда она выглядит коротко, хотя и не всем понятно. В остальном — по скорости и гибкости проигрывают наборам символов [ 3 ].
Случайная кириллица на регекспах
В варианте с кириллицей уже некуда применить toString(36). Пойдём другим путём. Получим любой символ из некоторого диапазона кодов и удалим ненужные. Что-то наподобие решета Эратосфена. Понятно, что каждое удаление вычисленного случайного символа записывается в пассив, поэтому, чем больший процент удалений, тем медленнее алгоритм и тем сильнее он будет проигрывать классике [ 3 ]. Оптимизировать — легко, если набор символов занимает ограниченный диапазон кодов юникодов. Но в коде тогда появляются проверки и магические числа.
Вот, для начала, пример такой сборки из латинницы. Получаем добавление больших и малых букв. Из-за «лени», оттого что выбрали диапазон 0. 127, имеется много лишних вычислений случайных величин. Алгоритму это не мешает — он строку наберёт, если есть хотя бы 1 символ в диапазоне, но время страдает.
(Если хотим увидеть, сколько пропадает попыток, просто пишем ".length" в конце длинной строчки. Нули в ответной строке — пропавшие попытки, 1 — использованные. Примерно такая плотность: «0101000010000000010100010100101100». Это к тому, чтобы не удивляться, насколько медленнее будет алгоритм.)
Легко проверить, что тормозит здесь короткий, но сложно выполняемый регексп (несколько проверок на буквы и цифры). Самый простой способ оптимизации — исключить диапазон 0. 47, написав внутри скобок Math.random() *80 +48.
Аналогично поступаем и для кириллицы. Берём диапазон где вся она есть, и удаляем не-кириллицу. Надо помнить, что такие циклы записываются красиво, но выполняются крайне медленно, в 1106/66 раз медленнее, чем могли бы быть при эффективном выборе диапазона, поэтому для вычисления нескольких тысяч символов использование их нормально, но для миллионов — не пойдёт. В то же время, алгоритм с набором символов в строке годится и для миллионов.
Для тестов здесь было взято в 100 раз меньше проходов с набором 15 символов, но результаты увеличены в 100 раз для наглядного сравнения с другими.
Время можно сократить в 16 раз, исходя из того, что код 1105 — максимальный юникод кириллицы, а 1040 — минимальный, записав Math.random() *(1106-1040) * 1040 в нужном месте. Но всё равно, секунды на 100 миллионов символов — это много для массовых вычислений по сравнению с функцией [ 3 ].
Читайте также: