Небольшая проблема с математикой
- Войдите на сайт для отправки комментариев
Чт, 07/03/2019 - 16:17
Коллеги, пользующиеся своим мозгом более, чем я (средний уровень - 3%), помогите понять вычислительную проблему... Отчего нижеприведённый код всегда даёт значение переменной y, делящееся нацело на 7?
Может я лопухнулся где с типами переменных?
void setup() { long h, l, x, y; Serial.begin(115200); x = analogRead(A0); h = x / 127773L; l = x % 127773L; y = 16807L * l - 2836L * h; Serial.print("x: "); Serial.println(x); Serial.print("y: "); Serial.println(y); Serial.print("y % 7L: "); Serial.println(y % 7L); } void loop() {}
Отчего нижеприведённый код всегда даёт значение переменной y, делящееся нацело на 7?
потому что математика
Давать ответ сразу или подождать других?
Потому что H всегда равно 0, L - всегда равно X, а Y всегда равно 16807*X. 16807 делится на 7 (2401), а значит и Y-ку деваться некуда.
Это что за тест такой был?
Давать ответ сразу или подождать других?
Вот, умный человек!
А я, похоже, вляпался. Это ж явно неспроста был вопрос :(
Че пятница уже с утра ? :)
Ну, типа, выходной же завтра.
Господа, не могу пока привести оригинал кода (с мобильника пишу). Но ЕвгенийП прав - вопрос был задан не просто так и проистекает из взаимоотношений честного и нечестного Си.
Ну, я же говорил - подвох како-то. Попал :(
Ну, если честный Си честно детектирует делимость на 7, то, вполне вероятно, от нечестного Си можно ожидать всего, что угодно. Тогда теоретически разница может иметь место.
А я вопрос не понял...
16807 легко делится на 7 и не один раз подряд, ибо это пятая степень семерки, даже если из нее вычесть 0.
h - очевидно равно 0 ибо X никогда не будет больше 1024.
В чем подвох? Получается ровно то что должно получиться.
Ждём пояснений. Для чего-то же он это запостил.
Итак...
Берём нечестный random() из Wiring:
Вместо 300 ставим 7 и получаем каждый запуск первым значением 0 вне зависимости от seed. Задаёмся вопросом - какого лешего....
Лезем в Arduino\hardware\arduino\avr\cores\arduino\WMath.cpp:
Никаких нулей. Продолжаем погружение. avr-lib ->stdlib -> random.c
А дальше вы и сами знаете...
Я не очень понял кто придумал пхать в 32 разрядную переменную, для старта рандома, по сути девятиразрядную. Это не си лживый, это кто то другой ....
Может лучше что то типа (analogRead(0)*1024+analogRead(1))*1024+analogRead(2) ?
Может и лучше, однако https://www.arduino.cc/reference/en/language/functions/random-numbers/random/ / https://www.arduino.cc/reference/en/language/functions/random-numbers/ra... нам показывает именно такой способ посева.
Да. Сидишь вот так и думаешь, что нечестный тебя подставил, а выходит, что вовсе нет.
Я вчера ещё прошёлся немного по мануалам и не нашёл рекомендаций или каких-либо ограничений по выбору seed. Да и само дефолтовое значение какбэ намекает, что однобитовое число тоже подходит для такого.
Нельзя, конечно, сказать, что данный случай - трагедия, однако странные ситуации породить может. Мне вот для отладки надо было получить random(7), наложить на массив структур, в котором дни недели были... И я был очень поражён тем, что каждый раз первым выпадал "понедельник".
... И я был очень поражён тем, что каждый раз первым выпадал "понедельник".
За мутными глазницами HP
ты в непорочности своей как рак-отшельник.
И всё не то и, все вокруг не те и,
каждый день как будто ПОНЕДЕЛЬНИК...???
Спасибо, будем знать. Чтобы по граблям поменьше топтаться.
вообще тема известная. про первое значение, после посева. самый простой лайфхак - брать второе. а ещё круче сделать случайное число шагов... ну, например, пока random(16) < 12.
ведь уже второе, после посева, вполне себе энтропийное...
На ардуине есть millis() и почему не брать случайные значения с него. Но нет ходить по граблями это как по-людски.
Все сомнений, что первооткрывателем я не стал и люди, имеющие практический опыт, давно уже всё это знают. Тем не менее представляется странным, что в пример применения функций random*() суют такой код, который порождает больше вопросов, нежели ответов. Я же не сразу стал постить на форум вопрос, побыстрогуглил на тему данной "проблемы" и ничего мне в глаза не бросилось.
Вчера у меня не было времени размышлять об этом, но с утра я снова посмотрел на формулу и понял, что такой же эффект будет проявляться у random(N), где N - делитель числа 16807.
А вот тут уже проблемы не наблюдается, хотя по исходникам rand() и random() близки.
Смотрим avr-lib -> stdlib -> rand.c
Где объявляется USE_WEAK_SEEDING я не нашёл.
Нагуглил "цитату из интернета" (R):
«Многие генераторы случайных чисел, используемые сегодня, работают не слишком хорошо. Разработчики обычно стараются не вникать, как устроены такие подпрограммы. И часто бывает так, что какой-то старый, неудовлетворительно работающий метод раз за разом слепо перенимается многими программистами, которые зачастую просто не знают о присущих ему недостатках»
Дональд Кнут, «Искусство программирования», том 2.
На ардуине есть millis() и почему не брать случайные значения с него. Но нет ходить по граблями это как по-людски.
С каких пор millis() стал рандомным? Да ещё с его склонностью к пьян... переполнению.
Не миллис случаен, а момент его считывания. Например, время когда вы нажмёте на гашетку. Это ж очевидно.)
Нагуглил "цитату из интернета" (R):
«Многие генераторы случайных чисел, используемые сегодня, работают не слишком хорошо. Разработчики обычно стараются не вникать, как устроены такие подпрограммы. И часто бывает так, что какой-то старый, неудовлетворительно работающий метод раз за разом слепо перенимается многими программистами, которые зачастую просто не знают о присущих ему недостатках»
Дональд Кнут, «Искусство программирования», том 2.
Т.е. это совершенно отдельная и достаточно сложная тема. Вряд ли ее стоит даже начинать обсуждать, не прочитав эти 200 страниц.
А после прочтения, вполне вероятно, что темы для обсуждения уже не обнаружится.
Не хочу говорить грубо, но у Вас проблема не только с рандомом, но и теорией вероятности.
Не миллис случаен, а момент его считывания. Например, время когда вы нажмёте на гашетку. Это ж очевидно.)
Я согласен с тем, что он может выдавать "рандомные" значения, если кто-то внешний жмёт на гашетку. Однако не вижу никакого смысла в том, чтобы опираться на него для получения случайных чисел в системе, которая выполняет свои инструкции с тем же тактом, что и тикает millis().
Надеюсь, что qwone не хочет сказать, что в нижеприведённом коде millis() каждый раз будет выдавать различные значения?
Т.е. это совершенно отдельная и достаточно сложная тема. Вряд ли ее стоит даже начинать обсуждать, не прочитав эти 200 страниц.
А после прочтения, вполне вероятно, что темы для обсуждения уже не обнаружится.
Цитату я привёл только для того, чтобы подвердить её - randomSeed(analogRead(0)); таскается из кода в код...
А насчёт второго... даже и не знаю, что лучше - прочитать и необсуждать или непрочитывать и получить немного общения на форуме хоббистов ))
Надеюсь, что qwone не хочет сказать, что в нижеприведённом коде millis() каждый раз будет выдавать различные значения?
Qwone, я предлагаю вам спорить не со мной, а с мантайнерами Wiring (а то и avr-lib). Спросите у них - на кой там random(), если есть прекрасный millis().
Вот зачем мне спорить с ними. Я не хочу поступать как Архат или подобные им. У меня есть нормальный рецепт и я им пользуюсь. А у других есть их важность своего мнения, вот пусть они и получают последствия важности их мнения, включая и мантайнерами Wiring.
Вот и отлично. Надеюсь, что Вы и со мной не будете спорить насчёт внутреннего устройства служебной функции random() языка C и о необходимости включения библиотеку stdlib функции millis(), которая позволит всем неверующим узреть свет Истины о правильной генерации ряда случайных чисел. А то напридумали тут алгоритмов, математики недобитые. Соловки по ним плачут.
если кто-то делает что-то плохо, значить кому-то это выгодно.
Но это не значить, что лично Вы должны это жрать.
Пух, тебе не приходило в голову, что время от начала программы и до перевого считывания millis() для получения случайного числа жестко детерминировано, при прочих равных условиях? О какой случайности речь?
Т.е. это совершенно отдельная и достаточно сложная тема. Вряд ли ее стоит даже начинать обсуждать, не прочитав эти 200 страниц.
А после прочтения, вполне вероятно, что темы для обсуждения уже не обнаружится.
Цитату я привёл только для того, чтобы подвердить её - randomSeed(analogRead(0)); таскается из кода в код...
А насчёт второго... даже и не знаю, что лучше - прочитать и необсуждать или непрочитывать и получить немного общения на форуме хоббистов ))
- хочется получить информацию,
- хочтся пообщаться.
В первом приближении для первого существуют тематические разделы, для второго - "Отвлеченные темы".
Я ни в коей мере не отрицаю важности общения "хоббистов" между собой, но место размещения темы IMHO вступает в противоречие с ее содержанием.
DetSimen,похоже и вам надо освежить теорию верояности. Проблема не в случайности появления, а равновероятности , которое у всех генераторов псевдослучайных чисел страдает. А о жестком детерменировании получения случайного числа, так не делайте его жестким. Или Вы в программировании новичек.
у всех генераторов псевдослучайных чисел страдает.
А у millis(), надо понимать, не страдает?
Ну-ка ну-ка, вот с этого места
можно поподробнее. Что Вы туда всунете? Уж не любимый ли analogRead()? :)
А вообще, Вы бы это практически сделали и посчитали распределение. Узнали бы, что в действительности, оно всё совсем не так, как на самом деле :)
Можно цифровой автомат всунуть или лямбда-функцию...
Если не учитывать полутонов, то на форум можно писать по одной из двух причин:
- хочется получить информацию,
...
Я ни в коей мере не отрицаю важности общения "хоббистов" между собой, но место размещения темы IMHO вступает в противоречие с ее содержанием.
Тогда это уже не форум, а справочная получится. Пришёл, запрос в окошко сунул, оттудова женщина ответила или справку с печатью дала и всё, уходить извольте.
Начиналось-то всё именно с программной проблемы связанной с внутренней реализацией рандомайза. Это потом уже пошло-поехало...
Можно цифровой автомат всунуть или лямбда-функцию...
Это да. А то вон andriano попробовал без лямда-функции, так чё попало получилось :(
Начиналось-то всё именно с программной проблемы связанной с внутренней реализацией рандомайза. Это потом уже пошло-поехало...
Нет, упоминание о random появились значительно позже.
И как только оно появилось, тема перестала быть специфичной для раздела "Программирование".
И ваши волосы будут шелковисты каждый день. А при каждом новом включении платы всегда будут по настоящему случайные числа.
Было б - включил бы, а то нечего включать :(
Как раз из-за отсутствия сообразительности, предпочитаю скучные и нудные формулы.
Мне больше "нано" нравится. За него по нынешним временам платють больше.
И ваши волосы будут шелковисты каждый день.
И как у Вас это получается? Я аж завидую! У меня такая хрень вообще не компилируется - аж лысина вспотела. А у Вас как-то ещё и волосы шелковятся :)
Настоящих случайных чисел в таких алгоритмах не бывает в принципе (это по формулам - по сообразительности не знаю, Вам виднее). А распределение Вы всё ж посчитайте. Много интересного узнаете :)))
Или Вы в программировании новичек.
В таком - да.
Ой, да тут пятница!
Что же делать системам без EEPROM - просить пользователя на бумажке записывать последнее число, а потом вводить его при необходимости? Прямо какой-то сериал Lost.
просить пользователя на бумажке записывать
Так и это и есть "обычная процедура в таких случаях" :)
Что же делать системам без EEPROM - просить пользователя на бумажке записывать последнее число, а потом вводить его при необходимости? Прямо какой-то сериал Lost.
У Интела я как-то пытался использовать младшый байт RDTSC для "псевдослучайности" да быстро бросил, хоть он и был примерно на 3 порядка "случайнее" millis().